[Python-Dev] Acquire/release functionality (Was: Extended Function syntax)

Paul Moore lists@morpheus.demon.co.uk
Sun, 02 Feb 2003 21:49:31 +0000


Guido van Rossum <guido@python.org> writes:

> I'm now wondering if there aren't really only two interesting cases,
> none of which require saving the thunk and calling it later:
>
> - synchronized-like, where the block should connect to its environment
>
> - property-like, where the block should introduce a new scope, and the
>   caller should be able to inspect or control that scope's namespace

I certainly haven't seen any use cases other than these two. In fact,
pretty much all of the cases I am aware of are synchronised-like,
which I've been referring to as acquire/release. The only
property-like cases I'm aware of are properties themselves.

Personally, I don't have much problem with the current property
approach, and the comments I've seen have been generally 50-50. I'd
like to see some focused discussion on property syntax, but maybe it
could be spun off onto a separate thread?

> All cases where something is saved to be called later can be done
> by using the extended 'def' syntax (def f(...) [something]: ...).

This sounds interesting, but I can't quite see what you are saying. Do
you have a real use case in mind?

As far as the acquire/release case is concerned, I think that there is
definitely a strong case for syntactic support for this. The
equivalent idiom in C++ is "resource acquisition is initialisation"
which, while not having syntax support, does rely on the existence of
deterministic destructors, and on the ability to introduce a new scope
at will. Python has neither of these, and the try...finally construct
is verbose enough to make something better worth having.

On the other hand, the simple "with" construct proposed by Samuele

> with <expr>:
>  <suite>
>
> would be possibly sugar for:

[...]

> _x = <expr>
> _x.__enter__()
> try:
>   try:
>    <suite>
>   except  getattr(_x,'__excepts__',()),_e:
>     _x.__except__(_e)
> finally:
>   _x.__exit__()

extended as suggested by Alex to allow __enter__ to be optional and to
allow for an assignment in the with statement, seems to cover this
excellently. (I'm not sure the exception handling needs to be quite
that complex, but that's a minor point).

Holger Kregel's suggestion is pretty much identical (although he
allows for multiple objects in the same statement).

> To satisfy a) i implemented the following execution hooks 
> (which all return None):
>
>     class some_execution_handler:
>
>         def __enter__(self):
>             """called before my indented block is executed """
>
>         def __except__(self, type, value, traceback):
>             """called if an exception occurs during execution"""
>
>         def __leave__(self):
>             """called when execution is finished.
>     
>                this is called if there was no exception AND
>                if there was an exception but no __except__ hook
>             """

So, to offer a unified proposal, I'd suggest the following syntax:

with [ var1 [, var2, ... ] = ] expr1 [, expr2 , ... ]:
    suite

This is equivalent to

var1, var2, ... = expr1, expr2, ...

if hasattr(var1, "__enter__"):
    var1.__enter__()
if hasattr(var2, "__enter__"):
    var2.__enter__()
...

try:
    try:
        suite

    except:
        # Handwave here for now

finally:
    ...
    var2.__exit__()
    var1.__exit__()

In this, any varN can be omitted and a dummy hidden variable is
used. The handwaving around the except clause needs to be clarified -
what I want is to call varN.__except__() ... var1.__except__()
allowing each in turn to catch the exception. If none of them do then
the exception should be propogated. The problem is that I'm not sure
what is the best way for an __except__ method to signal that it caught
the exception.

The __except__ attribute should be optional, as well. I can't see any
reason why it would ever be useful for __exit__ to be optional, but
for generality it might be worth allowing that as well.

I like "with" as the keyword here. One obvious question - if we decide
to go with syntax like this, would we be looking at "pre-reserving"
the keyword in Python 2.3 somehow, so that it could become a full
keyword in 2.4? Or would the new syntax need a future statement in
Python 2.4, to become finalised in 2.5?

Paul.

PS Just to summarise things once more: as I see it, we now have a
   number of threads lurking:

   - The def f(arg) [...] syntax that started all this off
   - With blocks (which give us acquire/release) for which I propose a
     unified solution above
   - Properties, which don't have a specific solution proposed yet
     (AFAICT)
   - Thunks in various forms, most notably Guido's hyper-general
     version (the one that lets us implement switch)

   It's not at all clear to me that the thunk based solutions have
   killer use cases that they solve better than anything else
   (assuming that switch doesn't count :-)) but they may be aiming at
   the property case. If so, I'd appreciate a concrete "this is how
   we'd write a property" explanation, as I'm losing the plot on
   that... On the other hand, thunks seem too heavyweight to me if
   they don't offer some other major advantage as well as properties
   (I actually prefer with for the acquire/release case).

   I'll stop now before I feel a need to summarise the summary of my
   summary :-)
-- 
This signature intentionally left blank