2009-08-20 2 views
10

Это довольно бесполезная ошибка утверждения; он не говорит значения выражения вовлеченного (предположим, что константы используются фактически имена переменных):Python assert - улучшенная интроспекция отказа?

$ python -c "assert 6-(3*2)" 
[...] 
AssertionError 

Есть ли лучший assert реализация в Python, что это больше фантазии? Он не должен вводить дополнительные надбавки за выполнение (кроме случаев, когда assert не работает) .. и должен отключиться, если используется флаг -O.

Редактировать: Я знаю о втором аргументе assert как строку. Я не хочу писать один, поскольку это закодировано в выражении, которое утверждается. СУХОЙ (не повторяй сам).

ответ

5

Вы можете прикрепить сообщение к assert:

assert 6-(3*2), "always fails" 

Сообщение также может быть построен динамически:

assert x != 0, "x is not equal to zero (%d)" % x 

См The assert statement в документации Python для получения дополнительной информации.

+0

Конечно, я знал, что это. Я не хочу писать один, поскольку он закодирован в выражении, которое утверждается. DRY. –

+0

Я понимаю, что вы имеете в виду. Я не верю, что у Python есть способ сделать это. –

0

Добавить сообщение Ваше утверждение, которое будет отображаться, если утверждение не:

$ python -c "assert 6-(3*2), '6-(3*2)'" 
Traceback (most recent call last): 
    File "<string>", line 1, in <module> 
AssertionError: 6-(3*2) 

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

+0

Точно. Это отключает эту «интроспекцию» на -O, что является ключом к вопросу. –

+0

.. НО это не накладные расходы, если эта функция вызывается только во время ошибок утверждения (а не вызовов подтверждения). –

4

The nose testing suite applies introspection to asserts.

Однако AFAICT, вы должны вызвать их утверждает, чтобы получить самоанализ:

import nose 
def test1(): 
    nose.tools.assert_equal(6, 5+2) 

результаты в

 
C:\temp\py>C:\Python26\Scripts\nosetests.exe -d test.py 
F 
====================================================================== 
FAIL: test.test1 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "C:\Python26\lib\site-packages\nose-0.11.1-py2.6.egg\nose\case.py", line 
183, in runTest 
    self.test(*self.arg) 
    File "C:\temp\py\test.py", line 3, in test1 
    nose.tools.assert_equal(6, 5+2) 
AssertionError: 6 != 7 
>> raise self.failureException, \ 
      (None or '%r != %r' % (6, 7)) 

Обратите внимание на AssertionError там. Когда моя линия была просто assert 6 == 5+2, я хотел бы получить:

 
C:\temp\py>C:\Python26\Scripts\nosetests.exe -d test.py 
F 
====================================================================== 
FAIL: test.test1 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "C:\Python26\lib\site-packages\nose-0.11.1-py2.6.egg\nose\case.py", line 
183, in runTest 
    self.test(*self.arg) 
    File "C:\temp\py\test.py", line 2, in test1 
    assert 6 == 5 + 2 
AssertionError: 
>> assert 6 == 5 + 2 

Кроме того, я не уверен, экспромтом, если их Утверждает пропускаются с -O, но это было бы очень быстро проверить.

+0

Достаточно хорошо для тестовых примеров, но для производственного кода .. есть накладные расходы на функционирование (даже с опцией -O) –

+4

Обычные утверждения тоже работают. См. Http://stackoverflow.com/questions/1308607/python-assert-improved-introspection-of-failure/1309039#1309039 – jfs

7

Как @Mark Rushakoff saidnose может оценивать неудавшиеся утверждения. Он работает и по стандарту assert.

# test_error_reporting.py 
def test(): 
    a,b,c = 6, 2, 3 
    assert a - b*c 

nosetests 'помощь:

$ nosetests --help|grep -B2 assert 
    -d, --detailed-errors, --failure-detail 
         Add detail to error output by attempting to evaluate 
         failed asserts [NOSE_DETAILED_ERRORS] 

Пример:

$ nosetests -d 
F 
====================================================================== 
FAIL: test_error_reporting.test 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "..snip../site-packages/nose/case.py", line 183, in runTest 
    self.test(*self.arg) 
    File "..snip../test_error_reporting.py", line 3, in test 
    assert a - b*c 
AssertionError: 
    6,2,3 = 6, 2, 3 
>> assert 6 - 2*3 


---------------------------------------------------------------------- 
Ran 1 test in 0.089s 

FAILED (failures=1) 
+0

Вопрос заключается в использовании assert в коде приложения (который непосредственно вызывается пользователем, для например, ./foo.py .. или нажав «foo.pyw» в Проводнике Windows), а не тестовый код .. для которого я действительно доволен выводами assert.pest. –

+1

@srid: В этом случае напишите: '__debug__ и your_fancy_assert (выражение)' - без накладных расходов на '-O'. – jfs

+0

Это звучит интересно; слишком плохо Python не имеет функции 'macro'. –

10

установить свою функцию, как sys.excepthook - см the docs.Ваша функция, если второй аргумент AssertionError, может проникнуть в содержимое вашего сердца; в частности, через третий аргумент, трассировку, он может получить фрейм и точное место, в котором не удалось выполнить утверждение, получить исключение из строя через источник или байт-код, значение всех соответствующих переменных и т. д. Модуль inspect помогает.

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

+1

Хорошо. Теперь для этого есть библиотека Python .. или мне нужно написать свой собственный? :-) (я, вероятно, не буду .., поскольку это задача с низким приоритетом для меня) –

+0

К сожалению, я не знаю о существующих библиотеках Python, выполняющих все это, кроме ориентированных на тестирование (которые, возможно, придется адаптировать с целью их использования на производственном коде). –

0

Похоже на то, что вы действительно хотите сделать, это установить точку останова отладчика непосредственно перед assert и проверить, насколько вам нравится, от вашего любимого отладчика.

0

Я закодировал замену на sys.excepthook (который вызван для любого необработанного исключения), который немного более причудливый, чем стандартный. Он проанализирует строку, в которой произошло исключение, и распечатает все переменные, которые упоминаются в этой строке (он не печатает все локальные переменные, потому что это может быть слишком много шума - также, может быть, важный var является глобальным или так).

Я назвал его py_better_exchook (отличное название), и это here.

Пример файла:

a = 6 

def test(): 
    unrelated_var = 43 
    b,c = 2, 3 
    assert a - b*c 

import better_exchook 
better_exchook.install() 

test() 

Выход:

$ python test_error_reporting.py 
EXCEPTION 
Traceback (most recent call last): 
    File "test_error_reporting.py", line 12, in <module> 
    line: test() 
    locals: 
     test = <local> <function test at 0x7fd91b1a05f0> 
    File "test_error_reporting.py", line 7, in test 
    line: assert a - b*c 
    locals: 
     a = <global> 6 
     b = <local> 2 
     c = <local> 3 
AssertionError 

 Смежные вопросы

  • Нет связанных вопросов^_^