2016-02-22 3 views
2

У меня есть функция, которая хранится в виде строки, которая выглядит примерно так:python traceback строки в exec?

func_str = "def <func_name> ..." 

Я оцениваю его с помощью «Exec» и использовать его на входе следующим образом:

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

Теперь эта функция может иметь исключение, и я хотел бы знать, какая строка вызвала ее в строке. Запуск его в интерпретаторе дает мне сообщение об ошибке, которое в точности то, что я хочу:

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

Это говорит мне, что шестая строка в моей строке вызывает проблему.

Есть ли способ захвата этого программного обеспечения? Я рассмотрел аналогичные решения, но они не рассматривают исключение, исходящее из строки, которая была выполнена в локальной области. При попытке использовать модуль трассировки я получил только номер строки для внешней функции, вызывающей exec.

благодаря

ответ

1

Я думаю, что вы хотите использовать eval в этом случае. exec не возвращает ничего:

>>> 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 
+0

exec должен был бы определить функцию в локальной области видимости, которую я затем выполняю с помощью "locals() [func_name] (inp)" –

2

Ну, это чувствует себя грязным и отвратительным, но здесь вы идете.

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

LINENO должен быть непосредственно выше строка Ваш stringized код Eval, или если код начинается в начале сценария - очевидно, это не нужно.

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("\n")) - frameinfo.lineno 

выход

Fails at: 12 
Inside: 7 

если Вы хотели "LINENO" для этого stringized источника только тогда

len(func_str.split("\n") - frameinfo.lineno 

Я не знаю, вы решили на этой архитектуре самостоятельно или были вызваны к нему, но мне жаль :)

Редактировать:

Если Вы получаете удаленно снабжать струной, тетивой и т.п.

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 + "\n" + 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("\n")) - 2 - frameinfo.lineno 

из:

Fails at: 27 
Inside: 11 

, но это, кажется, терпит неудачу на превышении новых линий в конце (так что вам нужно раздеться() func_str на наименее)

+0

Что делать, если моя строка передается извне, а не известна в то время, когда я написал скрипт? –

+0

Хмм, интересно, как отправлено через сокет? Тогда вы могли бы позволить сумасшедшему бросить :) и добавить «frameinfo = getframeinfo (currentframe()) перед строкой, которую вы собираетесь выполнить. – JustMe

+0

хорошо подумайте об этом как о удаленном вызове, кто-то отправит вам строку для выполнения. В любом случае, я думаю, что решил это с помощью tb_next, чтобы перейти к следующему слою. спасибо, что потратили время на ответ! –

0

Благодаря ответам они были очень полезны.

Я думаю, что я пропускал был по существу метод «идти внутрь» отладочную стеку, как я останавливался на внешних исключениях вместо того, чтобы идти к абсолютному «корню» провал

это сделало трюк для меня:

def go_deeper(deeep): 
    if deeep.tb_next == None: 
    return deeep.tb_lineno 
    else: 
    return go_deeper(deeep.tb_next) 

Это перейдет на самый глубокий слой для причины исключения, которое по сути является всем необходимым.