Bogdan
Bogdan

Reputation: 105

Python 2.7: check if default argument passed with a decorator

I am looking for something i'm not sure how to accompish and can't find an answer.

Lets say I have a function with unknown number of arguments except arg2. Also arg2 doesn't have a fixed argument location:

def some_func(arg1, arg2=None, maybe_arg3=None, maybe_not_arg4=None):
    # Do some stuff

Now I want to check if arg2 has been passed by the user and replace it from a decorator, eg:

def some_dec(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # Do some stuff
        # check if arg2 is passed and replace
        rv = func(*args, **kwargs)
        return rv
    return wrapper

If it's passed as key/value then it's no problem to get it from kwargs BUT how do I get it if it's passed to args? (Since IDK the number of arguments the function requires)

Upvotes: 2

Views: 300

Answers (2)

Paul Panzer
Paul Panzer

Reputation: 53029

inspect.signature should give you what you want:

def f(x, y, z, a=4):
    pass

import inspect
s = inspect.signature(f)

b = s.bind_partial(2, 4, 6, 8)
'a' in b.arguments

-> True

b = s.bind_partial(2, 4, 6)
'a' in b.arguments

-> False

Note that this was introduced at Python version 3.3. On older versions inspect.getargspec provides some of the same functionality.

So your decorator could look like:

import inspect
import functools

def nag_a(func):
    sig = inspect.signature(func)
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if not 'a' in sig.bind_partial(*args, **kwargs).arguments:
            print("You really should pass 'a', y'know.")
        rv = func(*args, **kwargs)
        return rv
    return wrapper

def nag_a_27(func):
    spec = inspect.getargspec(func)
    psn = spec.args.index('a')
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if not 'a' in kwargs and len(args) <= psn:
            print "You really should pass 'a', y'know."
        rv = func(*args, **kwargs)
        return rv
    return wrapper

@nag_a
def f(x, y, a=None):
    pass

Upvotes: 1

Moses Koledoye
Moses Koledoye

Reputation: 78546

You can first check the kwargs dict for the argument, if it's missing, it's likely going to be the second argument in args; check if args contains more than two arguments and :

def some_dec(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        if 'args2' in kwargs:
             kwargs['args2'] = 'new value' # update value
        elif len(args) > 1:
             args = (args[0], 'new value') + args[2:] # rebuild args with new value
        rv = func(*args, **kwargs)
        return rv
    return wrapper

Upvotes: 1

Related Questions