2013-05-01 36 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)