Python3:清除异常链?

时间:2013-08-28 22:37:15

标签: python exception python-3.x urwid

我正在尝试在except:块中引发异常但是解释器试图提供帮助并通过“强制”打印堆栈跟踪。有可能避免这种情况吗?

一些背景资料: 我正在玩urwid,一个TUI library用于python。用户界面通过调用urwid.MainLoop.run()启动,并以提升 urwid.ExitMainLoop()结束。到目前为止,这工作正常但是当引发另一个异常时会发生什么?例如。当我正在捕捉KeyboardInterrupt(urwid MainLoop没有)时,我做了一些清理并希望结束用户界面 - 通过引发适当的异常。但这会导致屏幕上出现堆栈痕迹。

一些小小的研究表明,python3会记住链式异常,并且可以使用'cause'显式引发异常:raise B() from A()。我学会了一些方法来更改或附加有关引发异常的数据,但我发现无法“禁用”此功能。我想避免打印像The above exception was the direct cause of...那样的堆栈跟踪和行,只是在except:块中引发接口结束异常,就像我在一个块外面一样。

这是可能还是我做了一些根本错误的事情?

修改: 这是一个类似于我当前架构的示例,导致同样的问题:

#!/usr/bin/env python3
import time

class Exit_Main_Loop(Exception):
    pass

# UI main loop
def main_loop():
    try:
        while True:
            time.sleep(0.1)
    except Exit_Main_Loop as e:
        print('Exit_Main_Loop')
        # do some UI-related clean up

# my main script
try:
    main_loop()
except KeyboardInterrupt as e:
    print('KeyboardInterrupt')
    # do some clean up
    raise Exit_Main_Loop()      # signal the UI to terminate

很遗憾,除了main_loop之外,我无法将KeyboardInterrupt更改为{{1}}。有没有解决这个问题的模式?

1 个答案:

答案 0 :(得分:1)

我仍然不太明白你的解释,但是来自代码:

try:
    main_loop()
except KeyboardInterrupt as e:
    print('KeyboardInterrupt')
    # do some clean up
    raise Exit_Main_Loop()      # signal the UI to terminate

main_loop无法看到Exit_Main_Loop()例外情况。当您到达KeyboardInterrupt句柄时,main_loop保证已经完成(在这种情况下,由于未处理的KeyboardInterrupt),因此其异常处理程序不再处于活动状态。

所以,会发生什么事情,你提出了一个没有人抓住的新例外。当异常在没有处理的情况下到达代码顶部时,Python会通过打印回溯和退出来自动处理它。

如果您想将一种类型的异常转换为另一种类型,以便main_loop可以处理它,您必须在内部 try块中执行此操作。

你说:

  

不幸的是,除了KeyboardInterrupt之外,我无法将main_loop更改为。

如果这是真的,那么对你的问题没有真正的答案......但我不确定首先出现问题,除了你创建的问题。只需从代码中删除Exit_Main_Loop(),是不是已经按照您的要求执行了操作?如果您只是想阻止Python打印回溯并退出,那么这将为您解决。


如果确实存在 问题 - 例如,main_loop代码有一些清理代码,无论如何都需要执行,并且它没有被执行,因为它没有句柄KeyboardInterrupt - 有两种方法可以解决这个问题。


首先,正如signal文档解释:

  

signal.signal()函数允许定义在接收到信号时要执行的自定义处理程序。安装了少量默认处理程序:... SIGINT已转换为KeyboardInterrupt例外。

因此,您所要做的就是用另一个处理程序替换默认处理程序:

def handle_sigint(signum, frame):
    raise ExitMainLoop()
signal.signal(signal.SIGINT, handle_sigint)

在你开始main_loop之前做这个,你应该没问题。请记住,线程程序和Windows都有一些限制,但如果这些限制都不适用,那么你就是金色的; ctrl-C将触发ExitMainLoop异常而不是KeyboardInterrupt,因此主循环将处理它。 (您可能还希望在包装器代码中添加except ExitMainLoop:块,以防 main_loop之外的异常。但是,您可以轻松编写contextmanager设置和恢复调用main_loop周围的信号,因此没有任何外部代码可以提升它。)


或者,即使您无法编辑main_loop源代码,也可以始终在运行时对其进行monkeypatch。在不知道代码是什么样的情况下,不可能完全解释如何做到这一点,但几乎总有办法做到这一点。