1

I would like to add an attribute called tag to the BoundFunctionWrapper of a wrapt decorated method in order to be able to inspect my instances for methods that have a certain tag value. As I use this decorator on multiple classes, this will allow me to find and execute all methods with a certain tag.

import wrapt
import inspect

def tag_method(tag_message:str):
    @wrapt.decorator
    def wrapper(wrapped, instance, args, kwargs):
        #  do some other wrapper stuff besides just tagging
        return wrapped(*args, **kwargs)
    setattr(wrapper, 'tag', tag_message)
    return wrapper


# Test Code
if __name__ == '__main__':
    class MyClass:
        def __init__(self) -> None:
            pass

        @tag_method('value_method')
        def return_value(self, value:int) -> int:
            return value

    instance = MyClass()
    print(instance.return_value(5))
    print(inspect.getmembers(instance, predicate=inspect.ismethod))
    print(inspect.getmembers(instance, predicate=inspect.ismethod)[1])
    print(inspect.getmembers(instance, predicate=inspect.ismethod)[1][1])  # why does this suddenly evaluate to the bound method instead of the BoundFunctionWrapper?
    print(inspect.getmembers(instance, predicate=inspect.ismethod)[1][1].tag)  # want to print the assigned tag 'value_method'

Is this possible? If so what am I missing in my decorator implementation?

Or is there a better way to implement this functionality dynamically? I prefer dynamically because statically labeling things in a dict or something could lead to user error.

  • Can I ask why you're using @wrapt.decorator for this (as in, why can't you accomplish what you're trying to do with a "regular" decorator wrapper)? Also, if you can accept my answer I'd appreciate it!! – Ori Yarden PhD Apr 22 '23 at 18:13

1 Answers1

0

We have to setattribute within the wrapt.decorator decorated wrapper on the instance; and we can get the __name__ of the wrapped method via __getattribute__ to avoid hard-coding return_value's or any other __func__tion's name:

def tag_method(tag_message:str):
    print('tag_method')
    @wrapt.decorator
    def wrapper(wrapped, instance, args, kwargs):
        #  do some other wrapper stuff besides just tagging
        print('wrapper')
        setattr(instance.__getattribute__(wrapped.__name__).__func__, 'tag', tag_message)
        return wrapped(*args, **kwargs)
    return wrapper

Outputs:

tag_method
wrapper
5
[('__init__', <bound method MyClass.__init__ of <__main__.MyClass object at 0x7fa0aaf80f70>>), ('return_value', <BoundFunctionWrapper at 0x7fa0aaf17160 for method at 0x7fa0aaf250c0>)]
('return_value', <BoundFunctionWrapper at 0x7fa0aaf17160 for method at 0x7fa0aaf250c0>)
<bound method MyClass.return_value of <__main__.MyClass object at 0x7fa0aaf80f70>>
value_method
Ori Yarden PhD
  • 1,287
  • 1
  • 4
  • 8