Defining a Context Manager Class
While the file example in the previous section shows an object
which is both the Context Manager and the Working Object. We'll show the
two as separate class definitions in order to clearly separate the two
elements of the design pattern.
A Context Manager must implement two methods to collaborate
properly with the
with
statement.
-
__enter__
-
This method is called on entry to the
with
statement. The value returned by this
method function will be the value assigned to the
as
variable.
-
__exit__
(
type
,
value
,
traceback
)
-
This method is called on exit from the
with
statement. If the
type
,
value
or
traceback
parameters have values other than
None
, then an exception occured. If the
type
,
value
or
traceback
parameters have values of
None
, then this is a normal conclusion of the
with
statement.
Example Context Manager. Let's assume we must produce a file which has a proper final
line, irrespective of errors encountered during the file's production.
For example, we could be producing a file with a secure hash like an
MD5 digest of the contents, This secure digest can be used to detect
processing errors or attempted tampering with the file.
We'll look at our working object first, then we'll look at a
Context Manager for that object. This is the working object,
SecureLog
. This is a class which writes to a
file, as well as accumulate a secure digest using the MD5
algorithm.
import hashlib
class SecureLog( object ):
def __init__( self, someFile, marker="-----HASH-----" ):
self.theFile= someFile
self.marker= marker
self.hash= hashlib.md5()
def write( self, aLine ):
self.theFile.write( aLine )
self.hash.update(aLine)
def finalize( self ):
self.theFile.write( "%s\n%s\n" % ( self.marker, self.hash.hexdigest(), ) )
def close( self ):
pass
If this class is used correctly, the file will end with a marker
line and the MD5 digest value. Other programs use the same MD5 algorithm
when reading the file to confirm that the expected digest is the actual
digest.
Here is a Context Manager, named
SecureLogManager
, which incorporates the
SecureLog
class. To be a context manager, this
class implements the required __enter__
and
__exit__
methods.
class SecureLogManager( object ):
def __init__( self, someFile ):
self.theFile= someFile
def __enter__( self ):
self.transLog= SecureLog( self.theFile )
return self.transLog
def __exit__( self, type, value, tb ):
if type is not None:
pass # Exception occurred
self.transLog.finalize()
self.transLog.close()
self.theFile.close()
|
The __enter__ method creates the
SecureLog and returns it so that the
as
clause will assign the log to a
variable.
|
|
The __exit__ method checks to see
if it is ending with an exception. In this case, we don't do any
special processing for exceptions raised within the
with
statement. The
__exit__ method, however, uses the
SecureLog 's
finalize method to be absolutely sure
that a proper digest is written to the log file before it is
closed.
|
The overall main program can have the following structure. We
don't need to make special arrangements in the main program to be sure
that the log is finalized correctly. We have delegated those special
arrangements to the context manager object, leaving us with an
uncluttered main program.
from __future__ import with_statement
result= open( 'log.log', 'w' )
with SecureLogManager( result ) as log:
log.write( "Some Configuration\n" )
source= open('source.dat','rU')
for line in source:
log.write( line )
source.close()
|
Until Python 2.6 arrives, we must add the
with
statement to Python with the from
__future__ import statement. This statement must be one of
the first statements in the program.
|
|
The
with
statement does several things.
First, it creates an instance of
SecureLogManager , which is our context
manager. Then the __enter__ method is
evaluated, which returns an instance of
SecureLog for use by the suite inside the
with
statement.
After this initialization, the suite of statements is
executed. Once the suite of statements finishes, the
SecureLogManager 's
__exit__ method is evaluated. This final
step will finalize and close the log file.
|