Python

时间:2018-06-12 17:26:48

标签: python python-3.x eval code-generation

我正在寻找一种在运行时从包含源代码的字符串创建Python函数的方法,以便通过检查获得源代码。

我目前的方法如下:

src = 'def foo(x, y):' + '\n\t' + 'return x / y'
g = {numpy: numpy, ...}  # Modules and such required for function
l = {}
exec(src, g, l)
func = l['foo']

哪个工作得很好,但该函数没有与之关联的源代码/文件。这使调试变得困难(注意foo()中出现错误的行未显示):

>>> foo(1, 0)
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-85-9df128c5d862> in <module>()
----> 1 myfunc(3, 0)

<string> in foo(x, y)

ZeroDivisionError: division by zero

如果我在IPython解释器中定义一个函数,我可以使用inspect.getsource获取源代码,它将以回溯方式打印。 inspect.getsourcefile为这些类型的函数返回类似'<ipython-input-19-8efed6025c6f>'的内容,当然这不是真正的文件。有没有办法在非交互式环境中做类似的事情?

1 个答案:

答案 0 :(得分:1)

所以我能够通过挖掘IPython源代码来部分解决这个问题。它使用内置模块linecache,它包含从文件中读取源代码和缓存结果的函数。 inspecttraceback模块都使用此模块来获取函数的来源。

解决方案是以与问题中相同的方式创建函数,但使用compile来组成一个唯一的文件名:

source = 'def foo(x, y):' + '\n\t' + 'return x / y'

filename = '<dynamic-123456>'  # Angle brackets may be required?
code = compile(source, filename, 'exec')

g = {numpy: numpy, ...}  # Modules and such required for function
l = {}
exec(src, g, l)
func = l['foo']

linecache包含一个变量cache,它是一个将文件名映射到(size, mtime, lines, fullname)元组的字典。您只需添加伪文件名的条目:

lines = [line + '\n' for line in source.splitlines()]

import linecache
linecache.cache[filename] = (len(source), None, lines, filename)

该函数将使用inspect.getsource(),IPython的? / ??语法和IPython回溯。但是,它似乎仍无法在内置回溯中运行。这对我来说已经足够了,因为我几乎总是在IPython中工作。

编辑:请参阅下面的user2357112评论,了解如何在内置解释器中使用回溯打印。