Как изменить объект трассировки Python при создании исключения?

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

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

Заранее благодарим за любой совет!

Ответы

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 4

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

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

Ответ 5

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

Он берет трассировку и удаляет первый файл, который не должен отображаться. Затем он имитирует поведение 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)

Ответ 6

Для python3 вот мой ответ. Пожалуйста, прочтите комментарии для объяснения:

def pop_exception_traceback(exception,n=1):
    #Takes an exception, mutates it, then returns it
    #Often when writing my repl, tracebacks will contain an annoying level of function calls (including the 'exec' that ran the code)
    #This function pops 'n' levels off of the stack trace generated by exception
    #For example, if print_stack_trace(exception) originally printed:
    #   Traceback (most recent call last):
    #   File "<string>", line 2, in <module>
    #   File "<string>", line 2, in f
    #   File "<string>", line 2, in g
    #   File "<string>", line 2, in h
    #   File "<string>", line 2, in j
    #   File "<string>", line 2, in k
    #Then print_stack_trace(pop_exception_traceback(exception),3) would print: 
    #   File "<string>", line 2, in <module>
    #   File "<string>", line 2, in j
    #   File "<string>", line 2, in k
    #(It popped the first 3 levels, aka f g and h off the traceback)
    for _ in range(n):
        exception.__traceback__=exception.__traceback__.tb_next
    return exception