2013-05-01 9 views
9

C'è un problema noto in Python, dove "close failed in file object destructor" when "Broken pipe" happens on stdout - Python tracker Issue 11380; visto anche in python - Why does my Python3 script balk at piping its output to head or tail (sys module)? - Stack Overflow.Soppressione stampa di "Exception ... ignorato" messaggio in Python 3

Quello che voglio fare, è stampato lo stesso messaggio personalizzato quando questo problema si verifica, sia in Python 2.7 e Python 3+. Così mi preparo uno script di test, testprint.py ed eseguirlo (frammenti mostrati fatto in bash, Ubuntu 11.04):

$ cat > testprint.py <<"EOF" 
import sys 

def main(): 
    teststr = "Hello " * 5 
    sys.stdout.write(teststr + "\n") 

if __name__ == "__main__": 
    main() 
EOF 

$ python2.7 testprint.py 
Hello Hello Hello Hello Hello 

$ python2.7 testprint.py | echo 

close failed in file object destructor: 
sys.excepthook is missing 
lost sys.stderr 

$ python3.2 testprint.py | echo 

Exception IOError: (32, 'Broken pipe') in <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'> ignored 

Come previsto dal link sopra, ci sono due messaggi differenti. In Help with a piping error (velocityreviews.com), si raccomanda di utilizzare sys.stdout.flush() per forzare Python 2 per registrare un IOError invece di quel messaggio; con questo, abbiamo:

$ cat > testprint.py <<"EOF" 
import sys 

def main(): 
    teststr = "Hello " * 5 
    sys.stdout.write(teststr + "\n") 
    sys.stdout.flush() 

if __name__ == "__main__": 
    main() 
EOF 

$ python2.7 testprint.py | echo 

Traceback (most recent call last): 
    File "testprint.py", line 9, in <module> 
    main() 
    File "testprint.py", line 6, in main 
    sys.stdout.flush() 
IOError: [Errno 32] Broken pipe 

$ python3.2 testprint.py | echo 

Traceback (most recent call last): 
    File "testprint.py", line 9, in <module> 
    main() 
    File "testprint.py", line 6, in main 
    sys.stdout.flush() 
IOError: [Errno 32] Broken pipe 
Exception IOError: (32, 'Broken pipe') in <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'> ignored 

OK, sempre più vicino ... Ora, il modo di "ignora" queste eccezioni (o nel mio caso, sostituirlo con un messaggio di errore personalizzato), è quello di gestirli:

Ignore exceptions - comp.lang.python

> c'è un modo per rendere [interprete] ignorare le eccezioni.
No. Gestisci le eccezioni o scrivi codice che non genera eccezioni.

... e come An Introduction to Python - Handling Exceptions note, il modo per farlo è un try/except block. Quindi proviamo che:

$ cat > testprint.py <<"EOF" 
import sys 

def main(): 
    teststr = "Hello " * 5 
    try: 
    sys.stdout.write(teststr + "\n") 
    sys.stdout.flush() 
    except IOError: 
    sys.stderr.write("Exc: " + str(sys.exc_info()[0]) + "\n") 

if __name__ == "__main__": 
    main() 
EOF 

$ python2.7 testprint.py | echo 

Exc: <type 'exceptions.IOError'> 

$ python3.2 testprint.py | echo 

Exc: <class 'IOError'> 
Exception IOError: (32, 'Broken pipe') in <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'> ignored 

Ok, quindi cercate/ad eccezione delle opere come mi aspetto che per Python 2.7 - ma poi, Python 3.2 entrambe le maniglie come previsto, ed ancora genera un messaggio Exception ... ignored! Qual è il problema - non è "except IOError" sufficiente per Python 3? Ma deve essere - altrimenti non avrebbe stampato il messaggio "Exc:..." personalizzato!

Quindi, qual è il problema qui e perché lo Exception ... ignored è ancora stampato in Python 3, anche se gestisco l'eccezione? E ancora più importante, come faccio a gestire in modo che il Exception ... ignoredfa non ottenere più stampate?

+0

Funziona per me con la testa, la coda, meno, più e unico, quindi sembra che ci sia un bug reale nell'interazione con l'eco in particolare. La parte "Eccezione ignorata" sta effettivamente accadendo durante lo spegnimento dell'interprete quando tenta di scaricare nuovamente i flussi standard. – ncoghlan

risposta

3

Solo alcune più note su questo - problema ancora non risolto ... primo: messaggio

Issue 6294: Improve shutdown exception ignored message - Python tracker

Questo errore viene generato in PyErr_WriteUnraisable, che è chiamato da molti contesti, compresi i metodi __del__ . Metodo __del__ chiamato durante l'arresto è più probabile che cosa genera l'errore che si sta parlando di , ma per quanto ne so il metodo __del__ non ha modo di sapere che viene chiamato durante l'arresto in particolare. Quindi la correzione proposta per il messaggio non funzionerà. [....]
Tuttavia, perché questo è un messaggio non si può nemmeno trappola dovrebbe essere completamente sicuro di cambiarlo.

Bene, grazie per questo messaggio che non si può intrappolare, molto comodo.Credo che questo sia in qualche modo correlato allo Ignore exceptions printed to stderr in del() - Stack Overflow, sebbene quel post (apparentemente) parli dei metodi personalizzati __del__.

Usando un po 'delle seguenti risorse:

... ho modificato la sceneggiatura, così ho sovraccaricare tutti i possibili gestori che posso, per vedere se ci non è uno spazio da qualche parte dove posso "gestire" questa eccezione quindi non è "ignorata":

import sys 
import atexit 
import signal 
import inspect, pprint 

def signalPIPE_handler(signal, frame): 
    sys.stderr.write('signalPIPE_handler!'+str(sys.exc_info())+'\n') 
    return #sys.exit(0) # just return doesn't exit! 
signal.signal(signal.SIGPIPE, signalPIPE_handler) 

_old_excepthook = sys.excepthook 
def myexcepthook(exctype, value, intraceback): 
    import sys 
    import traceback 
    sys.stderr.write("myexcepthook\n") 
    if exctype == IOError: 
    sys.stderr.write(" IOError intraceback:\n") 
    traceback.print_tb(intraceback) 
    else: 
    _old_excepthook(exctype, value, intraceback) 
sys.excepthook = myexcepthook 

def _trace(frame, event, arg): 
    if event == 'exception': 
    while frame is not None: 
     filename, lineno = frame.f_code.co_filename, frame.f_lineno 
     sys.stderr.write("_trace exc frame: " + filename \ 
     + " " + str(lineno) + " " + str(frame.f_trace) + str(arg) + "\n") 
     if arg[0] == IOError: 
     myexcepthook(arg[0], arg[1], arg[2]) 
     frame = frame.f_back 
    return _trace 
sys.settrace(_trace) 

def exiter(): 
    import sys 
    sys.stderr.write("Exiting\n") 
atexit.register(exiter) 

def main(): 
    teststr = "Hello " * 5 
    try: 
    sys.stdout.write(teststr + "\n") 
    sys.stdout.flush() 
    except IOError: 
    sys.stderr.write("Exc: " + str(sys.exc_info()[0]) + "\n") 
    #sys.exit(0) 


if __name__ == "__main__": 
    main() 

nota la differenza nel modo in cui questo script viene eseguito:

$ python2.7 testprint.py | echo 

signalPIPE_handler!(None, None, None) 
_trace exc frame: testprint.py 44 <function _trace at 0xb748e5dc>(<type 'exceptions.IOError'>, (32, 'Broken pipe'), <traceback object at 0xb748acac>) 
myexcepthook 
IOError intraceback: 
    File "testprint.py", line 44, in main 
    sys.stdout.flush() 
_trace exc frame: testprint.py 51 None(<type 'exceptions.IOError'>, (32, 'Broken pipe'), <traceback object at 0xb748acac>) 
myexcepthook 
IOError intraceback: 
    File "testprint.py", line 44, in main 
    sys.stdout.flush() 
Exc: <type 'exceptions.IOError'> 
Exiting 

$ python3.2 testprint.py | echo 

signalPIPE_handler!(None, None, None) 
_trace exc frame: testprint.py 44 <function _trace at 0xb74247ac>(<class 'IOError'>, (32, 'Broken pipe'), <traceback object at 0xb747393c>) 
myexcepthook 
IOError intraceback: 
    File "testprint.py", line 44, in main 
    sys.stdout.flush() 
_trace exc frame: testprint.py 51 None(<class 'IOError'>, (32, 'Broken pipe'), <traceback object at 0xb747393c>) 
myexcepthook 
IOError intraceback: 
    File "testprint.py", line 44, in main 
    sys.stdout.flush() 
Exc: <class 'IOError'> 
signalPIPE_handler!(None, None, None) 
Exiting 
signalPIPE_handler!(None, None, None) 
Exception IOError: (32, 'Broken pipe') in <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'> ignored 

Nota che signalPIPE_handler corre due volte più in Python 3! Penso che, se in Python ci fosse una sorta di "coda d'eccezione", potrei "sbirciare" dentro e rimuovere gli eventi rimanenti nello signalPIPE_handler, in modo da sopprimere il messaggio Exception ... ignored ... ma non so di alcun tale cosa.

Infine, queste risorse sono belli quando si cerca di eseguire il debug con gdb:

... dal momento che non ho python3-dbg, tutto questo si riduce a un passo attraverso le istruzioni della macchina (layout asm in gdb, quindi Ctrl-X + A), che in realtà non mi dice molto. Ma ecco come innescare il problema in gdb:

In un terminale:

$ mkfifo foo 
$ gdb python3.2 
... 
Reading symbols from /usr/bin/python3.2...(no debugging symbols found)...done. 
(gdb) run testprint.py > foo 
Starting program: /usr/bin/python3.2 testprint.py > foo 

Qui si blocca; in un altro terminale nella stessa diretory fare:

$ echo <foo 

... poi tornare al primo terminale - si dovrebbe vedere:

... 
Starting program: /usr/bin/python3.2 testprint.py > foo 
[Thread debugging using libthread_db enabled] 
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1". 

Program received signal SIGPIPE, Broken pipe. 
0x0012e416 in __kernel_vsyscall() 
(gdb) bt 
#0 0x0012e416 in __kernel_vsyscall() 
#1 0x0013c483 in __write_nocancel() from /lib/i386-linux-gnu/libpthread.so.0 
#2 0x0815b549 in ??() 
#3 0x08170507 in ??() 
#4 0x08175e43 in PyObject_CallMethodObjArgs() 
#5 0x0815df21 in ??() 
#6 0x0815f94e in ??() 
#7 0x0815fb05 in ??() 
#8 0x08170507 in ??() 
#9 0x08175cb1 in _PyObject_CallMethod_SizeT() 
#10 0x08164851 in ??() 
#11 0x080a3a36 in PyEval_EvalFrameEx() 
#12 0x080a3a53 in PyEval_EvalFrameEx() 
#13 0x080a43c8 in PyEval_EvalCodeEx() 
#14 0x080a466f in PyEval_EvalCode() 
#15 0x080c6e9d in PyRun_FileExFlags() 
#16 0x080c70c0 in PyRun_SimpleFileExFlags() 
#17 0x080db537 in Py_Main() 
#18 0x0805deee in main() 
(gdb) finish 
Run till exit from #0 0x0012e416 in __kernel_vsyscall() 
0x0013c483 in __write_nocancel() from /lib/i386-linux-gnu/libpthread.so.0 
... 

Purtroppo, non ho la possibilità di costruire python3 dai sorgenti e esegui il debug ora; quindi spero di ricevere una risposta da qualcuno che lo sa :)

Cheers!

messaggio
3

Questo errore è Python indica che la definizione condotta fornita è rotto, anche se in modo alquanto confuso (vedi http://bugs.python.org/issue11380)

echo realtà non accettare input dallo standard input, in modo che il tubo di ingresso da Python finisce essere chiuso presto L'eccezione extra che stai vedendo (al di fuori del gestore di eccezioni) è quindi dovuta al tentativo implicito di svuotare i flussi standard mentre l'interprete si sta spegnendo. Questo accade al di fuori dell'ambito di qualsiasi codice Python fornito dall'utente, quindi l'interprete scrive l'errore su stderr piuttosto che richiamare la normale elaborazione delle eccezioni.

Se sai che non ti importa dei tubi rotti per il tuo caso d'uso, puoi occuparti di questo caso chiudendo esplicitamente stdout prima della fine del tuo programma. Sarà ancora lamentano il tubo rotto, ma lo farà in un modo che consente di catturare e sopprimere l'eccezione, come al solito:

import sys 

def main(): 
    teststr = "Hello " * 5 
    try: 
    sys.stdout.write(teststr + "\n") 
    sys.stdout.flush() 
    except IOError: 
    sys.stderr.write("Exc: " + str(sys.exc_info()[0]) + "\n") 
    try: 
    sys.stdout.close() 
    except IOError: 
    sys.stderr.write("Exc on close: " + str(sys.exc_info()[0]) + "\n") 

if __name__ == "__main__": 
    main() 

In questa versione, solo i risultati attesi si vede, perché anche il tentativo in chiusura è sufficiente a garantire il flusso è già contrassegnata come chiusa durante l'arresto interprete:

$ python3 testprint.py | echo 

Exc: <class 'BrokenPipeError'> 
Exc on close: <class 'BrokenPipeError'> 
0

Questo è un brutto hack per eliminare il messaggio di errore venga mostrata in caso di stampa su stdout causato un tubo rotto (es perché un processo di cercapersone richiamato come your-program.py | less è stato chiuso senza scorrere fino alla fine dell'output:

try: 
    actual_code() 
except BrokenPipeError: 
    sys.stdout = os.fdopen(1) 
Problemi correlati