如何处理“ inspect.getsource”的局限性?或者如何仅获取函数的源?

时间:2019-03-11 17:19:24

标签: python python-3.x inspect

我一直在使用Python标准库中的inspect模块。

以下示例工作正常(假设已导入inspect):

def foo(x, y):
    return x - y
print(inspect.getsource(foo))

...将打印def foo(x, y):\n return x - y\n和...

bar = lambda x, y: x / y
print(inspect.getsource(bar))

...将打印bar = lambda x, y: x / y\n。到目前为止,一切都很好。但是,在以下示例中,事情变得有些奇怪了:

print(inspect.getsource(lambda x, y: x / y))

...将打印print(inspect.getsource(lambda x, y: x / y))和...

baz = [2, 3, lambda x, y: x / y, 5]
print(inspect.getsource(baz[2]))

...将打印baz = [2, 3, lambda x, y: x / y, 5]

模式似乎是getsource返回所有相关的源代码,而不管上下文如何。这些行中的所有其他内容(包括所需功能源/定义在内的其他内容)也包括在内。是否存在另一种“替代”方法,该方法将允许提取代表函数源代码的某些内容,并且仅提取其源代码,最好以某种匿名方式进行提取?


编辑(1)

def foo(x, y):
    return x - y
bar = [1, 2, foo, 4]
print(inspect.getsource(bar[2]))

...将打印def foo(x, y):\n return x - y\n

1 个答案:

答案 0 :(得分:3)

不幸的是,inspect无法做到这一点,而且如果不再次分析(和编译)源代码就不太可能工作。 inspect的{​​{3}}方法是相当有限的:它使用getsource来调用getsourcelines,这实际上将解开对象,直到我们到达findsource

那时,我们正在处理已编译的字节码。原始来源PyCodeObject留下的所有内容均为co_firstlineno

/* Bytecode object */
typedef struct {
    /* ... other fields omitted ... */
    int co_firstlineno;         /* first source line number */
    PyObject *co_code;          /* instruction opcodes */
    /* ... other fields omitted ... */
} PyCodeObject;

顺便说一句,类似于PyCodeObjectare fragments and hints, such,但 no 列,它解释了为什么回溯仅包括文件名和行:列未编译为字节码。

由于字节码没有比(第一行)更具体的区域,因此无法从inspect或仅使用(公共)字节码信息的其他任何库中获取确切的源位置解析。对于仅使用字节码的任何其他选项,例如pickle,也是如此。

inspect使用公共信息(co_firstlineno)和PyFrameObject also contains only a f_lineno。但是,inspect几乎在这里{em> ,但是它只能找到then just searches for a suitable begin of a function and the end of the surrounding block,并且它目前不能找到正确的那个。 inspect标记了整行,并且没有以正确的变体开始,它也不知道正确的对应源代码区域。

假设我们有

plus, minus, mult = lambda x: x + 1, lambda y: y - 1, lambda z: z * 5

,我们只需要minus。由于字节码不包含co_firstcolumn,因此我们只有整行可用。我们可以解析所有lambda,但是我们仍然不知道哪个lambda适合我们的co_code。我们将需要再次对其进行编译,并检查其字节码是否适合原始字节码。

最后,我们必须精确地做到这一点:再次解析源并找到正确的PyCodeObject。如果我们至少有一个起始列号,那将容易得多,因为我们可以仅使用语法分析,但是AST仅保留any block, not the correct one。因此,inspect需要一个大补丁,或者字节码需要包含已编译对象的起始列。