Suppressing printout of “Exception … ignored” message in Python 3
在python中有一个已知的问题,即当stdout上发生"breakpipe"时,"close failed in file object destructor"(文件对象析构函数关闭失败)-python tracker问题11380;在python中也有类似问题-为什么我的python3脚本在将其输出管道输送到head或tail(sys模块)时犹豫不决?-堆栈溢出。
我想做的是,当这个问题发生时,在python 2.7和python 3+中打印出相同的自定义消息。因此,我准备了一个测试脚本,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | $ cat > testprint.py <<"EOF" import sys def main(): teststr ="Hello" * 5 sys.stdout.write(teststr +" ") 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 |
正如上面的链接所预期的那样,有两条不同的消息。为了帮助解决管道错误(velocityreviews.com),建议使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | $ cat > testprint.py <<"EOF" import sys def main(): teststr ="Hello" * 5 sys.stdout.write(teststr +" ") 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 |
好吧,再近一点…现在,"忽略"这些异常(或者在我的例子中,用自定义错误消息替换)的方法是处理它们:
忽略异常-comp.lang.python
> Is there any way to make [interpreter] ignore exceptions.
Nope. Either handle the exceptions or write code that doesn't
generate exceptions.
…作为python处理异常说明的介绍,实现这一点的方法是一个try/except块。那么让我们试试看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | $ cat > testprint.py <<"EOF" import sys def main(): teststr ="Hello" * 5 try: sys.stdout.write(teststr +" ") sys.stdout.flush() except IOError: sys.stderr.write("Exc:" + str(sys.exc_info()[0]) +" ") 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 |
好的,所以试一下/except可以像我在python 2.7中预期的那样工作-但是,python 3.2都能按预期处理,并且仍然会生成一条
那么-这里的问题是什么?为什么即使我正在处理异常,
此错误消息是python,指示所提供的管道定义已被破坏,尽管方式有些混乱(请参阅http://bugs.python.org/issue11380)。
echo实际上不接受通过stdin输入的内容,所以来自python的输入管道最终会提前关闭。您看到的额外异常(异常处理程序之外)是由于解释器关闭时隐式地尝试刷新标准流。这发生在任何用户提供的python代码范围之外,因此解释器只将错误写入
如果你知道你不关心你的用例的管道破裂,你可以在程序结束之前通过明确关闭
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import sys def main(): teststr ="Hello" * 5 try: sys.stdout.write(teststr +" ") sys.stdout.flush() except IOError: sys.stderr.write("Exc:" + str(sys.exc_info()[0]) +" ") try: sys.stdout.close() except IOError: sys.stderr.write("Exc on close:" + str(sys.exc_info()[0]) +" ") if __name__ =="__main__": main() |
在此版本中,只看到预期的输出,因为即使尝试关闭它也足以确保在解释器关闭期间流已标记为关闭:
1 2 3 4 | $ python3 testprint.py | echo Exc: <class 'BrokenPipeError'> Exc on close: <class 'BrokenPipeError'> |
只是关于这个问题的一些笔记-问题还没有解决…第一:
问题6294:改进关闭异常忽略消息-python tracker
This error message is generated in PyErr_WriteUnraisable, which is
called from many contexts, including __del__ methods. A __del__ method
called during shutdown is most likely what is generating the error you
are speaking of, but as far as I know the __del__ method has no way to
know that it is being called during shutdown in particular. So the
proposed fix to the message won't work. [....]
However, because this is a message you can't even trap it
should be completely safe to change it.
好吧,谢谢你这条信息,你不能陷阱,非常方便。我相信这在某种程度上与忽略del()堆栈溢出中打印到stderr的异常有关,尽管这篇文章(显然)谈到了定制的
使用以下资源:
- 使用python的sys.settrace()来获得乐趣和利润回忆:属于或关于回忆的
- python全局异常处理-堆栈溢出
…我修改了脚本,因此我重载了所有可能的处理程序,以查看是否在某个地方没有可以"处理"此异常的空间,从而不被"忽略":
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | import sys import atexit import signal import inspect, pprint def signalPIPE_handler(signal, frame): sys.stderr.write('signalPIPE_handler!'+str(sys.exc_info())+' ') 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 ") if exctype == IOError: sys.stderr.write(" IOError intraceback: ") 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) +" ") 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 ") atexit.register(exiter) def main(): teststr ="Hello" * 5 try: sys.stdout.write(teststr +" ") sys.stdout.flush() except IOError: sys.stderr.write("Exc:" + str(sys.exc_info()[0]) +" ") #sys.exit(0) if __name__ =="__main__": main() |
请注意此脚本运行方式的不同:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | $ 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 |
注意,在python 3中,
最后,在尝试使用
- C-GDB-管道堆栈溢出调试
- Linux-在指定的可执行文件外使用gdb单步汇编代码会导致错误"找不到当前函数的边界"-堆栈溢出
…由于我没有
在一个终端中:
1 2 3 4 5 6 | $ 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 |
在这里,它将阻塞;在同一个目录下的另一个终端中,它将阻塞:
1 | $ echo <foo |
…然后返回到第一个终端-您应该看到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ... 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并对其进行调试;所以我希望知道
干杯!
如果打印到stdout导致管道断开(例如,因为像
1 2 3 4 | try: actual_code() except BrokenPipeError: sys.stdout = os.fdopen(1) |