[Python-Dev] PEP 343 - Abstract Block Redux

Shane Hathaway shane at hathawaymix.org
Sun May 15 01:10:20 CEST 2005


Guido van Rossum wrote:
> [Nick Coghlan]
> 
>>Also, the call to __enter__() needs to be before the try/finally block (as it is
>>in PEP 310). Otherwise we get the "releasing a lock you failed to acquire" problem.
> 
> 
> I did that on purpose. There's a separate object ('abc' in the
> pseudo-code of the translation) whose __enter__ and __exit__ methods
> are called, and in __enter__ it can keep track of the reversible
> actions it has taken.
> 
> Consider an application where you have to acquire *two* locks regularly:
> 
>     def lockBoth():
>         got1 = got2 = False
>         lock1.acquire(); got1 = True
>         lock2.acquire(); got2 = True
>         yield None
>         if got2: lock2.release()
>         if got1: lock1.release()
> 
> If this gets interrupted after locking lock1 but before locking lock2,
> it still has some cleanup to do.

That code is incorrect, though.  Say lockBoth() acquires lock1 but then
lock2.acquire() throws an exception.  (Maybe the lock requires some I/O
operation, and the operation fails.)  The interpreter will never reach
the yield statement and lock1 will never be released.

You really have to write it like this:

    def lockBoth():
        lock1.acquire()
        try:
            lock2.acquire()
        except:
            lock1.release()
            raise
        yield None
        try:
            lock2.release()
        finally:
            lock1.release()

> I know that this complicates simpler use cases, and I'm not 100% sure
> this is the right solution; but I don't know how else to handle this
> use case.

If __enter__ raises an exception, it has to clean up after itself before
propagating the exception.  __exit__ shouldn't be called if __enter__ fails.

Shane


More information about the Python-Dev mailing list