[Python-Dev] 2.4a2, and @decorators
Phillip J. Eby
pje at telecommunity.com
Mon Aug 2 21:49:41 CEST 2004
At 12:09 PM 8/2/04 -0700, Guido van Rossum wrote:
> > >>I would think the fact that the '[decorators]' syntax can be implemented
> > >>in pure Python (no changes to the interpreter) for existing Python
> > >>versions would give more weight to it.
>
>Can it? I must've missed that. It sure sounds like an incredible
>hack -- how to you prevent the default behavior that the list of
>decorators is thrown away by the interpreter?
By using sys.settrace (and a careful tracer implementation to avoid
interfering with debuggers or other active tracers):
def as(*decorators):
if len(decorators)>1:
decorators = list(decorators)
decorators.reverse()
def callback(frame,k,v):
for d in decorators:
v = d(v)
frame.f_locals[k] = v
add_assignment_advisor(callback)
def add_assignment_advisor(callback,depth=2):
"""Invoke 'callback(frame,name,value)' on the next assignment in 'frame'
The frame monitored is determined by the 'depth' argument, which gets
passed to 'sys._getframe()'. Note that when 'callback' is invoked, the
frame state will be as though the assignment hasn't happened yet, so any
previous value of the assigned variable will be available in the frame's
locals. (Unless there's no previous value, in which case there will be
no such variable in the frame locals.)
"""
frame = sys._getframe(depth)
oldtrace = [frame.f_trace]
old_locals = frame.f_locals.copy()
def tracer(frm,event,arg):
if event=='call':
# We don't want to trace into any calls
if oldtrace[0]:
# ...but give the previous tracer a chance to, if it wants
return oldtrace[0](frm,event,arg)
else:
return None
try:
if frm is frame and event !='exception':
# Aha, time to check for an assignment...
for k,v in frm.f_locals.items():
if k not in old_locals:
del frm.f_locals[k]
break
elif old_locals[k] is not v:
frm.f_locals[k] = old_locals[k]
break
else:
# No luck, keep tracing
return tracer
# Got it, fire the callback, then get the heck outta here...
callback(frm,k,v)
finally:
# Give the previous tracer a chance to run before we return
if oldtrace[0]:
# And allow it to replace our idea of the "previous" tracer
oldtrace[0] = oldtrace[0](frm,event,arg)
# Unlink ourselves from the trace chain.
frm.f_trace = oldtrace[0]
sys.settrace(oldtrace[0])
return oldtrace[0]
# Install the trace function
frame.f_trace = tracer
sys.settrace(tracer)
class X(object):
[as(classmethod)]
def foo(cls):
....
Of course, 'as' is a hack to let you use arbitrary callables as decorators,
rather than having to use add_assignment_advisor directly.
> > >>That is, if someone wants to implement a decorator that's forwards
> > >>and backwards-compatible, that's possible with the list syntax,
> > >>but not the @ syntax.
> > >
> > >.. but that also means you can still make the [decorators] syntax
> > >work in 2.4, if you want compatibility or don't like @syntax.
> >
> > But then why not just make that the default syntax, so that no
> > migration is necessary, and only one syntax has to be
> > learned/explained to people?
>
>Because that syntax received significant boohs when I presented it at
>EuroPython.
And "@" *didn't*??? Ah well. C'est la vie.
More information about the Python-Dev
mailing list