[Python-Dev] Simpler finalization semantics (was Re: PEP 343 - Abstract Block Redux)

Phillip J. Eby pje at telecommunity.com
Mon May 16 21:12:13 CEST 2005


At 11:20 AM 5/16/2005 -0700, Guido van Rossum wrote:
>I think the issue here is not implementation details but whether it
>follows a certain protocol. IMO it's totally acceptable to require
>that the expression used in a with-statement support an appropriate
>protocol, just like we require the expression used in a for-loop to be
>an iterable.

Perhaps I didn't explain well.  I mean that whether early release of a 
resource is desirable, depends on the resource.  For example, consider a 
file versus a StringIO.  If files support resource release and StringIO's 
don't, this pollutes client code with implementation knowledge.

Therefore, the penalty for people trying to clean up resources early is 
that they either pollute their client code with checking to see if things 
are 'with'-able (which seems insane), or else adding empty __enter__ and 
__exit__ methods to things so that their consumers can just use "with" 
whenever they want to scope a resource, whether the resource actually needs 
it or not.

I'm suggesting that we simply take Nick's proposal to its logical 
conclusion, and allow any object to be usable under "with", since it does 
not create any problems to do so.  (At least, none that I can see.)  A 
redundant 'with' does no harm; in the worst case it's just a hint to the 
reader about the scope within which an expression is used within the 
current fuction body.


> > This insight may actually be true regardless of what generators do or don't
> > do; the point is that if you change from using a generator to a built-in
> > iterator type, you shouldn't have to change every place you were using the
> > 'with' blocks to work again.
>
>Huh? The with-statement doesn't loop, and its use of generators is
>such that I don't see how you could ever replace it with a built-in
>iterator.

I'm referring here to Nick's proposal of doing things like this:

     with some_function() as items:
         for item in items:
             # etc.

To ensure that a *normal* generator is finalized.  This isn't about 
generator templates.  So, if you were ever to change 'some_function()' to 
return a list instead of being a generator, for example, this code would no 
longer work.


>About deleting VAR I have mixed feelings. I appreciate the observation
>that it's most likely dead after the with-statement is over, but I'm
>not sure that explicit deletion is correct. Remember that VAR can be
>an arbitrary assignment target, which means it could be a global, or a
>tuple, or an indexed list or dict item, or some object's attribute (I
>hesitate to write "etc." after such an exhaustive list :-).

Argh.  I forgot about that.  :(  On the other hand, I haven't seen any 
examples where arbitrary assignment targets were actually used, so perhaps 
it could be limited to local variables (and possibly-nested tuples 
thereof).  But I also partly agree with the false-sense-of-security thing, 
too, so I'm not sure on this either now that you point out the issue.  It 
also makes me wonder about something like this:

     with open("foo") as f:
         def callback():
             return f.read()

which can't behave sanely if 'callback' is used outside the 'with' 
block.  Of course, it won't work right even if 'f' remains bound to the 
file object, but this does seem to open new implementation complexities to 
get all these pieces to work right.  :(


> > should also be short for this (in the case where some_expr() has no
> > __enter__ or __exit__ methods):
> >
> >       foo = some_expr()
> >       try:
> >           # etc.
> >       finally:
> >           del foo
>
>-1 on this. You're trying to pack too much into a single statement.

Note that I was just leaving out the rest of the standard PEP 3XX expansion 
above, just highlighting the proposed 'del' portion of the expansion.  That 
is, I wasn't proposing an alternate expansion, just showing the effect of 
the other parts of the expansion being skipped because of null 
__enter__/__exit__.


> > And that could be a useful thing for many existing object types, without
> > even updating them for PEP 34[0-9].  :)  It wouldn't be *as* useful for
> > non-CPython implementations, but presumably by the time those
> > implementations catch up, more code will be out there with
> > __enter__/__exit__ methods.
>
>Most likely it would just give an implementation-dependent false sense
>of security.

I think I prefer to see the glass half-full here; that is, I think it 
increases the safety during a transitional period.  But the scoping of 
arbitrary VAR expansions makes things trickier, so I'll think about this 
some more.


>-1 on the whole thing (and noting that this would be an easy extension
>if at some point in the future we change our mind).

Your post has made me think that I might have the concept 
backwards.  Instead of guaranteeing that the variable goes out of scope at 
the end of the block, what a 'with' block actually guarantees (implicit in 
all of the PEP 34x variants) is that EXPR will remain *alive* for the 
duration of the block, without needing to hold it in a variable 
anywhere.  That is:

     with foo():
         bar()

Guarantees that the result of 'foo()' will remain alive at least until the 
block is exited.

(I'm not saying this is anything new; it was implicit all along.  I'm just 
musing about ways to summarize or introduce the 'with' block to people.)



More information about the Python-Dev mailing list