473,396 Members | 2,013 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

self-promotion of the decorator module (Was: How to learn OO of python?)

could ildg wrote:
I think decorator is a function which return a function, is this right? e.g. The decorator below if from http://www.python.org/peps/pep-0318.html#id1.
def accepts(*types):
def check_accepts(f):
assert len(types) == f.func_code.co_argcount
def new_f(*args, **kwds):
for (a, t) in zip(args, types):
assert isinstance(a, t), \
"arg %r does not match %s" % (a,t)
return f(*args, **kwds)
new_f.func_name = f.func_name
return new_f
return check_accepts

After I saw all the examples, I concluded that every decorator must
define an inner function which takes only one argument, the
function to decorate. Is this right?


Yes, but you may want to look at my decorator module:

http://www.phyast.pitt.edu/~micheles.../decorator.zip

Your example would be written as follows:

from decorator import decorator

def accepts(*types):
def check_accepts(f, *args, **kw):
assert len(types) == f.func_code.co_argcount
for a, t in zip(args, types):
assert isinstance(a, t), "arg %r does not match %s" % (a,t)
return f(*args, **kw)
return decorator(check_accepts)

with the following advantages:

1. one-level of nesting is saved ("flat is better than nested")
2. name, docstring and dictionary of the original function are
preserved;
3. the signature of the original function is preserved (this one is
nontrivial).
Michele Simionato

Jul 19 '05 #1
6 1372
Michele Simionato wrote:
with the following advantages:

1. one-level of nesting is saved ("flat is better than nested")
2. name, docstring and dictionary of the original function are
preserved;
3. the signature of the original function is preserved (this one is
nontrivial).


And at least the following bugs:
from decorator import *
@decorator def chatty(f, *args, **kw):
print "Calling %r" % f.__name__
return f(*args, **kw)
@chatty def f((x,y)): pass
Traceback (most recent call last):
File "<pyshell#11>", line 1, in -toplevel-
@chatty
File "C:\Work\decorator\decorator.py", line 119, in __call__
return _decorate(func, self.caller)
File "C:\Work\decorator\decorator.py", line 89, in _decorate
dec_func = eval(lambda_src, evaldict)
File "<string>", line 1
lambda ['x', 'y']: call(func, ['x', 'y'])
^
SyntaxError: invalid syntax
I would have thought the code would be simpler if it used
inspect.formatargspec to produce an argument list which is correctly
formatted for all the weird corner cases:
class Default: def __init__(self, n):
self.n = n
def __repr__(self):
return 'defarg[%d]' % self.n

def f(x, y=1, z=2, *args, **kw): pass a,v,vk,d = inspect.getargspec(f)
inspect.formatargspec(a,v,vk,map(Default, range(len(d)))) '(x, y=defarg[0], z=defarg[1], *args, **kw)' def g(x, (y,z)=(1,2), *args, **kw): pass a,v,vk,d = inspect.getargspec(g)
inspect.formatargspec(a,v,vk,map(Default, range(len(d)))) '(x, (y, z)=defarg[0], *args, **kw)' d ((1, 2),)
Even if we manage to decorate the function, the decorated object has the
wrong name in tracebacks:
@chatty def f(): pass
f(3)
Traceback (most recent call last):
File "<pyshell#15>", line 1, in -toplevel-
f(3)
TypeError: <lambda>() takes no arguments (1 given)


Using a lambda function here seems needlessly perverse. You know the name
of the function you want to define and you are going to eval a string, so
why not exec the string instead

"def %(name)s %(fullsign)s: call(func, %(shortsign)s)" % getinfo(func)

and then just pick the function out of the namespace after the exec?

e.g.

def _decorate(func, caller):
"""Takes a function and a caller and returns the function
decorated with that caller. The decorated function is obtained
by evaluating a lambda function with the correct signature.
"""
lambda_src = "def %(name)s(%(fullsign)s): call(func, %(shortsign)s)" %
getinfo(func)
# print lambda_src # for debugging
execdict = dict(func=func, call=caller, defarg=func.func_defaults or
())
exec lambda_src in execdict
dec_func = execdict.get(func.__name__)
dec_func.__doc__ = func.__doc__
dec_func.__dict__ = func.__dict__
return dec_func

There is still a problem with this one though. If the function has
arguments named 'call', 'func', or 'defarg' (or for that matter my
code above introduces a new problem if the function is called by one
of those names) nasty things will happen. Fortunately you have a list of
argument names readily available so it shouldn't be too hard to generate
unique names to use in their place.
Jul 19 '05 #2
Notice that the decorator module is at version 0.4 and I still consider
it as experimental. The reason why I posted the link here is to get
feedback and it seems I got it ;)

I will upgrade to version 0.5 shortly, fixing the easily fixable bugs
and
documenting what cannot be easily fixed.

Thanks for testing border cases I would have never imagined!

Michele Simionato

Jul 19 '05 #3
Duncan Booth:
Fortunately you have a list of
argument names readily available so it shouldn't be too
hard to generate unique names to use in their place.


It does not look too simple either. Whereas I can easily
build valid Python identifiers which are extremely unlikely
to clash with any identifier I can have in my program, it
seems impossible to get an identifier which cannot be
shadowed later . What about renaming
name-> _name_, call -> _call_, func-> _func_, defarg->_defarg_ and
checking that the argument list
does not contain such reserved names by means of an assert statement?

Michele Simionato

Jul 19 '05 #4
I have uploaded version 0.5 that should fix all the
subtle bugs you pointed out, but there could be others
(I would not be surprised in that case ;).
I will add a test suite for various corner cases tomorrow.

BTW, I cannot decide if exotic function signatures are
a feature or an accident ...
Michele Simionato

Jul 19 '05 #5
Michele Simionato wrote:
Duncan Booth:
Fortunately you have a list of
argument names readily available so it shouldn't be too
hard to generate unique names to use in their place.


It does not look too simple either. Whereas I can easily
build valid Python identifiers which are extremely unlikely
to clash with any identifier I can have in my program, it
seems impossible to get an identifier which cannot be
shadowed later . What about renaming
name-> _name_, call -> _call_, func-> _func_, defarg->_defarg_ and
checking that the argument list
does not contain such reserved names by means of an assert statement?


I'm not sure I see the problem. You only have to pick names which are not
the name of the current function not any of its arguments. These names get
substituted into the string used to define the function which is exec'ed in
its own private namespace. So long as they are unique within that namespace
there shouldn't be a problem.
Jul 19 '05 #6
Yep, I was wondering about irrelevant things, there is no problem in
this case,
actually.

Michele Simionato

Jul 19 '05 #7

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: Jeff Epler | last post by:
Hello. Recently, Generator Comprehensions were mentioned again on python-list. I have written an implementation for the compiler module. To try it out, however, you must be able to rebuild...
1
by: --CELKO-- | last post by:
This is a shameless plug. My new book, SQL PROGRAMMING STYLE is out now. Joe Celko's SQL Programming Style by Joe Celko Save 20% and get Free Shipping if you order by Friday May 6. Place...
2
by: --CELKO-- | last post by:
The third edition of SQL FOR SMARTIES was just released. ...
20
by: Wayne Sutton | last post by:
OK, I'm a newbie... I'm trying to learn Python & have had fun with it so far. But I'm having trouble following the many code examples with the object "self." Can someone explain this usage in...
0
by: --CELKO-- | last post by:
This is a shameless plug. My new book, SQL PROGRAMMING STYLE is out now. Joe Celko's SQL Programming Style by Joe Celko Save 20% and get Free Shipping if you order by Friday May 6. Place...
0
by: --CELKO-- | last post by:
The third edition of SQL FOR SMARTIES was just released. ...
122
by: Edward Diener No Spam | last post by:
The definition of a component model I use below is a class which allows properties, methods, and events in a structured way which can be recognized, usually through some form of introspection...
2
by: chrisber | last post by:
using the unittest module in Python 2.3.5, I've written some test code that ends up with if __name__ == "__main__": unittest.main() Since I want to run this code in various environments, I'd...
4
by: thomas.karolski | last post by:
Hi, I would like to create a Decorator metaclass, which automatically turns a class which inherits from the "Decorator" type into a decorator. A decorator in this case, is simply a class which...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.