草庐IT

python - 如何消除 "sys.excepthook is missing"错误?

coder 2023-05-21 原文

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

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

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

IE。:
% 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 捕获。)

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

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

编辑:以下脚本与上面的原始脚本的不同之处仅在于它重新定义了 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

最佳答案

How can I modify the test script above to avoid the error message when the script is run as shown (under 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 function 中实现的。
  • 在调用退出钩子(Hook)和关闭线程后,终结代码调用 PyImport_Cleanup 来终结和释放所有导入的模块。该函数执行的倒数第二个任务是 removing the sys module ,它主要包括调用 _PyModule_Clear 来清除模块字典中的所有条目 - 特别是包括标准流对象(Python 对象),例如 stdoutstderr
  • 当一个值从字典中删除或被一个新值替换时,its reference count is decremented 使用 the Py_DECREF macro 。引用计数达到零的对象有资格被释放。由于 sys 模块保存对标准流对象的最后剩余引用,当这些引用被 _PyModule_Clear 取消设置时,它们就可以被释放了。1
  • Python 文件对象的释放由 file_dealloc 中的 the fileobject.c function 完成。第一个 invokes the Python file object's close method 使用恰当命名的 close_the_file function :
    ret = close_the_file(f);
    

    对于标准文件对象 close_the_file(f) delegates to the C fclose function ,如果仍有数据要写入文件指针,它会设置错误条件。 file_dealloc 然后检查该错误条件并打印您看到的第一条消息:
    if (!ret) {
        PySys_WriteStderr("close failed in file object destructor:\n");
        PyErr_Print();
    }
    else {
        Py_DECREF(ret);
    }
    
  • 打印该消息后,Python 然后尝试使用 PyErr_Print 显示异常。这委托(delegate)给 PyErr_PrintEx ,作为其功能的一部分, 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 会回退到使用 PyErr_Display 打印异常信息,这是显示堆栈跟踪的默认方法。这个函数做的第一件事就是尝试访问 sys.stderr
    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+ 中的行为略有不同,因为在清除内置模块之前的完成过程现在是 explicitly flushes the standard output and error streams。这样,如果您有数据等待写入,您会收到一个错误,明确表示该情况,而不是正常完成过程中的“意外”失败。另外,如果你跑
    python printer.py | python printer.py
    

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

    1实际上,那是谎言。 Python 的导入机制 caches a copy of each imported module's dictionary 直到​​ _PyImport_Fini 运行时才会发布, later in the implementation of Py_Finalize ,这就是对标准流对象的最后引用消失的时候。一旦引用计数达到零,Py_DECREF 立即释放对象。但对于主要答案而言,重要的是引用从 sys 模块的字典中删除,然后在稍后释放。

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

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

    关于python - 如何消除 "sys.excepthook is missing"错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12790328/

    有关python - 如何消除 "sys.excepthook is missing"错误?的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

      关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

    4. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

      我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

    5. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

      我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

    6. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

      给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

    7. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    8. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

      大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

    9. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

      我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

    10. ruby - 如何指定 Rack 处理程序 - 2

      Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

    随机推荐