RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


A Parade of New Features Debuts in Python 2.5 : Page 4

Python 2.5 still has the smell of fresh paint but it's the perfect time to drill down on the most important new features in this comprehensive release. Read on for detailed explanations and examples of exception handling, resource management, conditional expressions, and more.

Resource Management With 'with'
People always ask me: 'Why doesn't C++ have a finally clause to clean up resources in the face of exceptions? What happens if an exception is thrown before some resource was cleaned up properly?' Well, the truth is nobody ever asked me that. It's an utter lie. Still, lots of people wonder about this issue and newsgroups are rife with suggestions to add a finally keyword to C++. It won't happen. The reason is that C++ has a different way to guarantee resource cleanup. It's called a destructor.

Every C++ object has a destructor that is called automatically when an object that's allocated on the stack exits its enclosing scope even when an exception is thrown. C++ preaches that you should wrap every resource in an object that will take care of the cleanup in its destructor. This idiom is called "Resource Acquisition Is Initialization" (RAII). Is RAII better than an explicit try-finally block? Usually, it's much better. It allows simpler and more readable code. Rather than write the cleanup code in every place you use the resource, you write it just once—in the resource object destructor. You don't have to introduce a try-finally block with the mandatory nesting and name scoping. It's impossible to forget to call cleanup code.

Well, guess what? RAII is exactly the model for the new 'with' statement in Python. It allows you to use a resource (that supports it) in a special with-block and not worry about cleanup. I'll develop a mini-example soon, but first I'll whet your appetite:

lock = threading.Lock()
with lock:
    # Critical section of code
The lock will be released automatically at the end of the critical section. A shorter version is:

with threading.Lock() as lock:
    # Critical section of code
The next example is more general. In it I have an ImportantResource class and a ResourceCoordinator class. The ResourceCoordinator is responsible for coordinating the use of a single resource between multiple users. Users are supposed to acquire the resource from the ResourceCoordinator, use it, and relinquish it.

When looking at the code notice that 'with' is not yet a full-fledged Python statement. In order to use it you must import it using __future__. This is a standard Python mechanism to introduce new keywords in case you have classes or variables that are named 'with'. In Python 2.6 'with' will gain first-class status.

from __future__ import with_statement
import random

class ImportantResource(object):
    def __init__(self, rc):
        self._cookie = None
        self._resourceCoordinator = rc
    def use(self):
        print 'ImportantResource - good job!'
    def __enter__(self):
    def __exit__(self, type, value, tb):

class ResourceCoordinator(object):
    def __init__(self):
        self._resource = ImportantResource(self)
        self._cookie = None

    def acquire(self):
        if self._cookie == None:
            self._cookie = random.random()
            print 'ResourceCoordinator - resource acquired'
            self._resource._cookie = self._cookie
            return (self._cookie, self._resource)
            return (None, None)

    def relinquish(self, cookie):
        if cookie == self._cookie:
            self._cookie = None
            print 'ResourceCoordinator - resource released'
If the resource is not being held by someone the ResourceCoordinator hands out the resource and a random cookie when acquire() is called. If the cookie is something other than None it means the resource is unavailable (someone already holds it) and a pair of Nones is returned. When the holder calls relinquish() the ResourceCoordinator sets its cookie to None, so the next acquirer will be able to get hold of the resource. The cookie is also used to verify (weakly) that the relinquisher is indeed the current holder. This is a very poor and unsafe implementation of a resource coordination framework meant only to demonstrate the 'with' statement.

The ImportantResource instance is initialized by the ResourceCoordinator and gets the current cookie every time it is acquired. The __enter__ and __exit__ methods are what makes ImportantResource a context manager qualified to participate in the 'with' game. The __enter__ method is called upon entry to the 'with' block and the result is assigned to the 'as' variable if one is available (see the lock variable in the threading example). The __exit__ method is called upon exit from the 'with' block. If an exception was raised it receives the type, value, and traceback, otherwise all the parameters are None.

It's time to experiment a little with the ResourceCoordinator. I created three experiments. In each experiment the resource is acquired from the ResourceCoordinator, used, and relinquished. In experiment_1 the user must remember to relinquish the resource. If an exception is raised the resource will never be relinquished.

def experiment_1(rc):
    """Not so good. If exception is thrown resource is not relinquished"""
    print '-'*5, 'Experiment 1'
    cookie, resource = rc.acquire()
In experiment_2 the code is placed inside a try-finally block. The user must still remember to relinquish the resource properly in the finally clause using the returned cookie, but at least it is guaranteed to happen even in the face of exceptions.

def experiment_2(rc):
    """Better. finally ensures that the resource is relinquished"""
    print '-'*5, 'Experiment 2'
        cookie, resource = rc.acquire()
In experiment_3 I used a with-block and it is much more concise. No need to manage a cookie or explicitly call relinquish.

def experiment_3(rc):
    """Much Better. Using the 'with' statement effectively"""
    print '-'*5, 'Experiment 3'

    resource = rc.acquire()[1] # no need to store the cookie now
    with resource:
I was kind enough not to raise any exceptions in all experiments so the results are identical. Here is the main script that invokes all experiments and the output:

if __name__=='__main__':
    rc = ResourceCoordinator()

----- Experiment 1
ResourceCoordinator - resource acquired
ImportantResource - good job!
ResourceCoordinator - resource released

----- Experiment 2
ResourceCoordinator - resource acquired
ImportantResource - good job!
ResourceCoordinator - resource released

----- Experiment 3
ResourceCoordinator - resource acquired
ImportantResource - good job!
ResourceCoordinator - resource released
The 'with' statement supports another idiom, which is similar to the IDisposable interface in C#. If an object has a method named close() (yes, that would definitely be file objects) then they can be used as context managers too, without implementing __enter__ and __exit__. Here is how it is done:

from contextlib import closing
with closing(open('test.txt', 'w')) as f:
            f.write('Yeah, it works!!!')

print open('test.txt').read()

Note that 'closing' must be explicitly imported from contextlib.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date