Context Managers¶
Indices and tables¶
Introduction¶
Context managers are used to create the environment (context) required by a section of code, and restore the environment to its original state after the section of code terminates.
There are two methods defined by a context manager:
__enter__()
- Executed prior to a section of code executing. Returns an object, related to the context that was created.__exit__()
- Executed after the section of code is done executing. Guaranteed to run regardless of why the code finished executing (ie. normally or due to an exception), therefore, it can sometimes replace thefinally
block in atry
statement.
Python supplies some built-in context managers:
- The
open()
function returns a context manager, which ensures any file opened is always closed. - SQL Lite connection objects, which automatically commit or roll-back a transaction if an exception occurs.
- The Lock class of the
multiprocessing
package, which ensures the lock is released if an exception occurs.
Context manages can be used on their own by invoking their methods. However, it is more common to use the with
statement to do this.
With Statement¶
The with
statement interfaces with the context manager(s) to automatically:
- Setup the context required prior to the
with_suite
executing- Teardown the context created after
with_suite
finishes executing (either naturally or due to an exception).
The with
statement can be used as follows:
with `expression0` [as `target0`] (, `expressionN` [as `targetN`])* :
`with_suite`
The
expression
is evaluated and expected to return a context manager object.The
__enter__()
method of the context manager object is executed and returns a value related to the context.If
target
is present, the value returned by__enter__()
is assigned to it.The
with_suite
is executed.The
__exit__()
method of the context manager object is executed.- If an exception occurs in the
with_suite
, the__exit__()
function is supplied with it. The__exit__()
method can decide to handle the exception, ignore it, or let it through un-handled.
- If an exception occurs in the
You can nest context managers in a
with
statement with additionalexpression
andtarget
sections.
Let’s use a code example to demonstrate the operation.
We define a custom context manager that will be vocal about what is executing:
1 2 3 4 5 6 7 8 9 10 11 | class MyContextMgr():
def __enter__(self):
print("Entering context manager")
return "Hello World"
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is not None:
print("Found an exception. Ignoring")
print("Exiting context manager")
return True
|
Now, the code that uses the context manager in a with
statement:
1 2 3 4 | with MyContextMgr() as val:
print("Executing with_suite")
print("Context manager val: {}".format(val))
print("Done with_suite")
|
Running the code produces the following output:
Entering context manager
Executing with_suite
Context manager val: Hello World
Done with_suite
Exiting context manager
If an exception was thrown in the with_suite
, as in:
1 2 3 4 5 | with MyContextMgr() as val:
print("Executing with_suite")
raise Exception
print("Context manager val: {}".format(val))
print("Done with_suite")
|
The __exit__()
method is still invoked even if though the with_suite
was terminated early:
Entering context manager
Executing with_suite
Found an exception. Ignoring
Exiting context manager
Going back to the idea of context managers created by open()
, they work like the following:
with open("foo.txt") as fh:
for line in fh:
print(line)
In this example, the foo.txt
file is opened and the file handle that points to it is passed into the with_suite
as fh
. Regardless of how the with_suite
terminates, the file will be closed by the context manager.
contextlib¶
The contextlib
package supplies some handy decorators and context manager classes. For example:
-
suppress
(*exceptions)¶ Return a context manager that suppresses any of the specified exceptions if they occur in the body of a with statement and then resumes execution with the first statement following the end of the with statement.
An example of its usage is below:
import os
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove('foo.txt')
In the above case, regardless of what happens, no FileNotFoundError
exception will make it outside the with
statement.
There are other context managers in that package that you should explore and considering using where possible.