3

as an example:

def decorator(func): 
    def nested(*args, **kwargs):
        return func(*args, **kwargs)
    return nested

@decorator
def decorated(): pass

is there a way for decorated to know it is getting decorated?

Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
  • 3
    Why does the function need to know that it's being decorated? What are you trying to achieve? – Aran-Fey Oct 23 '18 at 05:13
  • 1
    It could detect some wrappers (specifically, function-style wrappers that use `functools.wraps()` by telltale attributes they add to the wrapped function, especially in Python 3). But a decorator doesn't need to add a wrapper, and if it does, the wrapper doesn't need to be a function, and if it is, it might not use `functools.wraps()`. – kindall Oct 23 '18 at 05:15
  • In short, I have run into an issue wherein I take a look at the functions argument list. When a I come a cross a valid function lets say, decorating said function can cause problems. I should clarify it is a use case for a library I am developing that I am worried about. – Most_Arduous_Journey Oct 23 '18 at 05:19
  • Then why don't you add error checking in the decorator? Why does the function have to know it's being decorated? – Aran-Fey Oct 23 '18 at 05:21
  • Because I am not developing the decorator. Already explained why it would need to know about being decorated, refer to the above comment. – Most_Arduous_Journey Oct 23 '18 at 05:22
  • If you're not developing the decorator, why do you care if it works or not? It's not your responsibility to fix it. – Aran-Fey Oct 23 '18 at 05:45

2 Answers2

2

You can use a decorator that uses ast.NodeVistor to traverse through the AST nodes of the function to look for decorators of the function. If the decorator list includes more than the decorator checker itself, you can then get the details of the other decorators from the decorator nodes:

import inspect
import ast
from textwrap import dedent

class CheckDecorators(ast.NodeVisitor):
    def visit_FunctionDef(self, node):
        if len(node.decorator_list) > 1:
            print("function '%s' is decorated by: %s" % (node.name, ', '.join(ast.dump(decorator) for decorator in node.decorator_list if not isinstance(decorator, ast.Name) or decorator.id != 'check_decorators')))

def check_decorators(func):
    CheckDecorators().visit(ast.parse(dedent(inspect.getsource(func))))
    return func

so that:

def decorator(func):
    def nested(*args, **kwargs):
        return func(*args, **kwargs)
    return nested

@decorator
@check_decorators
def decorated():
    pass

would output:

function 'decorated' is decorated by: Name(id='decorator', ctx=Load())
blhsing
  • 91,368
  • 6
  • 71
  • 106
1

Following is a partial solution that works on closures that look exactly like your example.

import inspect

def decorator(func):
    def nested(*args, **kwargs):
        return func(*args, **kwargs)

    return nested

@decorator
def decorated(): pass

def not_decorated(): pass

print(inspect.getclosurevars(decorated).nonlocals)
print(inspect.getclosurevars(not_decorated).nonlocals)

# => {'func': <function decorated at 0x10e1408c8>}
# => {}

Decorated functions like yours will have closure variables, though there is no guarantee that others won't.

Also, there are other things in inspect that you can play with. Plus, it would be easy if functools.wrap is used in the first place (@kindall). For class method, you can look up How to detect is decorator has been applied to method or function?

Blownhither Ma
  • 1,461
  • 8
  • 18