Короткая версия:
Есть ли способ достичь в Python того же эффекта, который достигается утилитой Perl <a href="http://perldoc.perl.org/Carp.html">Carp::carp</a>
?
Длинная версия (для тех, кто не знаком с Carp::carp
):
Предположим, что мы реализуем некоторую библиотечную API-функцию (т. Е. Она предназначена для использования другими программистами в их коде), скажем, spam
, и предположим, что spam
включает некоторый код для проверки обоснованность аргументов, переданных ему. Конечно, этот код должен вызывать исключение, если обнаруживается какая-либо проблема с этими аргументами. Допустим, мы хотим сделать связанное сообщение об ошибке и трассировку как можно более полезным для тех, кто отлаживает некоторый клиентский код.
В идеале, последняя строка трассировки, созданная этим возбужденным исключением, должна указывать на «код ошибки», а именно строку в клиентском коде , где spam
был вызван с недопустимыми аргументами.
К сожалению, это не то, что произойдет, по крайней мере по умолчанию, с использованием Python. Вместо этого последняя строка трассировки будет ссылаться где-то во внутреннем коде библиотеки, где на самом деле исключение было raise
'd, что было бы весьма неясным для целевой аудитории этого конкретного трассировки .
Пример:
# 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 <module>
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 <module>
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-эквивалента Carp::carp
в Perl, но после изучения его в течение некоторого времени я не смог найти какой-либо способ использовать его для того, что я хочу делать. FWIW, Perl Carp::carp
позволяет точно настроить начальный кадр для трассировки путем предоставления глобальной (и, следовательно, динамической области) переменной $Carp::CarpLevel
. Библиотечные функции без API, которые могут carp
-out, local
-ize и увеличивать эту переменную при входе (например, local $Carp::CarpLevel += 1;
). Я не вижу ничего , даже удаленно , как у этого модуля Python traceback
. Так что, если я что-то пропустил, любое решение, использующее Python traceback
, должно было бы пойти по другому пути ...