Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to allow parameters for a decorator while keep the function's signature at the same time?

I'm trying to write a decorator which will do some actions based on kwargs while I want to keep the functions signature at the same time.

Here is the unit test I want to pass:

def test_test_deprecator(self):

    @test_deprecator("here is decorator message", 'z'):
    def some_method(x, y, z):
        return x + y

    self.assertEqual(some_method(2, 2, z=6), 4)

    # test the original function's signature
    argspec = inspect.getargspec(some_method)
    self.assertEqual(argspec.args, ["x", "y", "z"])

I wrote something like:

import decorator

def test_decorator(message, args_name):

    @decorator.decorator
    def _test_docorator(f, *args, **kwargs):
        if args_name in kwargs:
            print(message)
        return f(*args, **kwargs)

    return _test_docorator

The unit test can pass, but there is no output message, because kwargs is always empty.

Does anyone know how to achieve this?

like image 894
Chen Li Avatar asked Jan 29 '26 17:01

Chen Li


1 Answers

I'm not sure what the role of @decorator.decorator is in your code, but here is a decorator doing what you describe:

def test_decorator(message, arg_name):
    def _test_decorator(f):
        def wrapped_f(*args, **kwargs):
            if arg_name in kwargs:
                print(message)
            return f(*args, **kwargs)
        return wrapped_f
    return _test_decorator

Updated answer:

I learned a bit about decorators looking into this. In short, though, I think using the wrapt module would work for you:

def test_decorator(message, arg_name):
    @wrapt.decorator
    def wrapped_f(f, instance, args, kwargs):
        if arg_name in kwargs:
            print(message)
        return f(*args, **kwargs)
    return wrapped_f

Test code:

@test_decorator('found it', 'y')
def foo(x="bad", y="dog"):
    print("x={}, y={}".format(x, y))

def bar(x="bad", y="dog"):
    print("x={}, y={}".format(x, y))

print ("foo argspec={}".format(inspect.getargspec(foo)))
print ("bar argspec={}".format(inspect.getargspec(bar)))

foo(y='cat')

Output:

foo argspec=ArgSpec(args=['x', 'y'], varargs=None, keywords=None, defaults=('bad', 'dog'))
bar argspec=ArgSpec(args=['x', 'y'], varargs=None, keywords=None, defaults=('bad', 'dog'))
found it
x=bad, y=cat
like image 153
Turn Avatar answered Jan 31 '26 06:01

Turn



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!