Как ограничить трассировку python для определенных файлов
Я пишу много кода Python, который использует внешние библиотеки. Часто я буду писать ошибку, и когда я запустил код, я получаю большую длинную трассировку в консоли Python. 99.999999% времени, вызванного ошибкой кодирования в моем коде, а не из-за ошибки в пакете. Но трассировка проходит вплоть до строки ошибки в коде пакета, и либо требуется много прокрутки трассировки, чтобы найти код, который я написал, либо трассировка настолько глубоко в пакете, что мой собственный код не работает, t даже отображаются в трассировке.
Есть ли способ "черного ящика" кода пакета или как-то только показывать строки трассировки из моего кода? Я хотел бы указать в системе, какие каталоги или файлы я хочу видеть в трассировке.
Ответы
Ответ 1
Чтобы напечатать собственный стек, вам нужно будет обрабатывать все необработанные исключения самостоятельно; вот как облегчается sys.excepthook
.
Подпись для этой функции sys.excepthook(type, value, traceback)
, и ее задача следующая:
Эта функция выводит заданную трассировку и исключение в sys.stderr
.
Итак, пока вы можете играть с трассировкой и извлекать только часть, о которой вы заботитесь, все должно быть хорошо. Структуры тестирования делают это очень часто; у них есть пользовательские функции assert
, которые обычно не отображаются в трассировке, другими словами, они пропускают кадры, принадлежащие тестовой структуре. Кроме того, в этих случаях тесты обычно запускаются также с помощью тестовой среды.
В итоге вы получите трассировку, которая выглядит так:
[ custom assert code ]
+ ... [ code under test ] ...
+ [ test runner code ]
Как определить код.
Вы можете добавить глобальный код:
__mycode = True
Затем для определения фреймов:
def is_mycode(tb):
globals = tb.tb_frame.f_globals
return globals.has_key('__mycode')
Как извлечь ваши фреймы.
Обработчик примера.
def handle_exception(type, value, tb):
# 1. skip custom assert code, e.g.
# while tb and is_custom_assert_code(tb):
# tb = tb.tb_next
# 2. only display your code
length = mycode_traceback_levels(tb)
print ''.join(traceback.format_exception(type, value, tb, length))
установите обработчик:
sys.excepthook = handle_exception
Что дальше?
Вы можете настроить length
, чтобы добавить один или несколько уровней, если вам по-прежнему нужна информация о том, где сбой находится вне вашего собственного кода.
см. также https://gist.github.com/dnozay/b599a96dc2d8c69b84c6
Ответ 2
Как и другие, вы можете использовать sys.excepthook
:
Эта функция выводит заданную трассировку и исключение в sys.stderr.
Когда исключение возбуждается и не получается, интерпретатор вызывает sys.excepthook с тремя аргументами, классом исключений, экземпляром исключения и объектом traceback. В интерактивном сеансе это происходит непосредственно перед возвратом элемента управления в приглашение; в программе Python это происходит непосредственно перед выходом программы. Обработка таких исключений верхнего уровня может быть настроена путем назначения другой функции с тремя аргументами для sys.excepthook.
(акцент мой)
Можно фильтровать трассировку, извлеченную extract_tb
(или аналогичные функции из модуля traceback
) на основе указанных каталогов.
Две функции, которые могут помочь:
from os.path import join, abspath
from traceback import extract_tb, format_list, format_exception_only
def spotlight(*show):
''' Return a function to be set as new sys.excepthook.
It will SHOW traceback entries for files from these directories. '''
show = tuple(join(abspath(p), '') for p in show)
def _check_file(name):
return name and name.startswith(show)
def _print(type, value, tb):
show = (fs for fs in extract_tb(tb) if _check_file(fs.filename))
fmt = format_list(show) + format_exception_only(type, value)
print(''.join(fmt), end='', file=sys.stderr)
return _print
def shadow(*hide):
''' Return a function to be set as new sys.excepthook.
It will HIDE traceback entries for files from these directories. '''
hide = tuple(join(abspath(p), '') for p in hide)
def _check_file(name):
return name and not name.startswith(hide)
def _print(type, value, tb):
show = (fs for fs in extract_tb(tb) if _check_file(fs.filename))
fmt = format_list(show) + format_exception_only(type, value)
print(''.join(fmt), end='', file=sys.stderr)
return _print
Они оба используют traceback.extract_tb
. Он возвращает "список" предварительно обработанных "записей трассировки стека, извлеченных из объекта трассировки"; все они являются экземплярами traceback.FrameSummary
(именованный кортеж). Каждый объект traceback.FrameSummary
имеет поле filename
, которое сохраняет абсолютный путь к соответствующему файлу. Мы проверяем, начинается ли он с любого из путей каталога, предоставленных в виде отдельных аргументов функции, чтобы определить, нужно ли нам исключать запись (или сохранять ее).
Здесь Пример:
Модуль enum
из стандартной библиотеки не позволяет повторно использовать ключи,
import enum
enum.Enum('Faulty', 'a a', module=__name__)
дает
Traceback (most recent call last):
File "/home/vaultah/so/shadows/main.py", line 23, in <module>
enum.Enum('Faulty', 'a a', module=__name__)
File "/home/vaultah/cpython/Lib/enum.py", line 243, in __call__
return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
File "/home/vaultah/cpython/Lib/enum.py", line 342, in _create_
classdict[member_name] = member_value
File "/home/vaultah/cpython/Lib/enum.py", line 72, in __setitem__
raise TypeError('Attempted to reuse key: %r' % key)
TypeError: Attempted to reuse key: 'a'
Мы можем ограничить записи трассировки стека нашим кодом (в/home/vaultah/so/shadows/main.py).
import sys, enum
sys.excepthook = spotlight('/home/vaultah/so/shadows')
enum.Enum('Faulty', 'a a', module=__name__)
и
import sys, enum
sys.excepthook = shadow('/home/vaultah/cpython/Lib')
enum.Enum('Faulty', 'a a', module=__name__)
дают тот же результат:
File "/home/vaultah/so/shadows/main.py", line 22, in <module>
enum.Enum('Faulty', 'a a', module=__name__)
TypeError: Attempted to reuse key: 'a'
Есть способ исключить все каталоги сайтов (где установлены сторонние пакеты - см. site.getsitepackages
)
import sys, site, jinja2
sys.excepthook = shadow(*site.getsitepackages())
jinja2.Template('{%}')
# jinja2.exceptions.TemplateSyntaxError: unexpected '}'
# Generates ~30 lines, but will only display 4
Примечание. Не забудьте восстановить sys.excepthook из sys.__ excepthook__. К сожалению, вы не сможете "исправлять-восстановить" его с помощью диспетчера контекстов.
Ответ 3
traceback.extract_tb (tb) вернет кортеж фреймов ошибок в формате (файл, line_no, type, error_statement), вы можете играть с ним для форматирования трассировки. Также см. https://pymotw.com/2/sys/exceptions.html
import sys
import traceback
def handle_exception(ex_type, ex_info, tb):
print ex_type, ex_info, traceback.extract_tb(tb)
sys.excepthook = handle_exception