Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python inspect.getmembers does not return the actual function when used with decorators

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?

like image 467
Rajat Avatar asked Feb 10 '12 02:02

Rajat


2 Answers

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.

like image 197
unutbu Avatar answered Oct 14 '22 09:10

unutbu


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'
like image 27
tom stratton Avatar answered Oct 14 '22 11:10

tom stratton