Получите информацию о трассировке, включая SyntaxError из компиляции ()

Основная проблема

Похоже, что SyntaxError s (и TypeError s), поднятые функцией compile() , не включены в трассировку стека, возвращаемые sys.exc_info() , но печатаются как часть отформатированного вывода с использованием traceback.print_exc .

пример

Например, учитывая следующий код (где filename – это имя файла, содержащего код Python, с $ flagrant syntax error строки $ flagrant syntax error ):

 import sys from traceback import extract_tb try: with open(filename) as f: code = compile(f.read(), filename, "exec") except: print "using sys.exc_info:" tb_list = extract_tb(sys.exc_info()[2]) for f in tb_list: print f print "using traceback.print_exc:" from traceback import print_exc print_exc() 

Я получаю следующий вывод (где <scriptname> – это имя скрипта, содержащего код выше):

 using sys.exc_info: ('<scriptname>', 6, 'exec_file', 'code = compile(f.read(), filename, "exec")') using traceback.print_exc: Traceback (most recent call last): File "<scriptname>", line 6, in exec_file code = compile(f.read(), filename, "exec") File "<filename>", line 3 $ flagrant syntax error ^ SyntaxError: invalid syntax 

Мои вопросы

У меня есть три вопроса:

  • Почему sys.exc_info() не sys.exc_info() с кадром, в SyntaxError генерируется SyntaxError ?
  • Как traceback.print_exc получает информацию о недостающем кадре?
  • Каков наилучший способ сохранить «извлеченную» информацию SyntaxError , включая SyntaxError , в списке? SyntaxError ли быть создан последний элемент списка (т. SyntaxError Информация из фрейма стека, представляющая, где произошел SyntaxError ), вручную с использованием filename и самого SyntaxError исключения SyntaxError ?

Пример использования

Для контекста, вот мой пример использования для попытки получить полное извлечение трассировки стека.

У меня есть программа, которая по существу реализует DSL, exec некоторые файлы, содержащие написанный пользователем код Python. (Независимо от того, является ли это хорошей стратегией DSL-реализации, я более или менее застрял с ней.) При столкновении с ошибкой в ​​коде пользователя я бы (в некоторых случаях), как интерпретатор, чтобы сохранить ошибку для позже, а не рвота следов стека и смерти. Поэтому у меня есть класс ScriptExcInfo специально созданный для хранения этой информации. Вот (слегка отредактированная версия) этого метода __init__ этого класса, в комплекте с довольно уродливым обходным решением для проблемы, описанной выше:

 def __init__(self, scriptpath, add_frame): self.exc, tb = sys.exc_info()[1:] self.tb_list = traceback.extract_tb(tb) if add_frame: # Note: I'm pretty sure that the names of the linenumber and # message attributes are undocumented, and I don't think there's # actually a good way to access them. if isinstance(exc, TypeError): lineno = -1 text = exc.message else: lineno = exc.lineno text = exc.text # '?' is used as the function name since there's no function context # in which the SyntaxError or TypeError can occur. self.tb_list.append((scriptpath, lineno, '?', text)) else: # Pop off the frames related to this `exec` infrastructure. # Note that there's no straightforward way to remove the unwanted # frames for the above case where the desired frame was explicitly # constructed and appended to tb_list, and in fact the resulting # list is out of order (!!!!). while scriptpath != self.tb_list[-1][0]: self.tb_list.pop() 

Обратите внимание, что «довольно уродливо» я имею в виду, что это обходное решение превращает то, что должно быть однопараметрической, 4-строчной __init__ функцией, требующей двух аргументов и занимающей 13 строк.

One Solution collect form web for “Получите информацию о трассировке, включая SyntaxError из компиляции ()”

Единственное различие между двумя вашими подходами заключается в том, что print_exc() печатает форматированное исключение. Для SyntaxError который включает форматирование информации в этом исключении, которое включает в себя фактическую строку, вызвавшую проблему.

Для самой print_exc() использует sys.exc_info()[2] , ту же самую информацию, которую вы уже используете для создания трассировки. Другими словами, он не получает больше информации, чем вы уже делаете, но вы игнорируете сама информация об исключении:

 >>> import traceback >>> try: ... compile('Hmmm, nope!', '<stdin>', 'exec') ... except SyntaxError as e: ... print ''.join(traceback.format_exception_only(type(e), e)) ... File "<stdin>", line 1 Hmmm, nope! ^ SyntaxError: invalid syntax 

Здесь traceback.format_exception_only() – недокументированная функция, используемая traceback.print_exc() для форматирования значения исключения. Вся информация доступна вам для самого исключения:

 >>> dir(e) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__getslice__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__unicode__', 'args', 'filename', 'lineno', 'message', 'msg', 'offset', 'print_file_and_line', 'text'] >>> e.args ('invalid syntax', ('<stdin>', 1, 11, 'Hmmm, nope!\n')) >>> e.filename, e.lineno, e.offset, e.text ('<stdin>', 1, 11, 'Hmmm, nope!\n') 

Также см. Документацию traceback.print_exception() :

(3) если тип SyntaxError и значение имеет соответствующий формат, он печатает строку, в которой произошла синтаксическая ошибка с кареткой, указывающей приблизительное положение ошибки.

и документация SyntaxError :

Экземпляры этого класса имеют атрибуты filename , lineno , offset и text для облегчения доступа к деталям. str() экземпляра исключения возвращает только сообщение.

То, что строка с синтаксической ошибкой не включена в трассировку, логична; код с синтаксической ошибкой не может быть выполнен, поэтому для него не был создан кадр выполнения. И исключение бросает функция compile() , самый нижний кадр исполнения.

Таким образом, вы застряли в своем «уродливом» подходе; это правильный подход для обработки исключений SyntaxError . Однако атрибуты документированы.

Обратите внимание, что exception.message обычно устанавливается в exception.args[0] , а str(exception) обычно дает вам одно и то же сообщение (если args длиннее, вы получаете вместо str(exception.args) , хотя некоторые типы исключений предоставляют пользовательский __str__ что часто просто дает вам exception.args[0] ).

 
Interesting Posts for Van-Lav

Как отключить администратора Django в развернутом проекте, но сохранить его для локального развития?

TypeError: не может использовать шаблон строки для объекта с байтом

Python unicode popen или ошибка чтения Popen unicode

Загрузите файл через гиперссылку в PhantomJS, используя Selenium

Как получить одно значение от генератора в Python?

Регулярное выражение Python: сопоставление скобок в скобках

Оптимальный способ агрегирования географических точек с помощью Python / Shapely

Структура данных для быстрого поиска GPS?

В чем разница между аргументом буферизации open () и жестко запрограммированным размером буфера для чтения, используемым при итерации через файл?

Неплохо хранить все экземпляры класса в поле класса?

Бинаризация изображения в opencv

Python Config.cfg – почему не собирается Python, используя realpath & __file__?

Не найдено: /favicon.ico

Оптимизация генерации и тестирования строки

Объедините двух менеджеров контекста в один

Python - лучший язык программирования в мире.