Краткая версия:
Есть ли способ добиться в Python того же эффекта, что и Perl
Carp :: carp Утилита
?
Длинная версия (для тех, кто не знаком с Carp :: carp
):
Предположим, мы реализуем некоторую библиотечную функцию API (т.е., он предназначен для использования другими программистами в их коде), например, спаме
, и предположим, что спам
включает некоторый код для проверки правильности аргументов перешел к нему. Конечно, этот код должен вызывать исключение, если обнаружена какая-либо проблема с этими аргументами. Допустим, мы хотим сделать связанное с ним сообщение об ошибке и трассировку как можно более полезными для тех, кто отлаживает некоторый клиентский код.
В идеале последняя строка трассировки, созданная этим вызванным исключением, должна точно указывать на «код нарушения», а именно на строку в коде клиента , где спам
был вызван с недопустимыми аргументами. .
К сожалению, это не то, что происходит, по крайней мере, по умолчанию, при использовании Python. Вместо этого последняя строка трассировки будет относиться к чему-то внутри кода библиотеки, где на самом деле исключение было raise
'd, что было бы весьма непонятно для предполагаемой аудитории this конкретная трассировка.
Пример:
# spam.py (library code)
def spam(ham, eggs):
'''
Do something stupid with ham and eggs.
At least one of ham and eggs must be True.
'''
_validate_spam_args(ham, eggs)
return ham == eggs
def _validate_spam_args(ham, eggs):
if not (ham or eggs):
raise ValueError('if we had ham '
'we could have ham and eggs '
'(if we had eggs)')
# client.py (client code)
from spam import spam
x = spam(False, False)
Когда мы запускаем client.py
, мы получаем:
% python client.py
Traceback (most recent call last):
File "client.py", line 3, in
x = spam(False, False)
File "/home/jones/spam.py", line 7, in spam
_validate_spam_args(ham, eggs)
File "/home/jones/spam.py", line 12, in _validate_spam_args
raise ValueError('if we had ham '
ValueError: if we had ham we could have ham and eggs (if we had eggs)
, тогда как то, что нам нужно, будет ближе к:
% python client.py
Traceback (most recent call last):
File "client.py", line 3, in
x = spam(False, False)
ValueError: if we had ham we could have ham and eggs (if we had eggs)
... с ошибочным кодом ( x = spam (False, False)
) в качестве последней строки трассировки.
Что нам нужно, так это способ сообщить об ошибке «с точки зрения вызывающего» (это то, что Carp :: carp
позволяет делать в Perl).
РЕДАКТИРОВАТЬ: Для ясности, этот вопрос не касается LBYL и EAFP, а также предварительных условий или программирования по контракту. Прошу прощения, если я произвел такое неверное впечатление.Этот вопрос касается того, как произвести обратную трассировку, начиная с нескольких (одного, двух) уровней стека вызовов.
EDIT2: модуль Python traceback
- очевидное место для поиска Python-эквивалента Perl Carp :: carp
, но после его изучения в течение некоторого времени я не смог найти способ использовать его для того, что я хочу делать. FWIW, Perl's Carp :: carp
позволяет точно настроить начальный кадр для трассировки, выставив глобальную (следовательно, с динамической областью видимости) переменную $ Carp :: CarpLevel
. Библиотечные функции, не относящиеся к API, которые могут вычислить
-out, local
-ize и увеличить эту переменную при входе (например, local $ Carp :: CarpLevel + = 1;
). Я не вижу ничего даже отдаленно вроде этого модуля трассировки
Python. Так что, если я что-то не пропустил, любое решение, использующее трассировку Python
, должно было бы занять несколько иной подход ...