Explaining Python's '__enter__' and '__exit__'
Using these magic methods (__enter__
, __exit__
) allows you to implement objects which can be used easily with the with
statement.
The idea is that it makes it easy to build code which needs some 'cleandown' code executed (think of it as a try-finally
block). Some more explanation here.
A useful example could be a database connection object (which then automagically closes the connection once the corresponding 'with'-statement goes out of scope):
class DatabaseConnection(object):
def __enter__(self):
# make a database connection and return it
...
return self.dbconn
def __exit__(self, exc_type, exc_val, exc_tb):
# make sure the dbconnection gets closed
self.dbconn.close()
...
As explained above, use this object with the with
statement (you may need to do from __future__ import with_statement
at the top of the file if you're on Python 2.5).
with DatabaseConnection() as mydbconn:
# do stuff
PEP343 -- The 'with' statement' has a nice writeup as well.
How do __enter__ and __exit__ work in Python decorator classes?
the __exit__()
method should accept information about exceptions that come up in the with:
block. See here.
The following modification of your code works:
def __exit__(self, exc_type, exc_value, tb):
if exc_type is not None:
traceback.print_exception(exc_type, exc_value, tb)
# return False # uncomment to pass exception through
return True
Then you can try raising an exception in one of your with:
blocks and it'll be caught in __exit__()
.
__enter__() takes exactly 3 arguments (1 given)
The __enter__
method is never given any arguments, so beyond self
your signature should not have any other.
You should move those arguments to the __init__
method instead:
class FooBar(object):
def __init__(self, param1, param2):
# do something here ...
def __enter__(self):
# something else, perhaps return self
Creating an instance of FooBar()
is a separate step. with
calls __enter__
on the result of your mymod.FooBar("hello", 123)
expression, the expression itself is not translated to an __enter__
call.
If it was, you couldn't also use it like this, but you can:
cm = mymod.FooBar("hello", 123)
with cm as x:
# do something here with x, which is the return value of cm.__enter__()
Note that x
is assigned whatever cm.__enter__()
returned; you can return self
from __enter__
or you can return something entirely different.
The expected methods __enter__
and __exit__
are documented in the With Statement Context Managers section of the Python Data model documentation:
object.__enter__(self)
Enter the runtime context related to this object. The
with
statement will bind this method’s return value to the target(s) specified in theas
clause of the statement, if any.
as well as in the Content Manager Types section of the Built-In Types documentation:
contextmanager.__enter__()
Enter the runtime context and return either this object or another object related to the runtime context. The value returned by this method is bound to the identifier in the
as
clause ofwith
statements using this context manager.An example of a context manager that returns itself is a file object. File objects return themselves from
__enter__()
to allowopen()
to be used as the context expression in awith
statement.
If you are interested in the exact interactions, see the original proposal: PEP 343 -- The "with" Statement; from the specification section you can see what with EXPR as VAR: BLOCK
statement does under the hood:
mgr = (EXPR)
exit = type(mgr).__exit__ # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
try:
VAR = value # Only if "as VAR" is present
BLOCK
except:
# The exceptional case is handled here
exc = False
if not exit(mgr, *sys.exc_info()):
raise
# The exception is swallowed if exit() returns true
finally:
# The normal and non-local-goto cases are handled here
if exc:
exit(mgr, None, None, None)
Note the mgr = (EXPR)
part; in your case, mymod.FooBar("hello", 123)
is that part. Also note that (EXPR)
, __enter__
and __exit__
are not 'protected' by the try..except
here, exceptions raised in the expression or when entering or exiting are not handled by the context manager!
Receiving AttributeError: __exit__ even when WITH object has EXIT defined
The value of expression after with
keyword must be a valid context manager. That means the value of the expresion must have attributes __enter__
and __exit__
and these must accept parameters described in docs With Statement Context Managers. You can easily verify that the part aa.feed
would be acceptable, but the value of whole expression is None
and it has no necessary attribute. A difference between Python 3.5 and 3.6 is that one fails on missing __exit__
and the latter on missing __enter__
. Nothing unexpected.
You also forgot self
in the line def __call__(self, *args, **kargs):
that would be a problem if you will use args and it is a pythonic pattern also with unused args.
__enter__ and __exit__ on class level in python 3
__enter__
and __exit__
are special methods, and as such only work correctly when defined on a object's type, not in it's instance dictionary.
Now Spam
is a instance of type
, and type(Spam).__enter__
and type(Spam).__exit__
do not exist. Therefore you get an attribute error.
To make this work, the methods would need to be declared on the metaclass of the class you want to use. Example:
class Spam(type):
def __enter__(cls):
print('enter')
return cls
def __exit__(cls, typ, value, tb):
print('exit')
class Eggs(metaclass=Spam):
pass
with Eggs:
pass
Now Eggs
is an instance of Spam
(type(Eggs)
== Spam
, and therefore type(Eggs).__enter__
and type(Eggs).__exit__
do exist).
However defining a metaclass just to use an instance of it as a context manager seems a little over the top. The more straight forward solution starting from your example would be to just use
with Spam():
pass
Or if you want to reuse the same instance later:
spam = Spam()
with spam:
pass
with conn: AttributeError: __exit__ when using socket to receive message
In Python 2.7 the socket
object does not support the context manager interface. You therefore cannot use the with
statement with the socket
object and have to explicitly call its close
method after you're done using it.
Change:
conn, addr = s.accept()
with conn:
print('Connected by', addr)
data = conn.recv(1024)
...
to:
conn, addr = s.accept()
print('Connected by', addr)
data = conn.recv(1024)
...
conn.close()
Python missing __exit__ method
The error means that BacktestingDatabaseHelper
is not designed to be used in a with
statement. Sounds like the classes testingResource
and BacktestingDatabaseHelper
are not compatible with each other (perhaps your version of common.databaseHelper
is out of date).
Conditional with statement in Python
If you want to avoid duplicating code and are using a version of Python prior to 3.7 (when contextlib.nullcontext
was introduced) or even 3.3 (when contextlib.ExitStack
was introduced), you could do something like:
class dummy_context_mgr():
def __enter__(self):
return None
def __exit__(self, exc_type, exc_value, traceback):
return False
or:
import contextlib
@contextlib.contextmanager
def dummy_context_mgr():
yield None
and then use it as:
with get_stuff() if needs_with() else dummy_context_mgr() as gs:
# do stuff involving gs or not
You alternatively could make get_stuff()
return different things based on needs_with()
.
(See Mike's answer or Daniel's answer for what you can do in later versions.)
python swallow exception in context manager and go on
There's no way to continue running the body of the with
statement after an exception makes it back to the context manager. The context manager can stop the exception from bubbling up further, but it can't do more than that.
What you might want is to use your context manager in several separate with
statements:
suppress = swallow_exceptions([ZeroDivisionError])
with suppress:
1 / 0 # this exception is suppressed
with suppress:
float("String") # this one is not
Related Topics
Database Does Not Update Automatically with MySQL and Python
How to Set the Current Working Directory
Using Beautiful Soup to Convert CSS Attributes to Individual HTML Attributes
Comparison of R, Statmodels, Sklearn for a Classification Task with Logistic Regression
Does Ruby Support Conditional Regular Expressions
Dead Simple Example of Using Multiprocessing Queue, Pool and Locking
How to Get Reproducible Results in Keras
Dll Load Failed Error When Importing Cv2
How to Delete the Contents of a Folder
Python Pandas: Convert Rows as Column Headers
Pandas Extract Number from String
How to Convert SQL Query Result to Pandas Data Structure
How to Select and Extract Texts Between Two Elements
How to Get Access of Individual Trees of a Xgboost Model in Python /R
How to Import a JSON from a File on Cloud Storage to Bigquery
Log All Requests from the Python-Requests Module