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 _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com