如何使“sys.excepthook is missing"静音错误? [英] How to silence "sys.excepthook is missing" error?

查看:20
本文介绍了如何使“sys.excepthook is missing"静音错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:我没有尝试在 Windows 下或使用 2.7.3 以外的 Python 版本重现下面描述的问题.

NB: I have not attempted to reproduce the problem described below under Windows, or with versions of Python other than 2.7.3.

引出问题的最可靠方法是通过 :(在 bash 下)通过管道传输以下测试脚本的输出:

The most reliable way to elicit the problem in question is to pipe the output of the following test script through : (under bash):

try:
    for n in range(20):
        print n
except:
    pass

即:

% python testscript.py | :
close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr

我的问题是:

如何修改上面的测试脚本来避免脚本运行时出现的错误信息(在Unix/bash下)?

How can I modify the test script above to avoid the error message when the script is run as shown (under Unix/bash)?

(如测试脚本所示,错误不能用 try-except 捕获.)

(As the test script shows, the error cannot be trapped with a try-except.)

诚然,上面的示例是高度人为的,但是当我的脚本的输出通过某些 3rd 方软件进行管道传输时,我有时会遇到同样的问题.

The example above is, admittedly, highly artificial, but I'm running into the same problem sometimes when the output of a script of mine is piped through some 3rd party software.

该错误消息当然是无害的,但它让最终用户感到不安,所以我想让它保持沉默.

The error message is certainly harmless, but it is disconcerting to end-users, so I would like to silence it.

以下脚本与上面的原始脚本的不同之处仅在于它重新定义了 sys.excepthook,其行为与上面给出的完全相同.

The following script, which differs from the original one above only in that it redefines sys.excepthook, behaves exactly like the one given above.

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

推荐答案

如何修改上面的测试脚本来避免脚本运行时出现的错误信息(在Unix/bash下)?

您需要阻止脚本向标准输出写入任何内容.这意味着删除任何 print 语句和任何对 sys.stdout.write 的使用,以及调用这些语句的任何代码.

You will need to prevent the script from writing anything to standard output. That means removing any print statements and any use of sys.stdout.write, as well as any code that calls those.

发生这种情况的原因是,您将 Python 脚本中的非零输出量传输到从未从标准输入中读取的内容.这不是 : 命令独有的;您可以通过管道传输到任何不读取标准输入的命令来获得相同的结果,例如

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 testscript.py | cd .

或者举个更简单的例子,考虑一个脚本 printer.py 只包含

Or for a simpler example, consider a script printer.py containing nothing more than

print 'abcde'

然后

python printer.py | python printer.py

会产生同样的错误.

当您将一个程序的输出通过管道传输到另一个程序时,写入程序产生的输出将备份在缓冲区中,并等待读取程序从缓冲区请求该数据.只要缓冲区不为空,任何关闭写入文件对象的尝试都会失败并显示错误.这是您看到的消息的根本原因.

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.

触发错误的具体代码在 Python 的 C 语言实现中,这就解释了为什么不能用 try/except 块捕获它:它在您的脚本内容完成处理后运行.基本上,当 Python 自行关闭时,它会尝试关闭 stdout,但失败了,因为仍有缓冲输出等待读取.所以 Python 尝试像往常一样报告这个错误,但是 sys.excepthook 已经作为完成过程的一部分被删除,所以失败了.Python 然后尝试向 sys.stderr 打印一条消息,但该消息已被释放,因此再次失败.您在屏幕上看到消息的原因是 Python 代码确实包含一个应急fprintf 以将一些输出直接写出到文件指针,即使 Python 的输出对象不存在.

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/except block: 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.excepthook has 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 fprintf to write out some output to the file pointer directly, even if Python's output object doesn't exist.

对于这个过程的细节感兴趣的人,让我们看看Python解释器的关闭序列,它在pythonrun.cPy_Finalize 函数.

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_Finalize function of pythonrun.c.

  1. 在调用退出钩子并关闭线程后,最终代码调用 PyImport_Cleanup 完成并释放所有导入的模块.此函数执行的倒数第二个任务是删除sys 模块,主要包括调用_PyModule_Clear 清除模块字典中的所有条目 - 特别包括标准流对象(Python 对象),例如 stdoutstderr.
  2. 当一个值从字典中删除或被一个新值替换时,它的引用计数减少 使用<代码>Py_DECREF 宏.引用计数达到零的对象有资格被释放.由于 sys 模块保存对标准流对象的最后剩余引用,当这些引用被 _PyModule_Clear 取消设置时,它们就可以被释放了.1
  3. Python 文件对象的释放由 完成fileobject.c 中的 file_dealloc 函数.第一个 调用 Python 文件对象的 close方法使用恰当命名的close_the_file 函数:

  1. After invoking exit hooks and shutting down threads, the finalization code calls PyImport_Cleanup to finalize and deallocate all imported modules. The next-to-last task performed by this function is removing the sys module, which mainly consists of calling _PyModule_Clear to clear all the entries in the module's dictionary - including, in particular, the standard stream objects (the Python objects) such as stdout and stderr.
  2. When a value is removed from a dictionary or replaced by a new value, its reference count is decremented using the Py_DECREF macro. Objects whose reference count reaches zero become eligible for deallocation. Since the sys module 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
  3. Deallocation of a Python file object is accomplished by the file_dealloc function in fileobject.c. This first invokes the Python file object's close method using the aptly-named close_the_file function:

ret = close_the_file(f);

对于标准文件对象,close_the_file(f) 委托给 C fclose 函数,如果仍有数据要写入文件指针,它会设置错误条件.file_dealloc 然后检查该错误情况并打印您看到的第一条消息:

For a standard file object, close_the_file(f) delegates to the C fclose function, which sets an error condition if there is still data to be written to the file pointer. file_dealloc then checks for that error condition and prints the first message you see:

if (!ret) {
    PySys_WriteStderr("close failed in file object destructor:
");
    PyErr_Print();
}
else {
    Py_DECREF(ret);
}

  • 打印该消息后,Python 会尝试使用 PyErr_Print.委托给 PyErr_PrintEx,并且作为其功能的一部分,PyErr_PrintEx 尝试从 sys.excepthook 访问 Python 异常打印机.

  • After printing that message, Python then attempts to display the exception using PyErr_Print. That delegates to PyErr_PrintEx, and as part of its functionality, PyErr_PrintEx attempts to access the Python exception printer from sys.excepthook.

    hook = PySys_GetObject("excepthook");
    

    如果在 Python 程序的正常过程中这样做就好了,但是在这种情况下,sys.excepthook 已经被清除了.2 Python 会检查这个错误条件并打印第二条消息作为通知.

    This would be fine if done in the normal course of a Python program, but in this situation, sys.excepthook has already been cleared.2 Python 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
    ");
        PyErr_Display(exception, v, tb);
    }
    

  • 在通知我们缺少 excepthook 之后,Python 会回退到使用 PyErr_Display,这是显示堆栈跟踪的默认方法.这个函数所做的第一件事就是尝试访问sys.stderr.

  • After notifying us about the missing excepthook, Python then falls back to printing the exception info using PyErr_Display, which is the default method for displaying a stack trace. The very first thing this function does is try to access sys.stderr.

    PyObject *f = PySys_GetObject("stderr");
    

    在这种情况下,这不起作用,因为 sys.stderr 已经被清除并且无法访问.3 所以代码调用了 fprintf 直接将第三条消息发送到 C 标准错误流.

    In this case, that doesn't work because sys.stderr has already been cleared and is inaccessible.3 So the code invokes fprintf directly to send the third message to the C standard error stream.

    if (f == NULL || f == Py_None)
        fprintf(stderr, "lost sys.stderr
    ");
    

  • 有趣的是,Python 3.4+ 中的行为略有不同,因为现在的最终确定过程 在清除内置模块之前明确刷新标准输出和错误流.这样,如果您有数据等待写入,您会收到一个错误,明确表示该情况,而不是正常完成过程中的意外"失败.另外,如果你运行

    Interestingly, the behavior is a little different in Python 3.4+ because the finalization procedure now explicitly flushes the standard output and error streams before 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 printer.py | python printer.py
    

    使用 Python 3.4(当然在 print 语句上加上括号之后),您根本不会收到任何错误.我想 Python 的第二次调用可能出于某种原因消耗了标准输入,但这是一个完全不同的问题.

    using Python 3.4 (after putting parentheses on the print statement 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.

    1实际上,那是谎言.Python 的导入机制 缓存每个导入模块字典的副本,直到 _PyImport_Fini 运行,稍后在 Py_Finalize<的实现中/code>那是对标准流对象的最后引用消失的时候.一旦引用计数达到零,Py_DECREF 立即释放对象.但对于主要答案而言,重要的是引用从 sys 模块的字典中删除,然后在稍后释放.

    1Actually, that's a lie. Python's import mechanism caches a copy of each imported module's dictionary, which is not released until _PyImport_Fini runs, later in the implementation of Py_Finalize, and that's when the last references to the standard stream objects disappear. Once the reference count reaches zero, Py_DECREF deallocates the objects immediately. But all that matters for the main answer is that the references are removed from the sys module's dictionary and then deallocated sometime later.

    2同样,这是因为 sys 模块的字典在真正释放任何东西之前被完全清除,这要归功于属性缓存机制.您可以使用 -vv 选项运行 Python,以在收到有关关闭文件指针的错误消息之前查看未设置的所有模块属性.

    2Again, this is because the sys module's dictionary is cleared completely before anything is really deallocated, thanks to the attribute caching mechanism. You can run Python with the -vv option to see all the module's attributes being unset before you get the error message about closing the file pointer.

    3除非您了解之前脚注中提到的属性缓存机制,否则此特定行为是唯一没有意义的部分.

    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.

    这篇关于如何使“sys.excepthook is missing"静音错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆