为什么 python-click 不在子命令之间传递上下文?
Why doesn't python-click pass context between subcommands?
我正在尝试通过 python-click 在两个子命令之间传递上下文。这是一个 MWE:
import click
@click.group(chain=True)
def cli() -> None:
pass
@cli.command()
@click.pass_context
def fn1(cxt):
cxt.obj = 1
@cli.command()
@click.pass_context
def fn2(cxt):
print(f'{cxt.obj=}')
if __name__ == '__main__':
cli()
如果我用 cli.py fn1 fn2
调用它,我希望得到“cxt.obj=1”,而我得到“cxt.obj=None”。
在尝试调试时,我还注意到我可以从 fn1
和 fn2
访问 cli
的上下文,但不能从 fn2
。因此,我可以在 fn1
中设置 cli
上下文的对象,并在 fn2
中读取 cli 的上下文,但必须有更好的方法,其中 obj
可在 fn2
的上下文中直接访问。
我的心智模型有什么问题,为什么子命令之间的上下文不持久?而且,当需要在子命令之间传递数据时,最好使用什么模式?
上下文从父项复制到子项。在你的例子中,context.obj
for cli()
你没有在你的例子中公开,是 None
。 None
被复制到 fn1()
和 fn2()
的子上下文中,但随后在 fn1()
中,您将副本更改为 1
。最后,在 fn2()
中,您会收到 cli()
上下文的副本,其中 obj
仍然是 None
因此,为了达到您想要的结果,有(至少)两个选择。
- 在父上下文中使
context.obj
成为一个可变对象,然后改变该对象
- 如您所说,可以通过
ctx.parent.obj = 1
之类的方式直接访问父上下文
第一种(我的首选)方法示例:
例子
import click, sys
@click.group(chain=True)
@click.pass_context
def cli(ctx) -> None:
ctx.obj = {}
@cli.command()
@click.pass_context
def fn1(ctx):
ctx.obj['fn1'] = 1
@cli.command()
@click.pass_context
def fn2(ctx):
click.echo(f'obj: {ctx.obj}')
测试代码
if __name__ == "__main__":
print('Click Version: {}'.format(click.__version__))
print('Python Version: {}'.format(sys.version))
print('-----------')
cmd = 'fn1 fn2'
print('> ' + cmd)
cli(cmd.split())
结果:
Click Version: 8.1.3
Python Version: 3.7.8 (tags/v3.7.8:4b47a5b6ba, Jun 28 2020, 08:53:46) [MSC v.1916 64 bit (AMD64)]
-----------
> fn1 fn2
obj: {'fn1': 1}
我正在尝试通过 python-click 在两个子命令之间传递上下文。这是一个 MWE:
import click
@click.group(chain=True)
def cli() -> None:
pass
@cli.command()
@click.pass_context
def fn1(cxt):
cxt.obj = 1
@cli.command()
@click.pass_context
def fn2(cxt):
print(f'{cxt.obj=}')
if __name__ == '__main__':
cli()
如果我用 cli.py fn1 fn2
调用它,我希望得到“cxt.obj=1”,而我得到“cxt.obj=None”。
在尝试调试时,我还注意到我可以从 fn1
和 fn2
访问 cli
的上下文,但不能从 fn2
。因此,我可以在 fn1
中设置 cli
上下文的对象,并在 fn2
中读取 cli 的上下文,但必须有更好的方法,其中 obj
可在 fn2
的上下文中直接访问。
我的心智模型有什么问题,为什么子命令之间的上下文不持久?而且,当需要在子命令之间传递数据时,最好使用什么模式?
上下文从父项复制到子项。在你的例子中,context.obj
for cli()
你没有在你的例子中公开,是 None
。 None
被复制到 fn1()
和 fn2()
的子上下文中,但随后在 fn1()
中,您将副本更改为 1
。最后,在 fn2()
中,您会收到 cli()
上下文的副本,其中 obj
仍然是 None
因此,为了达到您想要的结果,有(至少)两个选择。
- 在父上下文中使
context.obj
成为一个可变对象,然后改变该对象 - 如您所说,可以通过
ctx.parent.obj = 1
之类的方式直接访问父上下文
第一种(我的首选)方法示例:
例子
import click, sys
@click.group(chain=True)
@click.pass_context
def cli(ctx) -> None:
ctx.obj = {}
@cli.command()
@click.pass_context
def fn1(ctx):
ctx.obj['fn1'] = 1
@cli.command()
@click.pass_context
def fn2(ctx):
click.echo(f'obj: {ctx.obj}')
测试代码
if __name__ == "__main__":
print('Click Version: {}'.format(click.__version__))
print('Python Version: {}'.format(sys.version))
print('-----------')
cmd = 'fn1 fn2'
print('> ' + cmd)
cli(cmd.split())
结果:
Click Version: 8.1.3
Python Version: 3.7.8 (tags/v3.7.8:4b47a5b6ba, Jun 28 2020, 08:53:46) [MSC v.1916 64 bit (AMD64)]
-----------
> fn1 fn2
obj: {'fn1': 1}