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 thefinallyblock in atrystatement.
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
multiprocessingpackage, 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_suiteexecuting- Teardown the context created after
with_suitefinishes 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
expressionis 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
targetis present, the value returned by__enter__()is assigned to it.The
with_suiteis 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
withstatement with additionalexpressionandtargetsections.
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.