[Python-Dev] PEP 318 bake-off? (original) (raw)
Phillip J. Eby pje at telecommunity.com
Thu Apr 1 18🔞47 EST 2004
- Previous message: [Python-Dev] PEP 318 bake-off?
- Next message: [Python-Dev] Re: PEP 318 bake-off?
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
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.
- Previous message: [Python-Dev] PEP 318 bake-off?
- Next message: [Python-Dev] Re: PEP 318 bake-off?
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]