5

I have three python functions:

def decorator_function(func)
  def wrapper(..)
    return func(*args, **kwargs)
  return wrapper

def plain_func(...)

@decorator_func
def wrapped_func(....)

inside a module A.

Now I want to get all the functions inside this module A, for which I do:

for fname, func in inspect.getmembers(A, inspect.isfunction):
  # My code

The problem here is that the value of func is not what I want it to be.

It would be decorator_function, plain_func and wrapper (instead of wrapped_func).

How can I make sure that wrapped_func is returned instead of wrapper?

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
Rajat
  • 1,766
  • 2
  • 21
  • 42

2 Answers2

6

You can access the pre-decorated function with:

wrapped_func.func_closure[0].cell_contents()

For example,

def decorator_function(func):
  def wrapper(*args, **kwargs):
      print('Bar')
      return func(*args, **kwargs)
  return wrapper   

@decorator_function
def wrapped_func():
    print('Foo')

wrapped_func.func_closure[0].cell_contents()

prints

Foo    # Note, `Bar` was not also printed

But really, if you know you want to access the pre-decorated function, then it would be a whole lot cleaner to define

def wrapped_func():
    print('Foo')

deco_wrapped_func = decorator_function(wrapped_func)

So wrapped_func will be the pre-decorated function, and deco_wrapped_func will be the decorated version.

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • Thanks, unutbu. I have more more question. Can I get the function instance, rather than calling the function? I want to get the function instance so that I use func.__name__ or func.__module__ etc. – Rajat Feb 10 '12 at 03:38
  • Yeah, the thing is I am writing this code on some other modules which I cannot touch. The code that I am writing already contains the decorators embedded. – Rajat Feb 10 '12 at 03:42
  • @Rajat of course you can. As with any other python function, leaving off the `()` does not call the function. – lvc Feb 10 '12 at 03:42
  • Its also worth pointing out that `func_closure` has been renamed in Python 3 - its now spelled `wrapped_func.__closure__[0].cell_contents` – lvc Feb 10 '12 at 03:45
  • Looks like there are more problems than I thought!If my decorator function has arguments with it, then it is impossible to know which index of func_closure[i] is my function name. Any ideas on that? – Rajat Feb 10 '12 at 05:08
  • So my exact question is that if a decorator function contains multiple arguments, then what is the index at which my wrapped function will appear? In most cases, I have seen that it is at the last index, but in some cases, it is also at the first index! – Rajat Feb 10 '12 at 05:30
  • I see you've found a better way; good! But to answer your question, I think you would just have to iterate over the contents and check `isinstance(obj.cell_contents,types.FunctionType)` for each obj in `wrapped_func.func_closure`. – unutbu Feb 10 '12 at 11:30
5

If all you want is to keep the original function name visible from "the outside" I think that you can do that with

@functools.wraps

as a decorator to your decorator

here's the example from the standard docs

>>> from functools import wraps
>>> def my_decorator(f):
...     @wraps(f)
...     def wrapper(*args, **kwds):
...         print('Calling decorated function')
...         return f(*args, **kwds)
...     return wrapper
...
>>> @my_decorator
... def example():
...     """Docstring"""
...     print('Called example function')
...
>>> example()
Calling decorated function
Called example function
>>> example.__name__
'example'
>>> example.__doc__
'Docstring'
tom stratton
  • 668
  • 7
  • 13