为了账号安全,请及时绑定邮箱和手机立即绑定

在上下文管理器中运行子命令

在上下文管理器中运行子命令

拉莫斯之舞 2021-11-23 19:01:14
在 python clickCLI 应用程序的上下文中,我想在上下文管理器中运行一个子命令,该子命令将在更高级别的命令中设置。怎么可能做到这一点click?我的伪代码看起来像:import clickfrom contextlib import contextmanager@contextmanagerdef database_context(db_url):    try:        print(f'setup db connection: {db_url}')        yield    finally:        print('teardown db connection')@click.group@click.option('--db',default='local')def main(db):    print(f'running command against {db} database')    db_url = get_db_url(db)    connection_manager = database_context(db_url)    # here come the mysterious part that makes all subcommands    # run inside the connection manager@main.commanddef do_this_thing()    print('doing this thing')@main.commanddef do_that_thing()    print('doing that thing')这将被称为:> that_cli do_that_thingrunning command against local databasesetup db connection: db://user:pass@localdb:db_namedoing that thingteardown db connection> that_cli --db staging do_this_thingrunning command against staging databasesetup db connection: db://user:pass@123.456.123.789:db_namedoing this thingteardown db connection编辑:请注意,上面的示例是为了更好地说明 缺少的功能而伪造的click,而不是我想特别解决这个问题。我知道我可以在所有命令中重复相同的代码并达到相同的效果,这在我的实际用例中已经做到了。我的问题正是关于我只能在主函数中做什么,它会在上下文管理器中运行所有透明的子命令。
查看完整描述

2 回答

?
12345678_0001

TA贡献1802条经验 获得超5个赞

装饰命令

  1. 定义一个上下文管理器装饰contextlib.ContextDecorator

  2. 使用click.pass_context装饰器 on main(),以便您可以探索单击上下文

  3. 创建db_context上下文管理器的实例

  4. 迭代为组定义的命令main使用ctx.command.commands

  5. 对于每个命令,使用上下文管理器修饰的相同回调替换原始回调(命令调用的函数) db_context(cmd)

通过这种方式,您将以编程方式修改每个命令,使其行为类似于:

@main.command()

@db_context

def do_this_thing():

    print('doing this thing')

但无需更改您的功能之外的代码main()。


有关工作示例,请参阅下面的代码:


import click

from contextlib import ContextDecorator



class Database_context(ContextDecorator):

    """Decorator context manager."""


    def __init__(self, db_url):

        self.db_url = db_url


    def __enter__(self):

        print(f'setup db connection: {self.db_url}')


    def __exit__(self, type, value, traceback):

        print('teardown db connection')



@click.group() 

@click.option('--db', default='local')

@click.pass_context

def main(ctx, db):


    print(f'running command against {db} database')

    db_url = db  # get_db_url(db)


# here come the mysterious part that makes all subcommands

# run inside the connection manager


    db_context = Database_context(db_url)           # Init context manager decorator

    for name, cmd in ctx.command.commands.items():  # Iterate over main.commands

        cmd.allow_extra_args = True                 # Seems to be required, not sure why

        cmd.callback = db_context(cmd.callback)     # Decorate command callback with context manager



@main.command()

def do_this_thing():

    print('doing this thing')



@main.command()

def do_that_thing():

    print('doing that thing')



if __name__ == "__main__":

    main()

它执行您在问题中描述的内容,希望它能在实际代码中按预期工作。


使用 click.pass_context

下面的代码将让您了解如何使用click.pass_context.


import click

from contextlib import contextmanager


@contextmanager

def database_context(db_url):

    try:

        print(f'setup db connection: {db_url}')

        yield

    finally:

        print('teardown db connection')



@click.group()

@click.option('--db',default='local')

@click.pass_context

def main(ctx, db):

    ctx.ensure_object(dict)

    print(f'running command against {db} database')

    db_url = db #get_db_url(db)

    # Initiate context manager

    ctx.obj['context'] = database_context(db_url)


@main.command()

@click.pass_context

def do_this_thing(ctx):

    with ctx.obj['context']:

        print('doing this thing')


@main.command()

@click.pass_context

def do_that_thing(ctx):

    with ctx.obj['context']:

        print('doing that thing')


if __name__ == "__main__":

    main(obj={})

避免显式with语句的另一种解决方案可能是使用 将上下文管理器作为装饰器传递contextlib.ContextDecorator,但使用进行设置可能会更复杂click。


查看完整回答
反对 回复 2021-11-23
?
慕的地6264312

TA贡献1817条经验 获得超6个赞

此用例在 Click from v8.0 中通过使用 ctx.with_resource(context_manager)

https://click.palletsprojects.com/en/8.0.x/api/#click.Context.with_resource

Click 高级文档中有一个工作示例

https://click.palletsprojects.com/en/8.0.x/advanced/#managing-resources



查看完整回答
反对 回复 2021-11-23
  • 2 回答
  • 0 关注
  • 192 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信