Как я могу изменить объект трассировки Python при возникновении исключения? - PullRequest
20 голосов
/ 22 октября 2009

Я работаю над библиотекой Python, используемой сторонними разработчиками для написания расширений для нашего основного приложения.

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

Заранее спасибо за любые советы!

Ответы [ 5 ]

11 голосов
/ 16 декабря 2012

Вы можете легко удалить верх трассировки, подняв с помощью элемента трассировки tb_next:

except:
    ei = sys.exc_info()
    raise ei[0], ei[1], ei[2].tb_next

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

8 голосов
/ 22 октября 2009

Посмотрите, что делает jinja2 здесь:

https://github.com/mitsuhiko/jinja2/blob/5b498453b5898257b2287f14ef6c363799f1405a/jinja2/debug.py

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

1 голос
/ 12 сентября 2012

Вас также может заинтересовать PEP-3134 , который реализован в python 3 и позволяет привязать одно исключение / трассировку к исключению восходящего потока.

Это не совсем то же самое, что модификация трассировки, но, вероятно, это был бы идеальный способ донести «короткую версию» до пользователей библиотеки, оставив при этом «длинную версию» доступной.

1 голос
/ 04 декабря 2009

Как насчет того, чтобы не изменять трассировку? Две запрошенные вами вещи могут быть выполнены более легко по-разному.

  1. Если исключение из библиотеки перехвачено в коде разработчика, и вместо этого возникает новое исключение, исходная трассировка будет, конечно, отброшена. Вот как обычно обрабатываются исключения ... если вы просто разрешаете вызывать исходное исключение, но вы используете его для удаления всех «верхних» фреймов, фактическое исключение не будет иметь смысла, так как последняя строка в трассировке не будет сам по себе способен вызвать исключение.
  2. Чтобы удалить последние несколько кадров, вы можете попросить сократить ваши трассировки ... такие вещи, как traceback.print_exception (), принимают параметр "limit", который вы можете использовать для пропуска нескольких последних записей.

Тем не менее, вполне вероятно, что можно будет отследить трассировки, если вам действительно нужно ... но где бы вы это сделали? Если в каком-либо коде оболочки на самом верхнем уровне вы можете просто получить трассировку, взять фрагмент, чтобы удалить ненужные части, а затем использовать функции в модуле «трассировки» для форматирования / печати по желанию.

0 голосов
/ 28 сентября 2016

Этот код может вас заинтересовать.

Проходит трассировку и удаляет первый файл, который не должен отображаться. Затем он имитирует поведение Python:

Traceback (most recent call last):

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

Вот мой код, предполагая, что есть строка text:

try:
    exec(text)
except:
    # we want to format the exception as if no frame was on top.
    exp, val, tb = sys.exc_info()
    listing = traceback.format_exception(exp, val, tb)
    # remove the entry for the first frame
    del listing[1]
    files = [line for line in listing if line.startswith("  File")]
    if len(files) == 1:
        # only one file, remove the header.
        del listing[0]
    print("".join(listing), file=sys.stderr)
    sys.exit(1)
...