Section 10.4. Context Management


10.4. Context Management

10.4.1. with Statement

The unification of TRy-except and TRy-finally as described above makes programs more "Pythonic," meaning, among many other characteristics, simpler to write and easier to read. Python already does a great job at hiding things under the covers so all you have to do is worry about how to solve the problem you have. (Can you imagine porting a complex Python application into C++ or Java?!?)

Another example of hiding lower layers of abstraction is the with statement, made official as of Python 2.6. (It was introduced in 2.5 as a preview and to serve warnings for those applications using with as an identifier that it will become a keyword in 2.6. To use this feature in 2.5, you must import it with from __future__ importwith_statement.)

Like try-except-finally, the with statement, has a purpose of simplifying code that features the common idiom of using the TRy-except and try-finally pairs in tandem. The specific use that the with statement targets is when TRy-except and try-finally are used together in order to achieve the sole allocation of a shared resource for execution, then releasing it once the job is done. Examples include files (data, logs, database, etc.), threading resources and synchronization primitives, database connections, etc.

However, instead of just shortening the code and making it easier to use like try-except-finally, the with statement's goal is to remove the TRy, except, and finally keywords and the allocation and release code from the picture altogether. The basic syntax of the with statement looks like this:

with context_expr [as var]:     with_suite


It looks quite simple, but making it work requires some work under the covers. The reason is it not as simple as it looks is because you cannot use the with statement merely with any expression in Python. It only works with objects that support what is called the context management protocol. This simply means that only objects that are built with "context management" can be used with a with statement. We will describe what that means soon.

Now, like any new video game hardware, when this feature was released, some folks out there took the time to develop new games for it so that you can play when you open the box. Similarly, there were already some Python objects that support the protocol. Here is a short list of the first set:

  • file

  • decimal.Context

  • tHRead.LockType

  • threading.Lock

  • threading.RLock

  • threading.Condition

  • tHReading.Semaphore

  • tHReading.BoundedSemaphore

Since files are first on the list and the simplest example, here is a code snippet of what it looks like to use a with statement:

with open('/etc/passwd', 'r') as f:      for eachLine in f:           # ...do stuff with eachLine or f...


What this code snippet will do is... well, this is Python, so you can probably already guess. It will do some preliminary work, such as attempt to open the file, and if all goes well, assign the file object to f. Then it iterates over each line in the file and does whatever processing you need to do. Once the file has been exhausted, it is closed. If an exception occurs either at the beginning, middle, or end of the block, then some cleanup code must be done, but the file will still be closed automatically.

Now, because a lot of the details have been pushed down and away from you, there are really two levels of processing that need to occur: First, the stuff at the user levelas in, the things you need to take care of as the user of the objectand second, at the object level. Since this object supports the context management protocol, it has to do some "context management."

10.4.2. *Context Management Protocol

Unless you will be designing objects for users of the with statement, i.e., programmers who will be using your objects to design their applications with, most Python programmers are going to be just users of the with statement and can skip this optional section.

We are not going into a full and deep discussion about context management here, but we will explain the types of objects and the functionality that are necessary to be protocol-compliant and thus be eligible to be used with the with statement.

Previously, we described a little of how the protocol works in our example with the file object. Let us elaborate some more here.

Context Expression (context_expr), Context Manager

When the with statement is executed, the context expression is evaluated to obtain what is called a context manager. The job of the context manager is to provide a context object. It does this by invoking its required __context__() special method. The return value of this method is the context object that will be used for this particular execution of the with_suite. One side note is that a context object itself can be its own manager, so context_expr can really be either a real context manager or a context object serving as its own manager. In the latter case, the context object also has a __context__() method, which returns self, as expected.

Context Object, with_suite

Once we have a context object, its __enter__() special method is invoked. This does all the preliminary stuff before the with_suite executes. You will notice in the syntax above that there is an optional as var piece following context_expr on the with statement line. If var is provided, it is assigned the return value of __enter__(). If not, the return value is thrown away. So for our file object example, its context object's __enter__() returns the file object so it can be assigned to f.

Now the with_suite executes. When execution of with_suite terminates, whether "naturally" or via exception, the context object's __exit__() special method is called.__exit__()takes three arguments. If with_suite terminates normally, all three parameters passed in are None. If an exception occurred, then the three arguments are the same three values returned when calling the sys.exc_info() function (see section 10.12): type (exception class), value (this exception's instance), and traceback, the corresponding traceback object.

It is up to you to decide how you want to handle the exception here in __exit__(). The usual thing to do after you are done is not to return anything from __exit__() or return None or some other Boolean False object. This will cause the exception to be reraised back to your user for handling. If you want to explicitly silence the exception, then return any object that has a Boolean TRue value. If an exception did not occur or you returned true after handling an exception, the program will continue on the next statement after the with clause.

Since context management makes the most sense for shared resources, you can imagine that the __enter__() and __exit__() methods will primarily be used for doing the lower-level work required to allocate and release resources, i.e., database connections, lock allocation, semaphore decrement, state management, opening/closing of files, exception handling, etc.

To help you with writing context managers for objects, there is the contextlib module, which contains useful functions/decorators with which you can apply over your functions or objects and not have to worry about implementing a class or separate __context__(), __enter__(), __exit__() special methods.

For more information or more examples of context management, check out the official Python documentation on the with statement and contextlib module, class special methods (related to with and contexts), PEP 343, and the "What's New in Python 2.5" document.



Core Python Programming
Core Python Programming (2nd Edition)
ISBN: 0132269937
EAN: 2147483647
Year: 2004
Pages: 334
Authors: Wesley J Chun

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net