[Python-Dev] PEP 343 - next steps
Phillip J. Eby
pje at telecommunity.com
Sat Jun 11 21:09:59 CEST 2005
At 09:02 AM 6/11/2005 -0700, Guido van Rossum wrote:
>[Nick]
> > I also added a new question to the Wiki regarding what, if any,
> > guarantees will be made regarding when (or if) asynchronous interrupts
> > will be blocked. Given that the call to __enter__() can invoke
> > arbitrary Python code, is pushing the with statement setup inside a
> > single opcode actually sufficient?
>
>Not sufficient if __enter__ is written in Python; in that case,
>*nothing* can be done to make it sufficient.
I think PEP 343 would greatly benefit from some discussion of the
signalling issue. I've been wracking my brain a bit on it, and I'm having
trouble seeing where the problem is.
>The single opcode makes it possible to implement __enter__ in C to
>solve the issue in specific cases.
Or if __enter__ is a no-op, that would also solve the problem. Suitability
for 'with' could be keyed off of the __exit__ method instead. If there's
no __enter__, or the __enter__ is a default no-op __enter__ written in C,
then __exit__ is sufficient for many resources.
> > It also considers the possibility of using with statements in an RAII
> > style by acquiring the resource in __init__ or __new__ rather than
> > __enter__.
>
>Python isn't C++; you shouldn't do that.
Actually, it seems to me that it's standard procedure in today's Python:
objects allocate resources in __init__, and then have a 'close()' or some
similar method to release them. That's why so many people see this:
with open('foo') as f:
...
As a natural use of the 'with' statement, whether they have any familiarity
with C++ or not. (My own experiences with C++ are perhaps a decade out of
date, long before RAII was a meme in any event.)
It seems to me that there are two sensible patterns for using 'with':
1. __enter__ allocates or acquires a resource, __exit__ releases it
2. EXPR allocates or acquires a resource, __exit__ releases it (and
__enter__ doesn't execute any Python, because it's a no-op)
Pattern 1 is useful for transactions, locks, redirection, decimal contexts,
etc.
Pattern 2 is useful for resources that are acquired by creation anyway,
like files.
But maybe my understanding is flawed, because I don't *really* understand
the signal issue. I've been assuming that you're talking about the fact
that an exception can occur anywhere, due to e.g. KeyboardInterrupt or an
exception raised by some other signal handler. It seems to me that using
pattern 2, one could write 'closing()' safely
by doing this:
class closing(object):
def __init__(self, resource):
try:
self.resource = resource
except:
resource.close()
raise
def __exit__(self,*exc):
self.resource.close()
but maybe I'm still fundamentally "not getting" how the signal issue
manifests in practice. I suppose it's true that *another* exception could
then occur between the LOAD_ATTR and the CALL opcodes in the exception
handler here, but it's also true that this could happen with any Python
exception-handling code, and it could also happen in __exit__(). So I'm
not sure I see why this should influence the design of 'with' very much,
other than to point out that no Python code is safe from asynchronous
exceptions.
In any case, C-implemented types can handle this situation just fine, so a
'closing' written in C could easily be included with the implementation.
More information about the Python-Dev
mailing list