In <pan.2009.08.04.04.06...@remove.this.cybersource.com.au> Steven D'Aprano <ste...@remove.this.cybersource.com.au> writes:
>On Mon, 03 Aug 2009 19:59:23 +0000, kj wrote: >> I want to write a decorator that, among other things, returns a function >> that has one additional keyword parameter, say foo=None. >> >> When I try >> >> def my_decorator(f): >> # blah, blah >> def wrapper(*p, foo=None, **kw): >> x = f(*p, **kw) >> if (foo): >> # blah, blah >> else >> # blah blah >> return wrapper >> >> ...i get a syntax error where it says "foo=None". I get similar errors >> with everything else I've tried. >> >> Is is possible to do this in Python? >Have you tried this under Python 2.6 or 3.0? >I've run into similar issues, because you can't have keyword-only >arguments in Python 2.5 :( >My solution was to create a decorator that faked them. The docstring is >longer than the decorator itself. >from functools import wraps >def keywords(**defaults): > """Return a decorator which decorates a function to accept keyword > arguments. > Python 2.5 and earlier don't allow keyword-only arguments for > non-builtin functions. The obvious syntax: > def func(x, *args, key=None, word='parrot'): > is not permitted. As a work-around, write your function something > like the following example: > >>> @keywords(key=None, word='parrot') > ... def func(x, y=0, *args, **kwargs): > ... # Inside the function, we can guarantee that kwargs['key'] and > ... # kwargs['word'] both exist, and no other keys. > ... print "x=%s, y=%s, args=%s" % (x, y, args) > ... if kwargs['key'] is None: msg = "kwargs['key'] is None" > ... else: msg = "kwargs['key'] is something else" > ... msg += " and kwargs['word'] is %r" % kwargs['word'] > ... print msg > ... > When you call func, if you don't provide a keyword-only argument, the > default will be substituted: > >>> func(1, 2, 3, 4) > x=1, y=2, args=(3, 4) > kwargs['key'] is None and kwargs['word'] is 'parrot' > >>> func(1) > x=1, y=0, args=() > kwargs['key'] is None and kwargs['word'] is 'parrot' > Naturally you can provide your own values for keyword-only arguments: > >>> func(1, 2, 3, word='spam') > x=1, y=2, args=(3,) > kwargs['key'] is None and kwargs['word'] is 'spam' > >>> func(1, 2, 3, word='spam', key=len) > x=1, y=2, args=(3,) > kwargs['key'] is something else and kwargs['word'] is 'spam' > If you pass an unexpected keyword argument, TypeError is raised: > >>> #doctest:+IGNORE_EXCEPTION_DETAIL > ... func(1, 2, 3, 4, keyword='something') > Traceback (most recent call last): > ... > TypeError: ... > """ > def decorator(func): > """Decorate func to allow keyword-only arguments.""" > @wraps(func) > def f(*args, **kwargs): > for key in kwargs: > if key not in defaults: > raise TypeError( > "'%s' is an invalid keyword argument for " \ > "this function" % key > ) > d = defaults.copy() > d.update(kwargs) > return func(*args, **d) > return f > return decorator Thanks for this. It's very useful. A lot of stuff for me to chew on. kynn -- http://mail.python.org/mailman/listinfo/python-list