[Guido] > > In rev 1.10 I moved the __enter__ call out of the > > try-block again. Having it inside was insane: when __enter__ fails, it > > should do its own cleanup rather than expecting __exit__ to clean up > > after a partial __enter__.
[Ka-Ping Yee] > No, it wasn't insane. You had a good reason for putting it there. I did some introspection, and it was definitely a temporary moment of insanity: (1) I somehow forgot the obvious solution (that __enter__ should clean up its own mess if it doesn't make it to the finish line); (2) once a generator raises an exception it cannot be resumed, so the generator-based example I gave can't work. (I was too lazy to write down the class-based example, which *can* be made to work of course.) > The question is what style of implementation you want to encourage. > > If you put __enter__ inside, then you encourage idempotent __exit__, > which makes resource objects easier to reuse. But consider threading.RLock (a lock with the semantics of Java's monitors). Its release() is *not* idempotent, so we couldn't use the shortcut of making __enter__ and __exit__ methods of the lock itself. (I think this shortcut may be important for locks because it reduces the overhead of frequent locking -- no extra objects need to be allocated.) > If you put __enter__ outside, that allows the trivial case to be > written a little more simply, but also makes it hard to reuse. I skimmed your longer post about that but didn't find the conclusive evidence that this is so; all I saw was a lot of facts ("you can implement scenario X in these three ways) but no conclusion. The real reason I put it inside the try was different: there's a race condition in the VM where it can raise a KeyboardInterrupt after the __enter__() call completes but before the try-suite is entered, and then __exit__() is never called. Similarly, if __enter__() is written in Python and wraps some other operation, it may complete that other operation but get a KeyboardInterrupt (or other asynchronous exception) before reaching the (explicit or implicit) return statement. Ditto for generators and yield. But I think this can be solved differently in the actual translation (as opposed to the "translate-to-valid-pre-2.5-Python"); the call to __exit__ can be implicit in a new opcode, and then we can at least guarantee that the interpreter doesn't check for interrupts between a successful __exit__ call and setting up the finally block. This doesn't handle an __enter__ written in Python not reaching its return; but in the presence of interrupts I don't think it's possible to write such code reliably anyway; it should be written using a "with signal.blocking()" around the critical code including the return. I don't want to manipulate signals directly in the VM; it's platform-specific, expensive, rarely needed, and you never know whether you aren't invoking some Python code that might do I/O, making the entire thread uninterruptible for a long time. -- --Guido van Rossum (home page: http://www.python.org/~guido/) _______________________________________________ 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