栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

如何消除“ sys.excepthook丢失”错误?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何消除“ sys.excepthook丢失”错误?

如图所示(在Unix /下

bash
)运行脚本时,如何修改上面的测试脚本以避免错误消息?

您将需要防止脚本将任何内容写入标准输出。这意味着删除所有

print
语句和对的任何使用
sys.stdout.write
以及调用这些语句的任何代码。

发生这种情况的原因是,您正在将来自Python脚本的非零数量的输出传递给从未从标准输入读取的内容。这不是该

:
命令独有的。您可以通过管道传输到任何不读取标准输入的命令来获得相同的结果,例如

python testscript.py | cd .

再举一个简单的例子,考虑一个脚本,

printer.py
其中只包含

print 'abcde'

然后

python printer.py | python printer.py

会产生相同的错误。

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

触发错误的特定代码在Python的C语言实现中,这说明了为什么无法使用

try
/
except
块捕获该错误:它在脚本内容完成处理后运行。基本上,当Python关闭自身时,它会尝试关闭
stdout
,但这失败了,因为仍然有缓冲的输出等待读取。因此,Python会尝试像往常一样报告此错误,但
sys.excepthook
已在完成过程中被删除,因此失败。然后,Python尝试将消息打印到
sys.stderr
,但该消息已被重新分配,因此再次失败。您在屏幕上看到消息的原因是Python代码确实包含偶然性
fprintf

即使Python的输出对象不存在,也可以直接将一些输出写到文件指针中。

技术细节

对于那些对此过程的细节感兴趣的人,让我们看一下Python解释器的关闭序列,该序列是通过

Py_Finalize
函数来实现的
pythonrun.c

  1. 调用出口挂钩并关闭线程后,完成代码将调用
    Pyimport_Cleanup
    以完成和取消分配所有导入的模块。此函数执行的倒数第二个任务是删除
    sys
    模块,该模块主要包括调用
    _PyModule_Clear
    以清除模块字典中的所有条目-特别是包括标准流对象(Python对象)
    stdout
    stderr
  2. 当值是从字典移除或由一个新的值代替,其引用计数被递减使用的
    Py_DECREF
    宏。引用计数达到零的对象有资格进行释放。由于
    sys
    模块保留了对标准流对象的最后剩余引用,因此当这些引用未设置时
    _PyModule_Clear
    ,就可以将其释放。1个
  3. Python文件对象的重新分配由实现的
    file_dealloc
    功能在
    fileobject.c
    。首先使用适当命名的函数调用Python文件对象的
    close
    方法:
    close_the_file
    ret = close_the_file(f);

对于标准文件对象,

close_the_file(f)

委托给C
fclose
函数,如果仍有数据要写入文件指针,则C函数设置错误条件。
file_dealloc
然后检查该错误情况并打印您看到的第一条消息:

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

如果在Python程序的正常过程中这样做会很好,但是在这种情况下,

sys.excepthook
已经清除了。2
Python检查此错误情况,并输出第二条消息作为通知。

    if (hook && hook != Py_None) {    ...} else {    PySys_WriteStderr("sys.excepthook is missingn");    PyErr_Display(exception, v, tb);}
  1. 在通知我们有关丢失的信息之后
    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.stderrn");

有趣的是,此行为在Python
3.4+中有所不同,因为完成过程现在在清除内置模块之前会显式刷新标准输出和错误流。这样,如果您有等待写入的数据,则将得到一个错误信号,该信号明确地表明该情况,而不是正常完成过程中的“偶然”故障。另外,如果您跑步

python printer.py | python printer.py

使用Python
3.4(

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


1实际上,这是一个谎言。Python的导入机制缓存每个导入模块的字典中的副本,这是不释放,直到

_Pyimport_Fini
运行,后来在执行
Py_Finalize
,并且
那是 什么时候的标准流对象的最后一个引用消失。一旦引用计数达到零,
就立即
Py_DECREF
释放对象。但是,对于主要答案而言,最重要的是将引用从模块的字典中删除,然后在以后的某个时间释放。 __
sys

2同样,这是因为

sys
借助属性缓存机制,在真正释放任何内容之前,该模块的字典已被完全清除。您可以运行Python,并带有
-vv
选项以查看所有模块的属性都未设置,然后获得有关关闭文件指针的错误消息。

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



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/624223.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号