【问题标题】:How to stop Python from printing out ignored Exceptions from other libraries?如何阻止 Python 从其他库中打印出被忽略的异常?
【发布时间】:2020-05-02 12:29:23
【问题描述】:

我在 Python3 如何处理特定异常时遇到了一点问题,应该忽略它,但无论如何都会在执行结束时连同 Traceback 一起打印出来。

具体来说,我使用graph-tool 库来启动一个交互式窗口,如下所示:

graph_tool.draw.interactive_window(self.graphtool_graph, vertex_text=self.v_label, vertex_font_size=6,geometry=(1920, 1080))

如果我关闭打开的窗口它完成绘制和布置图形本身之前,我的问题就会出现。然后,当我的代码执行完成时,我得到这个打印输出:

Exception ignored in: <bound method GraphWindow.__del__ of <gtk_draw.GraphWindow object at 0x7f221ccf3750 (graph_tool+draw+gtk_draw+GraphWindow at 0x4c662a0)>>
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/graph_tool/draw/gtk_draw.py", line 1183, in __del__
  File "/usr/lib/python3/dist-packages/graph_tool/draw/gtk_draw.py", line 375, in cleanup
  File "/usr/lib/python3/dist-packages/gi/overrides/__init__.py", line 68, in __get__
TypeError: 'NoneType' object is not callable

这显然不是很好看,尤其是当最终用户与我的项目进行交互时。

我尝试在 try-catch 语句中包含对 draw.interactive_window 的调用,如下所示:

        try:
            graph_tool.draw.interactive_window(self.graphtool_graph, vertex_text=self.v_label, vertex_font_size=6,
                                               geometry=(1920, 1080))
        except TypeError:
            # Interactive Window closed before it finished drawing the graph. It would throw an exception that is
            # quite ugly to see. Let's ignore it.
            return

但我最终遇到了同样的问题。我什至尝试不指定TypeError 异常并使用毯子except,但无济于事。

有谁知道阻止 Python 打印出这个异常的方法?

P.S.:我在 Python 错误跟踪器上找到了这个 issue,这似乎是相关的。在所附的讨论中,这被认为是一个特性™而不是一个错误,但我仍然想了解是否有可能以任何方式阻止这个异常打印,特别是当我明确地试图捕捉它并忽略它时。

【问题讨论】:

    标签: python-3.x exception try-catch graph-tool


    【解决方案1】:

    您的问题来自 __del__ 方法调用中发生的异常。作为documented here

    由于调用 del() 方法的不稳定环境,在其执行期间发生的异常将被忽略,而是向 sys.stderr 打印警告

    顺便说一句,这就是为什么您的 except 块无法操作的原因...

    您可以使用此 mcve 验证这一点:

    # warntest.py
    
    class Foo(object):
        def __del__(self):
            raise ValueError("YADDA")
    
    f = Foo()
    

    然后

     $ python3 warntest.py 
    Exception ignored in: <bound method Foo.__del__ of <__main__.Foo object at 0x7efcb61dc898>>
    Traceback (most recent call last):
      File "warntest.py", line 3, in __del__
    ValueError: YADDA
    

    尽管“打印了警告”可能暗示了什么,令我沮丧的是,只是要求 Python 到 silence warnings 并没有改变这里的任何东西 - python3 -Wignore warntest.py 的行为相同,并手动设置“忽略”过滤器脚本没有做更多的事情。

    IOW,恐怕这里没有简单而干净的解决方案。这为您提供了三种可能的选择:

    1/ 从源头上解决问题。 graph-tool 是 OSS,您可以通过改进那些 __del__ 方法来做出贡献,这样它们就不会引发任何异常。

    2/ 使用 hack。一种可能的方法是使用contextlib.redirect_stderr 管理器来包装这个调用——我试过了,它按预期工作,但它绝对会阻止任何东西在这个调用期间到达stderr。

    3/ 忍受它...

    编辑

    我尝试查看 g-t 的来源,但找不到引发此异常的位置

    它在您发布的回溯中以纯文本形式编写...实际上,异常本身在 gi/overrides/__init__.py 中引发,但这不是重点 - 您想要编辑 GraphWindow.__del__(和 GraphWidget.__del__ 也是 FWIW)将self.graph.cleanup()GraphWidget 中的self.cleanup() 包装在最粗略的try/except 块中。以下MCVE 重现了该问题:

    class Sub(object):
        def cleanup(self):
            raise ValueError("YADDA")
    
        def __del__(self):
            self.cleanup()
    
    class Main(object):
        def __init__(self):
            self.sub = Sub()
    
        def __del__(self):
            self.sub.cleanup()
    
    Main()
    

    而且解决方法很简单:

    class Sub(object):
        def cleanup(self):
            raise ValueError("YADDA")
    
        def __del__(self):
            # yes, a bare except clause and a pass...
            # this is exactly what you're NOT supposed to do,
            # never ever, because it's BAD... but here it's
            # ok - provided you double-checked what the code
            # in the `try` block really do, of course.
            try:
                self.cleanup()
            except:
                pass
    
    class Main(object):
        def __init__(self):
            self.sub = Sub()
    
        def __del__(self):
            try:
                self.sub.cleanup()
            except:
                 pass
    

    重要提示:这种异常处理程序(一个空的 except 子句后跟 pass完全是应该永远不会强>做。这是最可怕的异常处理反模式。但是__del__确实是一个特例,而在当前情况下the cleanup() method only tries to unregister a callback function所以它真的是无害的。

    但即使将输出重定向到文件

    请注意,您要重定向 stderr,而不是 stdout。以下 sn-p 对我有用:

    import contextlib
    
    class Foo(object):
        def __del__(self):
            raise ValueError("YADDA")
    
    def test():    
        Foo()
    
    with contextlib.redirect_stderr(io.StringIO()):
        test()
    

    【讨论】:

    • 感谢您的回答!我尝试查看 gt 的来源,但找不到引发此异常的位置,所以现在我不会考虑选项 1。选项 2 似乎是最明智的,前提是它被重定向到某个地方,以后可以访问它以检查实际情况错误,但即使将输出重定向到文件,它仍然会打印到控制台并且文件是空的!我猜它不是来自这个函数调用?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-12
    • 1970-01-01
    • 2013-04-25
    相关资源
    最近更新 更多