Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

kw param question

0 views
Skip to first unread message

kj

unread,
Aug 3, 2009, 3:59:23 PM8/3/09
to

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

Albert Hopkins

unread,
Aug 3, 2009, 4:27:34 PM8/3/09
to pytho...@python.org
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.. 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

kj

unread,
Aug 3, 2009, 4:58:46 PM8/3/09
to

>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

Steven D'Aprano

unread,
Aug 4, 2009, 12:06:15 AM8/4/09
to

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

kj

unread,
Aug 4, 2009, 12:07:34 PM8/4/09
to


>from functools import wraps


Thanks for this. It's very useful. A lot of stuff for me to chew
on.

kynn

0 new messages