Next / Previous / Contents / Shipman's homepage

3.3. Context managers and the “with” construct

Starting in Python 2.5, Python adds a “with” statement feature, and the concept of a context manager. The sox module supports this feature, and its use can simplify and clarify the structure of the Python code and its generated XML.

A context manager is any instance of a class that defines two special methods, .__enter__ and .__exit__, that do initialization and cleanup operation respectively.

Here is the general form of the with statement, at its simplest:

with E [as V]: suite

Execution of this construct proceeds like this.

  1. Expression E is evaluated. It must produce a context manager.

  2. The context manager's .__enter__() method is called with no arguments (except self).

    If the expression is followed by “as”, the name V is bound to whatever value was returned by the .__enter__() method.

  3. The suite (containing one or more statements) is executed.

  4. The context manager's .__exit__ method is called with argument list (self, exc_type, exc_value, traceback).

    If the suite completed without raising an exception, the last three arguments will all be None.

    If the execution of the suite raised an exception, the last three arguments will be: the exception type; the exception value; and a stack traceback. Consult the The Python Language Reference for the details of the exception case.

In general, you can have several clauses in a with statement separated by commas, so for example this is quite legal:

with c1() as c, xyz(15), qrz("Jack") as w5dau:
    f(c, w5dau, "(5,9)")

The above example is equivalent to:

with c1() as c:
    with xyz(15):
        with qrz("Jack") as w5dau:
            f(c, w5dau, "(5,9)")

Since the purpose of a context manager is to ensure that cleanup operations are performed, and since the problem of XML stream generation is to ensure that tags are closed in the proper order, we can take advantage of this construct by making the Elt class a context manager, whose .__exit__() method generates the element's closing tag.

Here, then, is a rewrite of our simple XHTML generation example using context managers.

import sys
import sox
s = sox.Sox(sys.stdout)

with s.start("html"):
    with s.start("head"):
        s.leaf("title","Page title here"):
    with s.start("body"):
        s.leaf("h1", "Main title here"):
        s.leaf("hr")
        s.leaf("p", {'class': 'note'}, "Some text",
                     " and some more text", id='p001'):