2009-08-20 9 views
10

Questo è un errore di asserzione piuttosto inutile; non dice i valori della espressione coinvolti (assumere costanti utilizzate sono in realtà nomi di variabili):Asserzione di Python: miglioramento dell'introspezione degli errori?

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

C'è una migliore assert implementazione in Python che è più fantasia? Non deve introdurre ulteriori overhead sull'esecuzione (tranne quando l'asserimento non riesce) .. e deve essere disattivato se viene utilizzato il flag -O.

Modifica: conosco il secondo argomento di assert come stringa. Non voglio scriverne uno ... codificato nell'espressione che si sta affermando. ASCIUTTO (non ripeti te stesso).

risposta

5

è possibile allegare un messaggio a un assert:

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

Il messaggio può anche essere costruito in modo dinamico:

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

Vedi The assert statement nella documentazione Python per ulteriori informazioni.

+0

Naturalmente, sapevo che questo. Non voglio scriverne uno in quanto è codificato nell'espressione che viene asserita. ASCIUTTO. –

+0

Capisco cosa intendi. Non credo che Python abbia un modo per farlo. –

0

aggiungere un messaggio al tuo asserzione, che verrà visualizzato se l'asserzione fallisce:

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

L'unico modo che posso pensare di fornire automaticamente sarebbe quello di contenere l'affermazione in una chiamata di procedura, e quindi ispezionare lo stack per ottenere il codice sorgente per quella linea. La chiamata aggiuntiva, purtroppo, introdurrebbe un sovraccarico nel test e non verrebbe disabilitata con -O.

+0

Precisamente. Disabilita questa 'introspezione' su -O quella è la chiave della domanda. –

+0

.. MA questo non è un overhead se questa funzione viene chiamata solo durante errori di asserzione (non chiamate di asserzione). –

4

The nose testing suite applies introspection to asserts.

Tuttavia, AFAICT, si deve chiamare il loro afferma per ottenere l'introspezione:

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

risultati in

 
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)) 

Avviso del AssertionError lì. Quando la mia linea era appena assert 6 == 5+2, vorrei avere:

 
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 

Inoltre, non sono sicuro se il loro sbrigativo afferma vengono saltati con -O, ma sarebbe un rapido controllo.

+0

Abbastanza buono per i casi di test, ma per il codice di produzione .. c'è overhead di chiamata di funzione (anche con l'opzione -O) –

+4

Anche gli asser di ordinaria funzionano. Vedi http://stackoverflow.com/questions/1308607/python-assert-improved-introspection-of-failure/1309039#1309039 – jfs

7

As @Mark Rushakoff saidnose può valutare affermazioni non riuscite. Funziona anche sullo standard assert.

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

nosetests 'aiuto:

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

Esempio:

$ 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

La domanda riguarda l'uso di assert nel codice dell'applicazione (che viene invocato direttamente dall'utente, per es., ./foo.py .. o cliccando su "foo.pyw" su Windows Explorer), e non sul codice di test .. per il quale sono davvero felice con l'output di py.test. –

+1

@srid: in questo caso scrivi: '__debug__ e your_fancy_assert (espressione)' - nessun sovraccarico su '-O'. – jfs

+0

Sembra interessante; troppo male Python non ha una funzione 'macro'. –

10

Installare il di funzione sys.excepthook - vedi the docs.La tua funzione, se il secondo argomento è AssertionError, può introspettarsi ai contenuti del tuo cuore; in particolare, attraverso il terzo argomento, il traceback, può ottenere il frame e il punto esatto in cui l'asserito ha avuto esito negativo, ottenendo l'eccezione in errore attraverso il codice sorgente o bytecode, il valore di tutte le variabili rilevanti, ecc. Il modulo inspect aiuta.

Effettuarlo in tutta generalità è piuttosto un lavoro, ma a seconda dei vincoli che si è disposti ad accettare nel modo in cui si scrive il proprio assert s può essere alleggerito in modo sostanziale (ad esempio limitandoli solo a variabili locali o globali rende l'introspezione è più facile rispetto a quando potrebbero essere coinvolte variabili non locali di una chiusura e così via).

+1

Buono. Ora c'è una libreria Python per questo .. o devo scrivere la mia? :-) (probabilmente non lo farò .. poiché questo è un compito a basso impatto per me) –

+0

Purtroppo non conosco le librerie Python esistenti facendo tutto questo, eccetto quelle orientate al testing (che potrebbe dover essere adattato allo scopo di usarli sul codice di produzione). –

0

Sembra che quello che si vuole veramente fare sia impostare un breakpoint del debugger appena prima del assert e ispezionare dal proprio debugger preferito quanto volete.

0

Ho codificato un sostituto per sys.excepthook (che viene chiamato per qualsiasi eccezione non gestita) che è un po 'più elegante di quello standard. Analizzerà la linea in cui si è verificata l'eccezione e stamperà tutte le variabili a cui si fa riferimento in questa riga (non stampa tutte le variabili locali perché potrebbe essere troppo rumore - anche, forse l'importante var è globale o così).

L'ho chiamato py_better_exchook (nome perfetto) ed è here.

di file Esempio:

a = 6 

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

import better_exchook 
better_exchook.install() 

test() 

uscita:

$ 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