简体   繁体   English

在运行动态编译的代码对象时,如何在回溯中保留源代码行?

[英]How do I retain source lines in tracebacks when running dynamically-compiled code objects?

Say I use compile to create a code object from a string and a name: 假设我使用compile从字符串和名称创建code对象:

>>> a = compile('raise ValueError\n', '<at runtime>', 'exec')

I would like the lines within that string to appear within the traceback (note - the following is run in IDLE): 我希望该字符串中的行显示在回溯中(注意 - 以下内容在IDLE中运行):

>>> exec(a)
Traceback (most recent call last):
 File "<pyshell#11>", line 1, in <module>
    exec(c)
  File "<at runtime>", line 1, in <module>
    raise ValueError   <-- This line is what I want
ValueError

Alas, they do not: 唉,他们没有:

>>> exec(a)
Traceback (most recent call last):
 File "<pyshell#11>", line 1, in <module>
    exec(c)
  File "<at runtime>", line 1, in <module>
ValueError

Without creating a temporary file, how do I make that raise ValueError line appear in the traceback? 在不创建临时文件的情况下,如何在跟踪中显示raise ValueError行?

Using the undocumented cache member of the builtin linecache , this seems to work: 使用内置linecache的未记录的cache成员,这似乎工作:

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_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

It turns out this is pretty much how IDLE does it 事实证明这就是IDLE如何做到的

Well, you could write your own exception handler which fills the data: 好吧,你可以编写自己的异常处理程序来填充数据:

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)

Result: 结果:

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

Now just wrap that compile & exec into a smart_exec function to call every time... 现在只需将compile&exec包装成一个smart_exec函数,每次都调用...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM