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?
TIA!
kynn
Not exactly sure what you're trying to do.. but, regular arguments must
be used *before* positional and keyword arguments so the definition:
def wrapper(*p, foo=None, **kw):
is syntactically incorrect whereby the following is correct:
def wrapper(foo=None, *p, **kw):
But if what you are wanting is to actually add 'foo' to kw then I would
do this:
def my_decorator(f):
# blah, blah
def wrapper(*p, **kw):
if 'foo' not in kw:
kw['foo'] = None
x = f(*p, **kw)
if kw['foo']:
# blah blah
>On Mon, 2009-08-03 at 19:59 +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.
>>
>Not exactly sure what you're trying to do..
Yeah, I wasn't too clear. I figured out how to do what I wanted
to do:
def my_decorator(f):
# blah, blah
def wrapper(*p, **kw):
foo = kw.pop('force', None)
x = f(*p, **kw)
if (foo):
# blah, blah
else
# blah blah
return wrapper
Now the definitions of the original functions do not include the
foo=None argument, but "actual" functions (i.e. the ones generated
by the decorator) all accept the optional foo parameter. The only
remaining problem is how to document this... I don't see how pydoc
could possibly figure this one out. I guess this is sufficient
argument to abandon this idea. Bummer.
kynn
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
(Copy and pasted from working code, but I make no guarantee that it will
have survived the process in working order!)
--
Steven
>from functools import wraps
Thanks for this. It's very useful. A lot of stuff for me to chew
on.
kynn