如何使用Python 单击`ctx.with_resource`来捕获(子)命令/组中的回溯

问题描述 投票:0回答:1

在 Click 中,

Context.with_resource
文档 指出它可用于:

[r]注册资源,就像在

with
语句中使用该资源一样。当上下文弹出时,资源将被清理。

据此,我了解到我传递给

Context.with_resource
的上下文管理器将在执行根 CLI 组及其任何子组和命令后退出。这似乎与这样的示例配合得很好,我将
stdout
重定向到文件:

import contextlib
import click

@contextlib.contextmanager
def my_redirect_stdout(file):
    with open(file, mode="w") as fp:
        with contextlib.redirect_stdout(fp):
            yield

@click.group()
@click.pass_context
@click.argument("file")
def cli(ctx, file):
    ctx.with_resource(my_redirect_stdout(file))

@cli.command()
def hello():
    print(f"this goes to a file")

if __name__ == "__main__":
    cli()

但是,当我尝试通过以下方式捕获异常回溯时,这不起作用:

import contextlib
import sys
import traceback
import click

@contextlib.contextmanager
def capture_traceback(file):
    with open(file, mode="w") as fp:       
        try:
            yield 
        except:
            print(f"exception!")
            traceback.print_exc(file=fp)
            sys.exit(1)

@click.group()
@click.pass_context
@click.argument("file")
def cli(ctx, file):
    ctx.with_resource(capture_traceback(file))

@cli.command()
def hello():
    raise ValueError("error!")

if __name__ == "__main__":
    cli()

异常似乎没有被

capture_traceback
函数捕获。相反,它像往常一样由解释器打印。似乎 Click 正在捕获错误,关闭上下文管理器,然后重新引发。如何捕获来自任何组或命令的异常,将回溯打印到文件,然后退出程序(不将回溯打印到终端/
stderr
)?

python python-click
1个回答
0
投票

单击不会传播这样的错误。

click.BaseCommand
类拥有一个
click.Context
,它将这些上下文管理器存储在一个简单的
ExitStack
中。当上下文中出现
__exit__
时,它会关闭退出堆栈而不转发错误信息。代码大致为:

class Context:
    ...
    def __exit__(self, exc_type, exc_value, tb):
        self._depth -= 1
        if self._depth == 0:
            self.close()
        pop_context()
    ...

    def close(self) -> None:
        self._exit_stack.close()
        self._exit_stack = ExitStack()

因为click没有转发异常信息,所以无法处理。话虽如此,看起来确实可以覆盖应用程序使用的上下文类。

免责声明以下代码非常脆弱。最好在上游存储库中提出一个问题来请求它。至少,您需要将 click 的版本固定到确切的版本,以便可以确保此补丁继续发挥作用。

...

class MyContext(click.Context):
    def __exit__(self, exc_type, exc_value, tb):
        self._depth -= 1
        if self._depth == 0:
            self._exit_stack.__exit__(exc_type, exc_value, tb)
            self._exit_stack = contextlib.ExitStack()
        click.core.pop_context()


cli.context_class = MyContext


if __name__ == "__main__":
    cli()
© www.soinside.com 2019 - 2024. All rights reserved.