1

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

一点背景信息:我正在玩 urwid,一个Python 的TUI 库。用户界面由 call 开始,由raiseurwid.MainLoop.run()结束。到目前为止,这工作正常,但是当引发另一个异常时会发生什么?例如,当我正在捕捉(urwid MainLoop 没有)时,我会进行一些清理并希望通过引发适当的异常来结束用户界面。但这会导致屏幕充满堆栈跟踪。 urwid.ExitMainLoop()KeyboardInterrupt

一些小的研究表明,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为 except KeyboardInterrupt。有解决这个问题的模式吗?

4

1 回答 1

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保证已经完成(在这种情况下,由于 unhandled KeyboardInterrupt),因此它的异常处理程序不再处于活动状态。

所以,发生的事情是你引发了一个没有人捕捉到的新异常。当异常到达代码顶部而没有被处理时,Python 会通过打印回溯并退出来自动处理它。

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

你说:

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

如果这是真的,那么您的问题没有真正的答案……但我不确定首先是否存在问题,除了您创建的问题。只需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 存在一些限制,但如果这些限制都不适用,那么您就是黄金;a ctrl-C 将触发ExitMainLoop异常而不是 a KeyboardInterrupt,因此主循环将处理它。(您可能还想在包装代码中添加一个except ExitMainLoop:以防.可能会提高它。) main_loopcontextmanagermain_loop


或者,即使您无法编辑main_loop源代码,您也可以随时在运行时对其进行修补。如果不知道代码是什么样子,就不可能准确地解释如何做到这一点,但几乎总有办法做到这一点。

于 2013-08-28T23:57:45.050 回答