Python “如何沉默”;sys.EXPEPTHOOK缺失“1;错误?

Python “如何沉默”;sys.EXPEPTHOOK缺失“1;错误?,python,exception,io,Python,Exception,Io,注意:我没有尝试在Windows下或在2.7.3以外的Python版本中重现下面描述的问题 引出问题的最可靠方法是通过管道将以下测试脚本的输出传输到:(在bash下): 即: 我的问题是: 当脚本如图所示运行时(在Unix/bash下),如何修改上面的测试脚本以避免出现错误消息 (如测试脚本所示,除了,不能使用尝试捕获错误) 诚然,上面的例子是高度人工的,但当我的脚本输出通过第三方软件传输时,我有时会遇到同样的问题 错误消息当然是无害的,但它会让最终用户感到不安,所以我想让它安静下来 编辑:以下

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

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

即:

我的问题是:

当脚本如图所示运行时(在Unix/
bash
下),如何修改上面的测试脚本以避免出现错误消息

(如测试脚本所示,除了,不能使用
尝试捕获错误)

诚然,上面的例子是高度人工的,但当我的脚本输出通过第三方软件传输时,我有时会遇到同样的问题

错误消息当然是无害的,但它会让最终用户感到不安,所以我想让它安静下来

编辑:以下脚本与上面的原始脚本不同,只是它重新定义了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

在您的程序中,抛出一个无法使用try/except块捕获的异常。要抓住他,重写函数sys.excepthook

import sys
sys.excepthook = lambda *args: None
发件人:

sys.excepthook(类型、值、回溯)

当引发异常并取消捕获时,解释器调用 带有三个参数的sys.excepthook,异常类,异常 实例和一个回溯对象。在本周的互动会议中 在控件返回到提示符之前发生;在Python中 程序这发生在程序退出之前。处理 这样的顶级异常可以通过分配另一个异常来定制 sys.excepthook的三参数函数

示例:

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
当脚本如图所示运行时(在Unix/
bash
下),如何修改上面的测试脚本以避免出现错误消息

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

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

python testscript.py | cd .

或者对于一个更简单的例子,考虑一个脚本<代码>打印机.Py < /> > >包含

print 'abcde'
然后

将产生相同的错误

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

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

技术细节

对于感兴趣的这个过程的细节,让我们看看Python解释器的停机顺序,它是在<代码> python Run.c<代码> >

中实现的。
  • 在调用退出钩子并关闭线程之后,终结代码将调用以终结和取消分配所有导入的模块。该函数执行的倒数第二个任务是,它主要包括调用以清除模块字典中的所有条目,尤其包括标准流对象(Python对象),例如
    stdout
    stderr
  • 当从字典中删除某个值或将其替换为新值时,请使用。引用计数达到零的对象将有资格取消分配。由于
    sys
    模块保存标准流对象的最后剩余引用,因此当
    \u PyModule\u Clear
    取消设置这些引用时,它们就可以被释放。1
  • Python文件对象的解除分配是通过在
    fileobject.c
    中完成的。首先使用恰当命名的:

    对于标准文件对象,
    关闭文件(f)
    ,如果仍有数据要写入文件指针,则会设置错误条件<代码>文件\u dealoc然后检查错误情况并打印您看到的第一条消息:

    if (!ret) {
        PySys_WriteStderr("close failed in file object destructor:\n");
        PyErr_Print();
    }
    else {
        Py_DECREF(ret);
    }
    
  • 在打印该消息之后,Python然后尝试使用显示异常。作为其功能的一部分,
    PyErr_PrintEx
    尝试从
    sys.excepthook
    访问Python异常打印机

    hook = PySys_GetObject("excepthook");
    
    如果在Python程序的正常过程中执行此操作,则没有问题,但在这种情况下,
    sys.excepthook
    已被清除。2 Python检查此错误情况并将第二条消息打印为通知

    if (hook && hook != Py_None) {
        ...
    } else {
        PySys_WriteStderr("sys.excepthook is missing\n");
        PyErr_Display(exception, v, tb);
    }
    
  • 在通知我们缺少的
    excepthook
    之后,Python会返回到使用打印异常信息,这是显示异常的默认方法
    ret = close_the_file(f);
    
    if (!ret) {
        PySys_WriteStderr("close failed in file object destructor:\n");
        PyErr_Print();
    }
    else {
        Py_DECREF(ret);
    }
    
    hook = PySys_GetObject("excepthook");
    
    if (hook && hook != Py_None) {
        ...
    } else {
        PySys_WriteStderr("sys.excepthook is missing\n");
        PyErr_Display(exception, v, tb);
    }
    
    PyObject *f = PySys_GetObject("stderr");
    
    if (f == NULL || f == Py_None)
        fprintf(stderr, "lost sys.stderr\n");
    
    python printer.py | python printer.py
    
    --- 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
    
    $ python testscript.py  | :
    $
    
    print "Good Bye"
    
    print ("Good Bye")