[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