Python 在运行动态编译的代码对象时,如何在回溯中保留源代码行?
假设我使用字符串和名称创建Python 在运行动态编译的代码对象时,如何在回溯中保留源代码行?,python,Python,假设我使用字符串和名称创建code对象: >>> a = compile('raise ValueError\n', '<at runtime>', 'exec') >a=compile('raisevalueerror\n','exec') 我希望该字符串中的行出现在回溯中(注意-以下是在空闲状态下运行的): 执行(a) 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 行政主任(c) 文件“”,第1行,在 提升值错误>>执行(a) 回溯(最近一次呼叫最后
code
对象:
>>> a = compile('raise ValueError\n', '<at runtime>', 'exec')
>a=compile('raisevalueerror\n','exec')
我希望该字符串中的行出现在回溯中(注意-以下是在空闲状态下运行的):
执行(a)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
行政主任(c)
文件“”,第1行,在
提升值错误>>执行(a)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
行政主任(c)
文件“”,第1行,在
数值误差
在不创建临时文件的情况下,如何使
raisevalueerror
行显示在回溯中?使用内置的未记录的缓存
成员,这似乎可行:
def better_compile(src, name, mode):
# there is an example of this being set at
# https://hg.python.org/cpython/file/2.7/Lib/linecache.py#l104
from linecache import cache
cache[name] = (
len(src), None,
[line+'\n' for line in src.splitlines()], name
)
return compile(src, name, mode)
>>c=better\u compile('raise ValueError\n','好的,您可以编写自己的异常处理程序来填充数据:
code = """
def f1():
f2()
def f2():
1 / 0
f1()
"""
a = compile(code, '<at runtime>', 'exec')
import sys
import traceback
try:
exec(a)
except:
etype, exc, tb = sys.exc_info()
exttb = traceback.extract_tb(tb)
## Fill the missing data:
exttb2 = [(fn, lnnr, funcname,
(code.splitlines()[lnnr-1] if fn=='<at runtime>'
else line))
for fn, lnnr, funcname, line in exttb]
# Print:
sys.stderr.write('Traceback (most recent call last):\n')
for line in traceback.format_list(exttb2):
sys.stderr.write(line)
for line in traceback.format_exception_only(etype, exc):
sys.stderr.write(line)
code=”“”
def f1():
f2()
def f2():
1/0
f1()
"""
a=编译(代码,,'exec')
导入系统
导入回溯
尝试:
行政主任(a)
除:
etype,exc,tb=sys.exc_info()
exttb=回溯。提取\u tb(tb)
##填写缺失的数据:
exttb2=[(fn,lnnr,funcname,
(如果fn='',则代码.splitlines()[lnnr-1]
其他行)
对于fn、lnnr、funcname、exttb中的行]
#打印:
sys.stderr.write('回溯(最近一次调用):\n')
对于traceback.format_列表(exttb2)中的行:
系统标准写入(行)
对于traceback.format_exception_中的行,仅限(etype,exc):
系统标准写入(行)
结果:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<at runtime>", line 8, in <module>
f1()
File "<at runtime>", line 3, in f1
f2()
File "<at runtime>", line 6, in f2
1 / 0
ZeroDivisionError: integer division or modulo by zero
回溯(最近一次呼叫最后一次):
文件“”,第2行,在
文件“”,第8行,在
f1()
文件“”,第3行,在f1中
f2()
文件“”,第6行,在f2中
1/0
ZeroDivisionError:整数除法或模零除法
现在,只需将compile&exec包装到一个smart\u exec
函数中,以便每次调用…只是为了澄清一点——您希望回溯指向行a=compile(…)
?不,我希望回溯包括传递给compile
的字符串的对应行。在我看来,它确实保留了行号…“文件”,第1行,in'。如果在raise
语句之前添加一些换行符,您将看到该数字增加。我需要行内容以及行号。啊…好的,我现在明白了。对不起:-)这对monkey patch来说是一件非常讨厌的事情,当您使用相同的模块名编译不同的代码段时,这将导致错误的回溯,或者如果您不断生成新名称并缓存无限数量的代码段,将导致内存泄漏。@user2357112:我不是说better\u compile
始终是compile
的合适替代品,但有了受控使用(创建负责任的名称),也应该,根据@user2357112的反馈,这似乎是一个上下文管理器的好地方,它可以在上下文退出时删除缓存项。@mgilson:或者可能对code
对象进行子类化,并在\uu del_u
@user2357112:谢谢您的警告。使用更好的编译自动生成伪文件名(如
)可以避免重复的名称,如
。lru缓存有限制。对于真实文件名,文件在转储后可以重新读取(但导入后可能已更改)。如果一个回溯在被转储后需要
,我们将回到当前的情况——回溯中缺少代码。因此,您的论点是包装exec
,而不是compile
?这也可能是一个问题option@Eric没错,但是,当然,您也必须将原始代码(code
)传递给包装器,因为编译的a
没有保存源代码。
>>> c = better_compile('raise ValueError\n', '<a name>', 'exec')
>>> exec(c)
Traceback (most recent call last):
File "<pyshell#50>", line 1, in <module>
exec(c)
File "<a name>", line 1, in <module>
raise ValueError
ValueError
code = """
def f1():
f2()
def f2():
1 / 0
f1()
"""
a = compile(code, '<at runtime>', 'exec')
import sys
import traceback
try:
exec(a)
except:
etype, exc, tb = sys.exc_info()
exttb = traceback.extract_tb(tb)
## Fill the missing data:
exttb2 = [(fn, lnnr, funcname,
(code.splitlines()[lnnr-1] if fn=='<at runtime>'
else line))
for fn, lnnr, funcname, line in exttb]
# Print:
sys.stderr.write('Traceback (most recent call last):\n')
for line in traceback.format_list(exttb2):
sys.stderr.write(line)
for line in traceback.format_exception_only(etype, exc):
sys.stderr.write(line)
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<at runtime>", line 8, in <module>
f1()
File "<at runtime>", line 3, in f1
f2()
File "<at runtime>", line 6, in f2
1 / 0
ZeroDivisionError: integer division or modulo by zero