如图所示(在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
。
ret = close_the_file(f);
对于标准文件对象,close_the_file(f)
委托给Cfclose
函数,如果仍有数据要写入文件指针,则C函数设置错误条件。file_dealloc
然后检查该错误情况并打印您看到的第一条消息:
if (!ret) {
PySys_WriteStderr("close Failed in file object destructor:\n");
PyErr_Print();
}
else {
Py_DECREF(ret);
}
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);
}
PyObject *f = PySys_GetObject("stderr");
在这种情况下,这将不起作用,因为sys.stderr
它已被清除并且无法访问。3因此,代码fprintf
直接调用以将第三条消息发送到C标准错误流。
if (f == NULL || f == Py_None)
fprintf(stderr, "lost sys.stderr\n");
有趣的是,此行为在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除非您了解前面脚注中提到的属性缓存机制,否则这部分行为是唯一没有意义的部分。