[Python-Dev] PEP 318 bake-off?
Phillip J. Eby
pje at telecommunity.com
Thu Apr 1 18:18:47 EST 2004
At 11:08 AM 4/1/04 -0800, Guido van Rossum wrote:
> > >What I'm asking (especially of Phillip) is to collect a set of
> > >realistic method declarations using decorators; we can then
> > >collectively format these using any of the possible syntaxes, and see
> > >how they look.
> >
> > I'd be happy to scrounge up some samples from existing code using
> > 'property' and 'classmethod' as well as some of PEAK's decorators, and I
> > definitely think that Jack Diedrich and Bob Ippolito's samples should be
> > included as well.
> >
> > Important question, though: do we include code bodies, or just use 'pass'
> > for the bodies? If we include the body, how much of the body? Should we
> > include entire classes, especially if the class itself needs a decorator,
> > and multiple methods have decorators?
>
>Why not provide the bodies, for added realism?
Okay, here's one using two decorators, in today's syntax. It's excerpted
from an I/O scheduling component in 'peak.events'. The component manages a
set of read/write/error file handles, and allows pseudothreads waiting on
those handles to resume when I/O is possible. The 'select()' operation is
itself performed by a pseudothread called 'monitor'. Each instance of the
component should have exactly one such pseudothread, which should begin
running as soon as the component is "assembled" (attached to an application).
Two decorators control this: 'events.taskFactory', which accepts a
generator function and returns a function that returns a new pseudothread
each time it's invoked. That is, it's roughly equivalent to:
def taskFactory(func):
return lambda *args,**kw: Task(func(*args,**kw))
except that there is some extra magic so that introspecting the returned
function still shows the same argument signature. (Which is important for
documentation tools like pydoc and epydoc).
The second decorator is 'binding.Make', which takes a 1-argument callable
and returns a descriptor that will invoke the callable only once: when the
attribute is first accessed for a given instance. The result of the
callable is cached in the object's instance dictionary, where it will be
retrieved on any subsequent access.
So, applying the two decorators (i.e. [events.taskFactory, binding.Make])
to a 1-argument function results in an attribute that will be automatically
initialized when first used. By applying an extra keyword argument to
'binding.Make' in the current implementation, we can tell the descriptor to
automatically initialize itself when the componet is assembled. (Note:
this is not the same as __init__ time; a PEAK component is considered
"assembled" when it is made the child of a component that knows its "root"
component, and thus can be certain of its entire configuration environment.)
So, I would probably render this example with these decorators:
[events.taskFactory, binding.Make(uponAssembly=True)]
in order to specify that the function is a generator that should be run as
a task (pseudothread), it should be run at most once, and it should exist
as soon as the component knows its configuration environment. (Note: the
'uponAssembly' bit works today only with old-style
'foo=binding.Make(foo,...)' syntax, not decorator syntax.) Anyway, here's
the example:
def monitor(self) [events.taskFactory, binding.Make(uponAssembly=True)]:
r,w,e = self.rwe
count = self.count
sleep = self.scheduler.sleep()
time_available = self.scheduler.time_available
select = self.select
error = self._error
while True:
yield count; resume() # wait until there are selectables
yield sleep; resume() # ensure we are in top-level loop
delay = time_available()
if delay is None:
delay = self.checkInterval
try:
rwe = self.select(r.keys(),w.keys(),e.keys(),delay)
except error, v:
if v.args[0]==EINTR:
continue # signal received during select, try again
else:
raise
for fired,events in zip(rwe,self.rwe):
for stream in fired:
events[stream]().send(True)
>(I still think class decorators are a separate case, and much weaker
>-- you can do this by having a single 'decoratable' metaclass and
>setting __decorators__ = [...] in the class body.)
Or you can use the "class advisors" mechanism I implemented for PyProtocols
and Zope 3, which is clean and convenient in today's syntax. Its only
pitfall is that you absolutely *must* specify __metaclass__ first if you
are specifying one, or your class advisors won't work right. Actually, the
other pitfall for anybody taking this approach is that a correct
implementation of class advisors is *hard*, because it depends on a correct
re-implementation of much of Python's metaclass validation logic. But, at
least there is one working implementation available to steal from. :)
>I think that SPARK syntax and everything else that people have
>traditionally added to docstring markup that isn't strictly speaking
>documentation (even some extreme cases of doctest usage) ought to be
>considered as candidates for attribute-ification.
David Goodger mentioned docutils, so I mocked up a couple of
'rst_directive' examples in a seperate message.
More information about the Python-Dev
mailing list