[Python-Dev] exec/with thunk-handling proposal (on a new thread eventually)

holger krekel pyth@devel.trillke.net
Mon, 3 Feb 2003 14:35:31 +0100


[sorry that this posting may double but i failed to post
 this on a new thread and even 'mutt' with 120 columns
 doesn't show the subject anymore]


Hello,

I'll try to take the discussion about a "unified approach"
towards 'code block' or thunk handling into a fresh thread
and present what Michael Hudson truthfully calls 
"a friendly competing approach". 


Preliminaries
-------------

We have some use cases for a construct that allows 
encapsulating try/except/finally hooks into
an object.  Especially for timely finalization (of files,
locks or other resources) we don't want to scatter
try-finally constructs all over the place.  

Please understand that the following proposal has 
nothing to do with "function-level" thunk-manipulation 
ala classmethod but only with "inline" thunks (which 
don't and shouldn't have their own scope). 

Syntax and semantic proposal
----------------------------

I think we can may get away with only a "weak" keyword
and allow the aforementioned encapsulation of execution 
events into an object like this:

    exec expr [with params]: suite

where the expression is evaluated to return a
"thunk" handler with these optional "execution" hooks:

    def __enter__(self):  
        "before suite start"

    def __except__(self, type, value, tb): 
        "swallow given exception, reraise if neccessary"

    def __leave__(self):
        """upon suite finish (not called if __except__ 
           exists and an exception happened)
        """

The above "with" parameters (of the form name=expr, comma-separated) 
are bound in local (or global/nested) *and* handler instance 
namespace.  The 'suite' is what we call "thunk".

The above logic allows clean timely finalization for
multiple ressources:

    exec autoclose() with f1=open(name1), f2=open(name2, 'w'):
        for line in f1:
            ...
            f2.write(...)

which would execute as follows

    a) autoclose() instance is created and stored as the 
       "thunk"-handler

    b) f1/f2 are stored as attributes on the autoclose instance

    c) f1/f2 are put into the local/global namespace (and nested ones
       if rebinding is allowed)

    d) thunk executes (for line ...)

    e) autoclose 'leave' hook is called (with or without exception)
       and is implemented like this:
        
       def __leave__(self):
            for obj in self.__dict__.values(): 
                obj.close()

    f) thunk handler is removed 

Because computing 'f1' may succeed but 'f2' can subsequently
fail the assignments *have to* execute within "autoclose"
control.  

Now on to the usage of the except hook.  Nice use cases might be

    exec retry(maxretry=3, on=IOError): 
        # do network io-stuff
or
    exec skip_on(AttributeError, TypeError):
        some_object.notify_hook()

but i am sure there are more.  Exception handling is often
ugly when inlined with the code.  I think that stating 
'exception behaviour' up-front allows to write nice 
readable constructs. 


__exit__ versus __leave__
---------------------------

One remark (mainly to Michael as he does that other 
patch) about the hook-name __leave__ versus __exit__.
we may want to eventually allow 'yield' within the
thunk and then '__exit__' would be misleading.  Here is
the use case:

    exec self.mylock:   # would lock/unlock on entering/leaving 
                        # the generator
        ...
        for whatever in something:
            yield whatever  
        ...

Or do you think that this (future) use case warrants 
yet another hook?

If there is interest i can probably modify my patch 
to allow all of the proposed syntax so that you could 
play around with it.  

the next additional idea is not essential for my so-far
proposal (but hopefully interesting, nonetheless).

regards and interested in comments,

    holger


-------------  Catching values ala Quixote -------------------

With my former patch there was another "execution" event: 
the catching of "dropped" values ala Quixote.  For values
that are not assigned to a name and would be discarded otherwise
the execution handler can implement another hook:

    def __catch__(self, value):
        "catch dropped value from thunk"

this allows anonymous (un-named) objects to be passed 
*within the thunk* to the execution handler. 
some example use-cases:

    exec debug: 
        if condition:
            "something questionable happenend"

so you can quite completly encapsulate debug-handling 
into one object.  Note that this exec could be 
special-cased to do nothing if "debug" is None. 
Another one (ala the Quixote-framework):

    exec html_stream:
        "<h1>title</h1>"
        "<p>%s</p>" % html_quote(para)

or even:

    exec html.tr():
        exec html.td(): "column" 

which comes close to what Walter wants.  And maybe
you understand now why I choose this strange xml-syntax with 
my former patch :-) Actually i didn't have the idea of 
reusing 'exec' which makes a lot more sense to me, now.

again with regards and intersted in any remarks,

    holger

_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev

----- End forwarded message -----

-- 
"Why are people killing each other when there is so much fun stuff
 to be had through friendly cooperation?" (Bengt Richter on c.l.py)
"Why are people killing each other when there is so much fun stuff
 to be had through friendly cooperation?" (Bengt Richter on c.l.py)