Подтвердить что ты не робот

Как ограничить трассировку python для определенных файлов

Я пишу много кода Python, который использует внешние библиотеки. Часто я буду писать ошибку, и когда я запустил код, я получаю большую длинную трассировку в консоли Python. 99.999999% времени, вызванного ошибкой кодирования в моем коде, а не из-за ошибки в пакете. Но трассировка проходит вплоть до строки ошибки в коде пакета, и либо требуется много прокрутки трассировки, чтобы найти код, который я написал, либо трассировка настолько глубоко в пакете, что мой собственный код не работает, t даже отображаются в трассировке.

Есть ли способ "черного ящика" кода пакета или как-то только показывать строки трассировки из моего кода? Я хотел бы указать в системе, какие каталоги или файлы я хочу видеть в трассировке.

4b9b3361

Ответ 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')

Как извлечь ваши фреймы.

  • пропустите кадры, которые не имеют для вас значения (например, пользовательский код подтверждения)
  • определить, сколько кадров является частью вашего кода → length
  • извлечение length кадров

    def mycode_traceback_levels(tb):
      length = 0
      while tb and is_mycode(tb):
        tb = tb.tb_next
        length += 1
      return length
    

Обработчик примера.

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