[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