Reputation: 48986
I have the following Python script:
from contextlib import contextmanager
@contextmanager
def my_content_manager():
self.entrance = True
try:
yield
except Exception as ex:
self.entrance = False
with my_content_manager() as cm:
print (cm.entrance)
print (cm.entrance)
When I tried to run the script, I got the following:
Traceback (most recent call last):
File "test.py", line 12, in <module>
with my_content_manager() as cm:
File "C:\Users\abc\AppData\Local\Programs\Python\Python36\lib\contextlib.py", line 82, in __enter__
return next(self.gen)
File "test.py", line 5, in my_content_manager
self.entrance = True
NameError: name 'self' is not defined
Why is that? How can I solve this error?
Thanks.
Upvotes: 2
Views: 6775
Reputation: 4465
In general, using a class is probably a better way to go (as the other answer suggests). However, I remembered that function objects can have attributes. I came across this related question, which lead to the following trick (which does seem like an abuse of the feature):
from contextlib import contextmanager
@contextmanager
def my_content_manager():
my_content_manager.entrance = True
try:
yield my_content_manager.entrance
finally:
my_content_manager.entrance = False
my_content_manager.entrance = False
with my_content_manager() as cm:
print(my_content_manager.entrance)
# Or, print(cm)
print(my_content_manager.entrance)
There are probably pitfalls to this technique. I'm simply posting it as a curiosity.
Upvotes: 3
Reputation: 36063
The error NameError: name 'self' is not defined
is pretty self-explanatory, no pun intended. self
is just a name used by convention in methods of classes. You haven't defined self
anywhere so Python doesn't know what to do. Moreover you've defined a function, not a method in a class, so this isn't close to working. Use a class to define your context manager like so:
class MyContextManager(object):
def __init__(self):
self.entrance = True
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_val:
self.entrance = False
# signal that the exception was handled and the program should continue
return True
with MyContextManager() as cm:
print (cm.entrance)
raise Exception()
print (cm.entrance)
EDIT: if, as requested in a comment, you only want to print a value and not store it somewhere:
@contextmanager
def my_content_manager():
try:
yield
except Exception as ex:
print(False)
else:
print(True)
Upvotes: 2