2013-05-01 214 views
9

Python中存在已知問題,其中"close failed in file object destructor" when "Broken pipe" happens on stdout - Python tracker Issue 11380;也見於python - Why does my Python3 script balk at piping its output to head or tail (sys module)? - Stack Overflow在Python 3中禁止打印「Exception ... ignored」消息

我想要做的是在Python 2.7和Python 3+中發生此問題時打印出相同的自定義消息。所以,我準備測試腳本,testprint.py並運行它(片段顯示在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 

從上面的鏈接預期,有兩個不同的消息。在Help with a piping error (velocityreviews.com)中,建議使用sys.stdout.flush()強制Python 2註冊IOError而不是該消息;這樣,我們有:

$ 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,越來越近......現在,「忽略」這些例外(或者對於我來說,用自定義錯誤消息替換)的方式,是來處理它們:

Ignore exceptions - comp.lang.python

>有沒有什麼辦法讓[翻譯]忽略例外。
沒有。要麼處理異常,要麼編寫不生成異常的代碼 。

...以及作爲An Introduction to Python - Handling Exceptions筆記,要做到這一點的方法是try/except塊。因此,讓我們試試:

$ 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 

好了,嘗試/除工程,我希望它的Python 2.7 - 但是,Python的3.2如預期,仍然生成Exception ... ignored消息兩個手柄!有什麼問題 - 對於Python 3來說不是「except IOError」嗎?但它一定是 - 否則它不會打印定製的「Exc:...」消息!

所以 - 這裏有什麼問題,爲什麼仍然在Python 3中打印Exception ... ignored,即使我正在處理異常?更重要的是,我如何處理它,以便 確實不會而不是

+0

它適用於頭部,尾部,更少,更多和獨特的我,因此它看起來像是特別與回聲的交互中存在實際的錯誤。 「解析異常」部分實際上是在解釋器關閉期間發生的,當它試圖再次刷新標準流時。 – ncoghlan 2013-09-23 07:22:08

回答

3

剛想這一些說明 - 問題仍然沒有解決......第一:在PyErr_WriteUnraisable,這是 從許多情況下,包括__del__方法稱爲生成

Issue 6294: Improve shutdown exception ignored message - Python tracker

此錯誤信息。在關機過程中調用的__del__方法 很可能是您生成的錯誤 說的,但據我所知,__del__方法沒有辦法知道它在關機過程中被調用。因此建議修復該消息的 將無法​​正常工作。 [....]
但是,因爲這是一條消息,你甚至不能陷入它 應該是完全安全的改變它。

好吧,謝謝你這個信息,你不能陷阱,非常方便。我相信這與Ignore exceptions printed to stderr in del() - Stack Overflow有某種關係,儘管那篇文章(顯然)討論了自定義方法__del__

使用位以下資源:

...我修改劇本,所以我重載所有可能的處理程序,我可以,看是否有不是我可以「處理」該異常的空間,因此它不被「忽略」:

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

注意如何運行此腳本的區別:

$ 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 

注意signalPIPE_handler運行在Python 3的兩倍以上!我認爲,如果在Python中有某種「異常隊列」,我可以「偷看」它,並刪除signalPIPE_handler中的剩餘事件,以抑制Exception ... ignored消息......但我不知道任何這樣的事情。

最後,這些資源是要調試的時候尼斯gdb

...因爲我沒有做python3-dbg,這一切都降低了步進通過機器指令(layout asm,gdb,然後Ctrl-X + A),這並沒有真正地告訴我很多。但在這裏是如何觸發gdb問題:

在一個終端:

$ 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 

這將阻止;在同diretory另一端做:

$ echo <foo 

...然後返回到第一終端 - 你應該看到:

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

不幸的是,我沒有從源代碼編譯Python3的可能性並現在進行調試;所以我希望有人知道的答案:)

乾杯!

3

此錯誤消息是Python的指示提供的管道的定義是在有些混亂的方式破裂,儘管(參見http://bugs.python.org/issue11380

回波實際上不接受通過stdin輸入,所以從Python的輸入管結束儘早關閉。您看到的額外異常(異常處理程序外)是由於在解釋程序關閉時隱式嘗試刷新標準流。這發生在任何用戶提供的Python代碼的範圍之外,所以解釋器只是將錯誤寫入stderr而不是調用正常的異常處理。

如果你知道你不關心你的用例中的破損管道,​​你可以在程序結束之前通過明確地關閉stdout來處理這種情況。它仍然會抱怨破裂的管道,但它會做的方式,讓你趕上和抑制異常如常:

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

在這個版本中,只有預期的輸出所看到的,因爲即使是嘗試在關閉它足以保證該流已被標記解釋器關閉期間作爲關閉:

$ python3 testprint.py | echo 

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

這是一個非常醜陋的黑客從在殼體打印到標準輸出被示出抑制該錯誤消息引起斷裂的管道(例如因爲像your-program.py | less這樣被調用的尋呼機進程被退出而沒有滾動到輸出的底部:

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