在exec中的字符串的python回溯?

python traceback of a string in exec?

我有一个函数,它存储在一个字符串中,看起来像这样:

1
func_str ="def <func_name> ..."

我正在使用"exec"对其进行评估,并在输入上使用它,如下所示:

1
2
exec func_str in locals()
locals()[func_name](inp)

现在这个函数可能有一个异常,我想知道是哪一行在字符串中引起了它。在解释器中运行它会给我一条错误消息,这正是我想要的:

1
2
  File"<string>", line 6, in <func_name>
TypeError: can only concatenate tuple (not"int") to tuple

这告诉我字符串中的第6行导致了问题。

有没有什么方法可以通过编程的方式捕获这个?我看过类似的解决方案,但它们没有处理来自本地范围内执行的字符串的异常。尝试使用回溯模块时,我只得到调用exec的外部函数的行号。

谢谢


好吧,这感觉又脏又恶心,但给你。

sys.exc_info()[2].tb_next.tb_lineno + frameinfo.lineno

lineno必须直接在字符串化代码的上方行以进行eval,或者如果代码从脚本的开头开始,显然这是不必要的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import sys
from inspect import currentframe, getframeinfo

frameinfo = getframeinfo(currentframe())
func_str ="""
def func_name(param):
  d = []
  u = 1
  pass
  a = ''
  pass
  print a + param
  print"hi"
  print"ho"
   """

exec func_str in locals()
inp = 1
try:
  locals()["func_name"](inp)
except Exception as e:
  print"Fails at:", sys.exc_info()[2].tb_next.tb_lineno + frameinfo.lineno
  print"Inside:", len(func_str.split("
"
)) - frameinfo.lineno

输出

1
2
Fails at: 12
Inside: 7

如果您只想将"lineno"用于这个字符串化的源,那么

1
2
len(func_str.split("
"
) - frameinfo.lineno

我不知道你是自己决定的还是被迫的,但我很抱歉:)

编辑:

如果远程接收字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import sys
from inspect import currentframe, getframeinfo


some_item ="frameinfo = getframeinfo(currentframe())"

pass
random_items_here = 1

func_str ="""
line_no = 2
lineno = 3
a_number = 0
def func_name(param):
  d = []
  u = 1
  pass
  a = ''
  pass
  print a + param
  print"hi"
  print"ho"
   """

exec some_item +"
"
+ func_str in locals()
inp = 1
try:
  locals()["func_name"](inp)
except Exception as e:
  print"Fails at:", sys.exc_info()[2].tb_lineno
  print"Inside:", len(func_str.split("
"
)) - 2 - frameinfo.lineno

出:

1
2
Fails at: 27
Inside: 11

但这似乎在末尾的多余新行上失败了(所以您至少需要去掉()func_str)


我想在这种情况下你会使用evalexec不返回任何内容:

1
2
3
4
5
6
7
8
>>> import traceback
>>> try: eval("1/0")
... except: print"Got exception:", traceback.format_exc()
...
Got exception: Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
  File"<string>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero


多亏了这些答案,他们很有帮助。

我认为我所缺少的基本上是一种"进入"回溯堆栈的方法,因为我停止了外部异常,而不是进入失败的绝对"根"。

这对我有好处:

1
2
3
4
5
def go_deeper(deeep):
  if deeep.tb_next == None:
    return deeep.tb_lineno
  else:
    return go_deeper(deeep.tb_next)

对于异常的原因,这将进入最深层,这基本上是我所需要的。