如何获取装饰器包装的函数的源代码? [英] How to get source code of function that is wrapped by a decorator?

查看:69
本文介绍了如何获取装饰器包装的函数的源代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想打印 my_func 的源代码,该源代码由 my_decorator 包装:

I wanted to print the source code for my_func, that is wrapped by my_decorator:

import inspect
from functools import wraps

def my_decorator(some_function):
    @wraps(some_function)
    def wrapper():
        some_function()

    return wrapper

@my_decorator
def my_func():
    print "supposed to return this instead!"
    return

print inspect.getsource(my_func)

但是,而是返回包装器的源代码:

However, it returns source for wrapper instead:

@wraps(some_function)
def wrapper():
    some_function()

有没有办法打印以下内容?

Is there a way for it to print the following instead?

def my_func():
    print "supposed to return this instead!"
    return

请注意,以上内容是从较大的程序中抽象出来的。当然,在这个示例中我们可以摆脱装饰器,但这不是我想要的。

Note that the above is abstracted from a larger program. Of course we can just get rid of the decorator in this example, but that's not what I am looking for.

推荐答案

在Python 2中, @ functools.wraps()装饰器没有设置__wrapped __ 属性= noreferrer> Python 3版本添加了(Python 3.2中的新功能)。

In Python 2, the @functools.wraps() decorator does not set the convenience __wrapped__ attribute that the Python 3 version adds (new in Python 3.2).

这意味着您必须求助于提取原始函数从结束处开始。确切的位置将取决于确切的装饰器实现,但是选择第一个函数对象应该是一个很好的概括:

This means you'll have to resort to extracting the original function from the closure. Exactly at what location will depend on the exact decorator implementation, but picking the first function object should be a good generalisation:

from types import FunctionType

def extract_wrapped(decorated):
    closure = (c.cell_contents for c in decorated.__closure__)
    return next((c for c in closure if isinstance(c, FunctionType)), None)

用法:

print inspect.getsource(extract_wrapped(my_func))

使用示例进行演示:

>>> print inspect.getsource(extract_wrapped(my_func))
@my_decorator
def my_func():
    print "supposed to return this instead!"
    return

另一种选择是更新 functools 库为您添加 __ wrapped __ 属性,就像Python 3一样:

Another option is to update the functools library to add a __wrapped__ attribute for you, the same way Python 3 does:

import functools

def add_wrapped(uw):
    @functools.wraps(uw)
    def update_wrapper(wrapper, wrapped, **kwargs):
        wrapper = uw(wrapper, wrapped, **kwargs)
        wrapper.__wrapped__ = wrapped
        return wrapper

functools.update_wrapper = add_wrapped(functools.update_wrapper)

在导入要看到受影响的装饰器之前,先运行该代码 最终使用新版本的 functools.update_wrapper())。
您必须手动解开包装(Python 2 inspect 模块不会寻找该属性);这是一个简单的辅助函数:

Run that code before importing the decorator you want to see affected (so they end up using the new version of functools.update_wrapper()). You'll have to manually unwrap still (the Python 2 inspect module doesn't go looking for the attribute); here's a simple helper function do that:

def unwrap(func):
    while hasattr(func, '__wrapped__'):
        func = func.__wrapped__
    return func

这将解开任何内容装饰包装的水平。或使用 inspect.unwrap()实现,其中包括检查意外的循环引用。

This will unwrap any level of decorator wrapping. Or use a copy of the inspect.unwrap() implementation from Python 3, which includes checking for accidental circular references.

这篇关于如何获取装饰器包装的函数的源代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆