[Python-Dev] PEP 343 rewrite complete
Guido van Rossum
gvanrossum at gmail.com
Thu Jun 2 22:58:05 CEST 2005
[Nick Coghlan]
> Also, I'm wondering if it would be useful to have a 'closing' template
> that looked like:
>
> @with_template
> def closing(obj):
> try:
> yield obj
> finally:
> obj.close()
>
> That can be used to deterministically close anything with a close
> method, be it file, generator, or something else:
>
> with closing(open("argument.txt")) as contradiction:
> for line in contradiction:
> print line
>
> with closing(some_gen()) as data:
> for datum in data:
> process(datum)
I just realized this has a race condition. The bytecode for the
expression closing(open("...")) must necessarily contain a bytecode
that calls open() followed by another bytecode that calls closing().
If a ^C happens between these two byte codes, the stack contains an
open file object that won't be closed explicitly.
With the original opening() template, this race can be avoided (and I
intend to do so) by implementing opening() in C (as a class with
__enter__ and __exit__ methods), and by making sure that the
interpreter does *not* check for interrupts between the call to
__enter__ and the start of the try-finally-statement in the
translation of the with-statement.
The only way to avoid the race that I can see with the closing()
template would be to disable signals for the duration of the
evaluation of the expression in the with-statement, but I really don't
like that solution at all -- system calls like that can be
excruciatingly expensive compared to bytecode execution. Most
applications don't catch ^C so they don't need this extra protection
-- but the compiler doesn't know that so everybody pays for it. The
solution for avoiding interrupts between the __enter__() call and the
try start doesn't require disabling signals; there can be a single
opcode that calls __enter__ and sets up the try-finally context.
--
--Guido van Rossum (home page: http://www.python.org/~guido/)
More information about the Python-Dev
mailing list