[Python-Dev] Re: Extended Function syntax
holger krekel
pyth@devel.trillke.net
Sun, 2 Feb 2003 05:37:28 +0100
Guido van Rossum wrote:
> I received this from Glyph. He brings up some interesting use cases
> for thunks. I guess it could be used for "on" style event handler
> declarations. Hmm, you could even craft your own case statement with
> his suggestion:
>
> switch(expr):
> case(val1):
> block1
> case(val2):
> block2
> default:
> block3
>
> This actually makes me worry -- I didn't plan thunks to be the answer
> to all problems. A new idea that could cause a paradigm landslide is
> not necessarily right.
That's a very interesting point. Now i wonder if the following
implementation report falls (partly) into this category or goes
otherwise off-road. Please bear with me if it does.
Recently, i implemented an experimental "execution protocol" for
Python-2.2.2. It centers around "indented code blocks" and
their execution.
First i started out to (try to) put indented blocks into a code
object and allowing to denote before-hand an object that gets
and executes it.
(Regarding syntax I will only mention that much for now:
i experimentally tried out quite a number from the syntactic
ideas that were brought up here recently).
Soon i felt that trying to turn an indented block into a code
object goes against the grain of the CPython implementation.
Even worse, I couldn't seriously do much with a code object
apart from executing (or skipping) it and dealing with exceptions.
It's especially hard to construct a nice enough namespace/scope
interaction between a code object and its exec-handler.
Also 'execing' code often destroys optimization options.
All not so nice.
So i went on and tried to settle on what i really wanted:
a) to allow an object to hook into certain "execution events"
and especially to encapsulate error-handling which today
is often "inlined": the to-be-handled code block
intermingles with the enclosing try-except construct.
b) to have easy ways of "exchanging parameters" between
a code block and its execution handler.
Without such interaction the handler and its code block
would be completly orthogonal to each other (which is
puristic but doesn't give you much in practise).
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
"""
Now how to do the b) "exchanging parameters" part? After some
experiments I choose to do it *oneway* resp. only in the direction
from code block to execution handler. The rationale is that
the execution handler should only *add* behaviour but not
modify "foreign" local namespaces. I think this is a
crucial point.
This code-to-handler communication is made explicit by
specifying a series of "name=expr" bindings which
(as usual) have effect in local or global scope but *additionally*
are set as attributes on the execution handler instance.
Thus for timely finalization you now only have to
1. specify objects to be finalized, e.g.: f1=open('...')
2. name a handler that implements __leave__ like this:
def __leave__(self):
for f in self.__dict__.values():
f.close()
For 'acquire/release' it's even simpler:
class autolock(threading.RLock):
__enter__ = threading.RLock.__acquire__
__leave__ = threading.RLock.__release__
This worked nicely but soon I also wanted a way
to pass *anonymous* values towards an execution
handler. I went the Quixotish way and added
this fourth and last hook to the execution handler:
def __catch__(self, arg):
"""catches a dropped value ala Quixote.
"dropped" means that an expression's result
of the code block would otherwise be discarded.
"""
To summarize, the execution protocol now has three semantic
aspects:
- execution hooks (enter, leave, except)
- local name bindings set as instance attributes
- drop-catch pattern for anonymous object flow
from code block to handler
Now let's assume that i realized this with a XML-like syntax
(which i actually did but don't see as central), then
the above requirements roughly map to
'<' expr (NAME '=' expr)* '>' suite
The evaluated 'expr' is the execution handler and 'suite'
is the code block. It's hopefully evident now how
the following example usages work:
<autoclose() f1=open('/etc/passwd')>
data = f1.read()
# here f1 will be closed no matter what
def some_class_method(self, ...):
...
<self.lock1>
# in critical part here
<debug> u"hello world" # debug object catches dropped object
<async_remote> # handles the remote call including errors
remote.call(...)
<shell> "ls -la"
if shell.errno:
...
I could imagine that you find this mixture of XML syntax
and python horrible. FWIW i found that this "markup" in
a way resembles the notion of 'adding characteristics and meaning'
to a code block much like 'html' tags do for text.
However, it's certainly better to focus on semantic ideas
and problems first before discussing syntactic details.
In the hope of not just having wasted your time,
holger