注意:我没有尝试在Windows下重现下面描述的问题,也没有尝试在2.7.3以外的Python版本中重现。
引发问题的最可靠方法是通过:
(在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
我的问题是:
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
捕获。)
上面的例子,诚然,是高度人工的,但是当我的脚本输出通过第三方软件管道传输时,我遇到了同样的问题有时。
这个错误消息当然是无害的,但它让最终用户感到不安,所以我想让它安静下来。
编辑:下面的脚本与上面的原始脚本不同,只是它重新定义了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
今天我自己也遇到了这样的问题,就去找答案。我认为这里的一个简单解决方法是确保首先刷新stdio,这样python就不会在脚本关闭期间失败。例如:
然后这个脚本什么也不会发生,因为异常(IOError:[Errno 32]断开的管道)被try…except禁止。
在您的程序中抛出一个使用try/except块无法捕获的异常。为了抓住他,重写函数
sys.excepthook
:来自documentation:
示例:
您需要防止脚本将任何内容写入标准输出。这意味着删除任何
print
语句和sys.stdout.write
的任何用法,以及调用这些语句的任何代码。发生这种情况的原因是,您正在将来自Python脚本的非零量输出管道化为从不从标准输入读取的输出。这不是
:
命令所独有的;您可以通过管道连接到任何不读取标准输入的命令来获得相同的结果,例如或者对于一个简单的例子,考虑一个脚本
printer.py
,它只包含那么
会产生同样的错误。
当您将一个程序的输出导入另一个程序时,写入程序生成的输出将备份到缓冲区中,并等待读取程序从缓冲区请求该数据。只要缓冲区不是空的,任何关闭写文件对象的尝试都会失败,并出现错误。这是你看到的信息的根本原因。
触发错误的特定代码在Python的C语言实现中,这解释了为什么不能用
try
/except
块捕获它:它在脚本内容完成处理之后运行。基本上,当Python关闭自身时,它会尝试关闭stdout
,但这会失败,因为仍有缓冲输出等待读取。所以Python试图像正常情况一样报告这个错误,但是sys.excepthook
已经作为终结过程的一部分被删除了,所以失败了。然后,Python尝试将消息打印到sys.stderr
,但该消息已经被释放,因此再次失败。您在屏幕上看到这些消息的原因是,Python代码确实包含一个列联项fprintf
,可以直接将一些输出写到文件指针,即使Python的输出对象不存在。技术细节
对于那些对这个过程的细节感兴趣的人,让我们看看Python解释器的关闭序列,它是在} function 中实现的。
pythonrun.c
的^{stdout
和stderr
。sys
模块保存对标准流对象的最后一个剩余引用,当这些引用被_PyModule_Clear
取消设置时,它们就可以被释放了。1Python文件对象的释放由} function 完成。第一个invokes the Python file object's ^{} method 使用了恰当命名的^{} function :
fileobject.c
中的the ^{对于标准文件对象} function ,如果仍有数据要写入文件指针,则设置错误条件。
close_the_file(f)
delegates to the C ^{file_dealloc
然后检查错误情况并打印您看到的第一条消息:打印该消息后,Python将尝试使用^{} 显示异常。委托给^{} ,作为其功能的一部分,
PyErr_PrintEx
尝试从sys.excepthook
访问Python异常打印机。如果在Python程序的正常过程中完成,这是可以的,但是在这种情况下,
sys.excepthook
已经被清除。2Python检查此错误条件并将第二条消息作为通知打印。在通知我们丢失的} 打印异常信息,这是显示堆栈跟踪的默认方法。这首赋的第一件事操作是尝试访问
excepthook
之后,Python会返回到使用^{sys.stderr
。在这种情况下,这不起作用,因为
sys.stderr
已经被清除并且不可访问。3所以代码直接调用fprintf
将第三条消息发送到C标准错误流。有趣的是,Python 3.4+中的行为有点不同,因为在清除内置模块之前,终结过程现在是explicitly flushes the standard output and error streams。这样,如果您有等待写入的数据,您将得到一个错误,该错误将显式地指示该条件,而不是正常终结过程中的“意外”失败。还有,如果你跑
使用Python 3.4(当然,在
print
语句中加上括号之后),根本不会得到任何错误。我想Python的第二次调用可能出于某种原因正在使用标准输入,但这是一个完全独立的问题。实际上,那是个谎言。Python的导入机制caches a copy of each imported module's dictionary,直到^{} 运行,later in the implementation of ^{} 和才被释放,这是当最后一个对标准流对象的引用消失时。一旦引用计数为零,
Py_DECREF
立即释放对象。但对于主答案来说,所有重要的是从sys
模块的字典中删除引用,然后稍后释放。再次指出,这是因为
sys
模块的字典在真正释放任何东西之前被完全清除,这要归功于属性缓存机制。您可以使用-vv
选项运行Python,在收到有关关闭文件指针的错误消息之前,查看模块的所有属性都被取消设置。3除非您了解前面脚注中提到的属性缓存机制,否则此特定行为是唯一没有意义的部分。
相关问题 更多 >
编程相关推荐