Python 如何消除“sys.excepthook is missing”错误?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12790328/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to silence "sys.excepthook is missing" error?
提问by kjo
NB: I have not attempted to reproduce the problem described below under Windows, or with versions of Python other than 2.7.3.
注意:我没有尝试在 Windows 下或使用 2.7.3 以外的 Python 版本重现下面描述的问题。
The most reliable way to elicit the problem in question is to pipe the output of the following test script through :(under bash):
引出相关问题的最可靠方法是将以下测试脚本的输出通过管道:(在 下bash):
try:
for n in range(20):
print n
except:
pass
I.e.:
IE:
% python testscript.py | :
close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr
My question is:
我的问题是:
How can I modify the test script aboveto avoid the error message when the script is run as shown (under Unix/
bash)?
我如何修改上面的测试脚本以避免在脚本运行时出现错误消息(在 Unix/ 下
bash)?
(As the test script shows, the error cannot be trapped with a try-except.)
(正如测试脚本所示,错误不能用try-except.)
The example above is, admittedly, highly artificial, but I'm running into the same problem sometimeswhen the output of a script of mine is piped through some 3rd party software.
诚然,上面的示例是高度人为的,但是当我的脚本的输出通过某些 3rd 方软件进行管道传输时,有时我会遇到同样的问题。
The error message is certainly harmless, but it is disconcerting to end-users, so I would like to silence it.
错误消息当然是无害的,但它让最终用户感到不安,所以我想让它保持沉默。
EDIT: The following script, which differs from the original one above only in that it redefines sys.excepthook, behaves exactly like the one given above.
编辑:以下脚本与上面的原始脚本的不同之处仅在于它重新定义了 sys.excepthook,其行为与上面给出的完全相同。
import sys
STDERR = sys.stderr
def excepthook(*args):
print >> STDERR, 'caught'
print >> STDERR, args
sys.excepthook = excepthook
try:
for n in range(20):
print n
except:
pass
采纳答案by David Z
How can I modify the test script above to avoid the error message when the script is run as shown (under Unix/
bash)?
当脚本按所示运行时(在 Unix/ 下
bash),如何修改上面的测试脚本以避免出现错误消息?
You will need to prevent the script from writing anything to standard output. That means removing any printstatements and any use of sys.stdout.write, as well as any code that calls those.
您需要防止脚本将任何内容写入标准输出。这意味着删除任何print语句和任何使用sys.stdout.write,以及调用它们的任何代码。
The reason this is happening is that you're piping a nonzero amount of output from your Python script to something which never reads from standard input. This is not unique to the :command; you can get the same result by piping to any command which doesn't read standard input, such as
发生这种情况的原因是您将 Python 脚本中的非零输出量传输到从未从标准输入中读取的内容。这不是:命令独有的;您可以通过管道传输到任何不读取标准输入的命令来获得相同的结果,例如
python testscript.py | cd .
Or for a simpler example, consider a script printer.pycontaining nothing more than
或者举一个更简单的例子,考虑一个只printer.py包含以下内容的脚本
print 'abcde'
Then
然后
python printer.py | python printer.py
will produce the same error.
会产生同样的错误。
When you pipe the output of one program into another, the output produced by the writing program gets backed up in a buffer, and waits for the reading program to request that data from the buffer. As long as the buffer is nonempty, any attempt to close the writing file object is supposed to fail with an error. This is the root cause of the messages you're seeing.
当您将一个程序的输出通过管道传输到另一个程序时,写入程序产生的输出将备份在缓冲区中,并等待读取程序从缓冲区请求该数据。只要缓冲区不为空,任何关闭写入文件对象的尝试都会失败并显示错误。这是您看到的消息的根本原因。
The specific code that triggers the error is in the C language implementation of Python, which explains why you can't catch it with a try/exceptblock: it runs after the contents of your script has finished processing. Basically, while Python is shutting itself down, it attempts to close stdout, but that fails because there is still buffered output waiting to be read. So Python tries to report this error as it would normally, but sys.excepthookhas already been removed as part of the finalization procedure, so that fails. Python then tries to print a message to sys.stderr, but that has already been deallocated so again, it fails. The reason you see the messages on the screen is that the Python code does contain a contingency fprintfto write out some output to the file pointer directly, even if Python's output object doesn't exist.
触发错误的具体代码在 Python 的 C 语言实现中,这就解释了为什么不能用try/except块捕获它:它在脚本内容处理完毕后运行。基本上,当 Python 自行关闭时,它会尝试 close stdout,但失败了,因为仍有缓冲输出等待读取。因此,Python 尝试像往常一样报告此错误,但sys.excepthook已作为完成过程的一部分被删除,因此失败。然后 Python 尝试将消息打印到sys.stderr,但该消息已被释放,因此再次失败。您在屏幕上看到消息的原因是 Python 代码确实包含一个意外事件fprintf将一些输出直接写出到文件指针,即使 Python 的输出对象不存在。
Technical details
技术细节
For those interested in the details of this procedure, let's take a look at the Python interpreter's shutdown sequence, which is implemented in the Py_Finalizefunctionof pythonrun.c.
对于那些有兴趣在此过程中的细节,让我们来看看Python解释器的关闭序列,这是在实现Py_Finalize功能的pythonrun.c。
- After invoking exit hooks and shutting down threads, the finalization code calls
PyImport_Cleanupto finalize and deallocate all imported modules. The next-to-last task performed by this function is removing thesysmodule, which mainly consists of calling_PyModule_Clearto clear all the entries in the module's dictionary - including, in particular, the standard stream objects (the Python objects) such asstdoutandstderr. - When a value is removed from a dictionary or replaced by a new value, its reference count is decrementedusing the
Py_DECREFmacro. Objects whose reference count reaches zero become eligible for deallocation. Since thesysmodule holds the last remaining references to the standard stream objects, when those references are unset by_PyModule_Clear, they are then ready to be deallocated.1 Deallocation of a Python file object is accomplished by the
file_deallocfunctioninfileobject.c. This first invokes the Python file object'sclosemethodusing the aptly-namedclose_the_filefunction:ret = close_the_file(f);For a standard file object,
close_the_file(f)delegates to the Cfclosefunction, which sets an error condition if there is still data to be written to the file pointer.file_deallocthen checks for that error condition and prints the first message you see:if (!ret) { PySys_WriteStderr("close failed in file object destructor:\n"); PyErr_Print(); } else { Py_DECREF(ret); }After printing that message, Python then attempts to display the exception using
PyErr_Print. That delegates toPyErr_PrintEx, and as part of its functionality,PyErr_PrintExattempts to access the Python exception printer fromsys.excepthook.hook = PySys_GetObject("excepthook");This would be fine if done in the normal course of a Python program, but in this situation,
sys.excepthookhas already been cleared.2Python checks for this error condition and prints the second message as a notification.if (hook && hook != Py_None) { ... } else { PySys_WriteStderr("sys.excepthook is missing\n"); PyErr_Display(exception, v, tb); }After notifying us about the missing
excepthook, Python then falls back to printing the exception info usingPyErr_Display, which is the default method for displaying a stack trace. The very first thing this function does is try to accesssys.stderr.PyObject *f = PySys_GetObject("stderr");In this case, that doesn't work because
sys.stderrhas already been cleared and is inaccessible.3So the code invokesfprintfdirectly to send the third message to the C standard error stream.if (f == NULL || f == Py_None) fprintf(stderr, "lost sys.stderr\n");
- 在调用退出钩子并关闭线程后,终结代码调用
PyImport_Cleanup以终结和释放所有导入的模块。此函数执行的倒数第二个任务是删除sys模块,它主要包括调用_PyModule_Clear以清除模块字典中的所有条目 - 特别包括标准流对象(Python 对象),例如stdout和stderr。 - 当值是从字典移除或由一个新的值代替,其引用计数被递减使用的
Py_DECREF宏。引用计数达到零的对象有资格被释放。由于sys模块保存了对标准流对象的最后剩余引用,当这些引用被取消设置时_PyModule_Clear,它们就准备好被释放了。1 Python文件对象的重新分配由实现的
file_dealloc功能在fileobject.c。这首先使用 aptly-named函数调用 Python 文件对象的close方法:close_the_fileret = close_the_file(f);对于标准文件对象,
close_the_file(f)委托给 Cfclose函数,如果仍有数据要写入文件指针,它会设置错误条件。file_dealloc然后检查该错误情况并打印您看到的第一条消息:if (!ret) { PySys_WriteStderr("close failed in file object destructor:\n"); PyErr_Print(); } else { Py_DECREF(ret); }打印该消息后,Python 会尝试使用
PyErr_Print. 这委托给PyErr_PrintEx,并且作为其功能的一部分,PyErr_PrintEx尝试从 访问 Python 异常打印机sys.excepthook。hook = PySys_GetObject("excepthook");如果在 Python 程序的正常过程中完成,这会很好,但在这种情况下,
sys.excepthook已经清除了。2Python 检查此错误情况并打印第二条消息作为通知。if (hook && hook != Py_None) { ... } else { PySys_WriteStderr("sys.excepthook is missing\n"); PyErr_Display(exception, v, tb); }在通知我们丢失的 之后
excepthook,Python 然后回退到使用 打印异常信息PyErr_Display,这是显示堆栈跟踪的默认方法。这个函数所做的第一件事就是尝试访问sys.stderr.PyObject *f = PySys_GetObject("stderr");在这种情况下,这不起作用,因为
sys.stderr已经被清除并且无法访问。3所以代码fprintf直接调用将第三条消息发送到 C 标准错误流。if (f == NULL || f == Py_None) fprintf(stderr, "lost sys.stderr\n");
Interestingly, the behavior is a little different in Python 3.4+ because the finalization procedure now explicitly flushes the standard output and error streamsbefore builtin modules are cleared. This way, if you have data waiting to be written, you get an error that explicitly signals that condition, rather than an "accidental" failure in the normal finalization procedure. Also, if you run
有趣的是,Python 3.4+ 中的行为略有不同,因为终结过程现在在清除内置模块之前显式刷新标准输出和错误流。这样,如果您有数据等待写入,您会收到一个错误,明确表示该情况,而不是正常完成过程中的“意外”失败。另外,如果你跑
python printer.py | python printer.py
using Python 3.4 (after putting parentheses on the printstatement of course), you don't get any error at all. I suppose the second invocation of Python may be consuming standard input for some reason, but that's a whole separate issue.
使用 Python 3.4(print当然在语句上加上括号之后),您根本不会收到任何错误。我想 Python 的第二次调用可能出于某种原因消耗了标准输入,但这是一个完全不同的问题。
1Actually, that's a lie. Python's import mechanism caches a copy of each imported module's dictionary, which is not released until _PyImport_Finiruns, later in the implementation of Py_Finalize, and that'swhen the last references to the standard stream objects disappear. Once the reference count reaches zero, Py_DECREFdeallocates the objects immediately. But all that matters for the main answer is that the references are removed from the sysmodule's dictionary and then deallocated sometime later.
1其实,那是谎言。Python 的导入机制缓存了每个导入模块的字典的副本,该副本在_PyImport_Fini运行之前不会被释放,稍后在 的实现中Py_Finalize,那是对标准流对象的最后引用消失的时候。一旦引用计数达到零,立即Py_DECREF释放对象。但是对于主要答案而言,重要的是引用从模块的字典中删除,然后在稍后的某个时间释放。sys
2Again, this is because the sysmodule's dictionary is cleared completely before anything is really deallocated, thanks to the attribute caching mechanism. You can run Python with the -vvoption to see all the module's attributes being unset before you get the error message about closing the file pointer.
2同样,这是因为在sys真正释放任何内容之前,模块的字典已被完全清除,这要归功于属性缓存机制。-vv在收到有关关闭文件指针的错误消息之前,您可以运行 Python 并选择查看未设置的所有模块属性。
3This particular piece of behavior is the only part that doesn't make sense unless you know about the attribute caching mechanism mentioned in previous footnotes.
3除非您了解前面脚注中提到的属性缓存机制,否则此特定行为是唯一没有意义的部分。
回答by defuz
In your program throws an exception that can not be caught using try/except block. To catch him, override function sys.excepthook:
在您的程序中抛出一个无法使用 try/except 块捕获的异常。要抓住他,请覆盖函数sys.excepthook:
import sys
sys.excepthook = lambda *args: None
From documentation:
从文档:
sys.excepthook(type, value, traceback)
When an exception is raised and uncaught, the interpreter calls sys.excepthook with three arguments, the exception class, exception instance, and a traceback object. In an interactive session this happens just before control is returned to the prompt; in a Python program this happens just before the program exits. The handling of such top-level exceptions can be customized by assigning another three-argument function to sys.excepthook.
sys.excepthook(类型、值、回溯)
当异常被引发并未被捕获时,解释器会使用三个参数调用 sys.excepthook,异常类、异常实例和回溯对象。在交互式会话中,这发生在控制返回到提示之前;在 Python 程序中,这发生在程序退出之前。可以通过为 sys.excepthook 分配另一个三参数函数来自定义此类顶级异常的处理。
Illustrative example:
说明性示例:
import sys
import logging
def log_uncaught_exceptions(exception_type, exception, tb):
logging.critical(''.join(traceback.format_tb(tb)))
logging.critical('{0}: {1}'.format(exception_type, exception))
sys.excepthook = log_uncaught_exceptions
回答by Andrew
I ran into this sort of issue myself today and went looking for an answer. I think a simple workaround here is to ensure you flush stdio first, so python blocks instead of failing during script shutdown. For example:
我今天自己也遇到了这种问题,然后就去找答案了。我认为这里的一个简单解决方法是确保您首先刷新 stdio,因此 python 会阻塞而不是在脚本关闭期间失败。例如:
--- a/testscript.py
+++ b/testscript.py
@@ -9,5 +9,6 @@ sys.excepthook = excepthook
try:
for n in range(20):
print n
+ sys.stdout.flush()
except:
pass
Then with this script nothing happens, as the exception (IOError: [Errno 32] Broken pipe) is suppressed by the try...except.
然后这个脚本什么也没有发生,因为异常 (IOError: [Errno 32] Broken pipe) 被 try...except 抑制了。
$ python testscript.py | :
$
回答by Stephen Mann
I realize that this is an old question, but I found it in a Google search for the error. In my case it was a coding error. One of my last statements was:
我意识到这是一个老问题,但我在谷歌搜索错误中找到了它。就我而言,这是一个编码错误。我最后的发言之一是:
print "Good Bye"
The solution was simply fixing the syntax to:
解决方案只是将语法固定为:
print ("Good Bye")
[Raspberry Pi Zero, Python 2.7.9]
[树莓派零,Python 2.7.9]

