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的外部函数的行号。
谢谢
好吧,这感觉又脏又恶心,但给你。
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)
我想在这种情况下你会使用
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) |
对于异常的原因,这将进入最深层,这基本上是我所需要的。