如何阻止 Python 从其他库打印出忽略的异常?
How to stop Python from printing out ignored Exceptions from other libraries?
我对 Python3 处理特定异常的方式有点疑问,应该忽略该异常,但无论如何它都会在执行结束时连同 Traceback 打印出来。
具体来说,我使用 graph-tool 库来启动交互式 window,如下所示:
graph_tool.draw.interactive_window(self.graphtool_graph, vertex_text=self.v_label, vertex_font_size=6,geometry=(1920, 1080))
如果我关闭 打开的 window,然后 它完成绘制和布置图形本身,就会出现我的问题。然后,当我的代码执行完成时,我得到这个打印输出:
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
这显然不是很好看,尤其是当最终用户与我的项目进行交互时。
我试图将对 draw.interactive_window
的调用包含在 try-catch 语句中,如下所示:
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,这似乎是相关的。在随附的讨论中,这被描述为一个功能™而不是一个错误,但我仍然想了解是否有可能以任何方式阻止打印此异常,尤其是当我明确地试图捕获它并忽略它时。
您的问题来自 __del__
方法调用中发生的异常。作为 documented here、
Due to the precarious circumstances under which del() methods are invoked, exceptions that occur during their execution are ignored, and a warning is printed to sys.stderr instead
顺便说一下,这就是为什么你的 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
尽管 "a warning is printed" 可能会提出什么建议,但令我非常沮丧的是,只是询问 Python 到 silence warnings 并没有改变任何东西 - python3 -Wignore warntest.py
表现相同,并在脚本中手动设置 "ignore" 过滤器并没有做更多。
IOW,恐怕这里没有简单而干净的解决方案。这给您留下了三个可能的选择:
1/ 从源头上解决问题。 graph-tool 是 OSS,您可以通过改进这些 __del__
方法来做出贡献,这样它们就不会引发任何异常。
2/ 使用 hack。一个可能的方法是用 contextlib.redirect_stderr
管理器包装这个调用——我试过了,它按预期工作,但它绝对会阻止任何东西在这个调用期间到达 stderr。
3/ 接受它...
编辑
I tried looking into the source of g-t and cannot find where this exception is raised
它在您发布的回溯中以纯文本形式写入...实际上,异常本身在 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所以真的是无伤大雅。
but even when redirecting the output to a file
请注意,您要重定向 stderr
,而不是 stdout
。以下代码段对我有用:
import contextlib
class Foo(object):
def __del__(self):
raise ValueError("YADDA")
def test():
Foo()
with contextlib.redirect_stderr(io.StringIO()):
test()
我对 Python3 处理特定异常的方式有点疑问,应该忽略该异常,但无论如何它都会在执行结束时连同 Traceback 打印出来。
具体来说,我使用 graph-tool 库来启动交互式 window,如下所示:
graph_tool.draw.interactive_window(self.graphtool_graph, vertex_text=self.v_label, vertex_font_size=6,geometry=(1920, 1080))
如果我关闭 打开的 window,然后 它完成绘制和布置图形本身,就会出现我的问题。然后,当我的代码执行完成时,我得到这个打印输出:
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
这显然不是很好看,尤其是当最终用户与我的项目进行交互时。
我试图将对 draw.interactive_window
的调用包含在 try-catch 语句中,如下所示:
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,这似乎是相关的。在随附的讨论中,这被描述为一个功能™而不是一个错误,但我仍然想了解是否有可能以任何方式阻止打印此异常,尤其是当我明确地试图捕获它并忽略它时。
您的问题来自 __del__
方法调用中发生的异常。作为 documented here、
Due to the precarious circumstances under which del() methods are invoked, exceptions that occur during their execution are ignored, and a warning is printed to sys.stderr instead
顺便说一下,这就是为什么你的 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
尽管 "a warning is printed" 可能会提出什么建议,但令我非常沮丧的是,只是询问 Python 到 silence warnings 并没有改变任何东西 - python3 -Wignore warntest.py
表现相同,并在脚本中手动设置 "ignore" 过滤器并没有做更多。
IOW,恐怕这里没有简单而干净的解决方案。这给您留下了三个可能的选择:
1/ 从源头上解决问题。 graph-tool 是 OSS,您可以通过改进这些 __del__
方法来做出贡献,这样它们就不会引发任何异常。
2/ 使用 hack。一个可能的方法是用 contextlib.redirect_stderr
管理器包装这个调用——我试过了,它按预期工作,但它绝对会阻止任何东西在这个调用期间到达 stderr。
3/ 接受它...
编辑
I tried looking into the source of g-t and cannot find where this exception is raised
它在您发布的回溯中以纯文本形式写入...实际上,异常本身在 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所以真的是无伤大雅。
but even when redirecting the output to a file
请注意,您要重定向 stderr
,而不是 stdout
。以下代码段对我有用:
import contextlib
class Foo(object):
def __del__(self):
raise ValueError("YADDA")
def test():
Foo()
with contextlib.redirect_stderr(io.StringIO()):
test()