Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect is decorator has been applied to method or function?

Tags:

python

The goal is that I wan't to have one decorator that will work with both, function and instance methods, and I would like to retrieve within wrapping function the self object when decorator has been applied on method or the function object itself when applied to function.

Here's what I have found almost working, this is only the func I'm using to detect on what decorator has been applied:

def _is_method(func):
    for stack_frame in inspect.stack():
        # if the code_context of the stack frame starts with 'class' this
        # function is defined within a class and so a method.
        if inspect.getframeinfo(stack_frame[0]).code_context[0].strip().startswith('class'):
            return True
    return False

This does work for me, with one small exception, It throws exceptions when I run tests in parallel in multiple processes.

like image 711
canni Avatar asked Oct 11 '13 09:10

canni


2 Answers

Thanks to this SO answer: Using the same decorator (with arguments) with functions and methods

I came to this solution witch works for me flawlessly:

def proofOfConcept():
    def wrapper(func):

        class MethodDecoratorAdapter(object):
            def __init__(self, func):
                self.func = func
                self.is_method = False

            def __get__(self, instance, owner):
                if not self.is_method:
                    self.is_method = True
                self.instance = instance

                return self

            def __call__(self, *args, **kwargs):
                # Decorator real logic goes here
                if self.is_method:
                    return self.func(self.instance, *args, **kwargs)
                else:
                    return self.func(*args, **kwargs)

        return wraps(func)(MethodDecoratorAdapter(func))

    return wrapper

NOTE This is not thread safe, to have a thread safe method one must return a callable object from __get__ that will have scope tied to instance

like image 113
canni Avatar answered Sep 29 '22 12:09

canni


You can use inspect.getargspec:

import inspect

def _is_method(func):
    spec = inspect.getargspec(func)
    return spec.args and spec.args[0] == 'self'

Example usage:

>>> def dummy_deco(f):
...     print('{} is method? {}'.format(f.__name__, _is_method(f)))
...     return f
... 
>>> @dummy_deco
... def add(a, b):
...     return a + b
... 
add is method? False
>>> class A:
...     @dummy_deco
...     def meth(self, a, b):
...         return a + b
... 
meth is method? True

NOTE This code depend on the name of the first argument. If the name is not self it will treat it as non-instance-method even though it is.

like image 33
falsetru Avatar answered Sep 29 '22 13:09

falsetru