单击:如何将操作应用于所有命令和子命令,但允许命令选择退出?

2023-12-11

我有一个案例,我想自动运行一个常用函数,check_upgrade(),对于我的大多数单击命令和子命令,但在少数情况下我不想运行它。我想我可以有一个可以添加的装饰器(例如@bypass_upgrade_check) 对于命令,其中check_upgrade()不应该运行。

我希望有类似的东西(谢谢史蒂芬·劳奇最初的想法):

def do_upgrade():
    print "Performing upgrade"

def bypass_upgrade_check(func):
    setattr(func, "do_upgrade_check", False)
    return func

@click.group()
@click.pass_context
def common(ctx):
    sub_cmd = ctx.command.commands[ctx.invoked_subcommand]
    if getattr(sub_cmd, "do_upgrade_check", True):
        do_upgrade()

@bypass_upgrade_check
@common.command()
def top_cmd1():
    # don't run do_upgrade() on top level command
    pass

@common.command()
def top_cmd2():
    # DO run do_upgrade() on top level command
    pass

@common.group()
def sub_cmd_group():
    pass

@bypass_upgrade_check
@sub_cmd.command()
def sub_cmd1():
    # don't run do_upgrade() on second-level command
    pass

@sub.command()
def sub_cmd2():
    # DO run do_upgrade() on second-level command
    pass

不幸的是,这只适用于顶级命令,因为ctx.invoked_subcommand指的是sub_cmd_group并不是sub_cmd1 or sub_cmd2.

有没有办法递归搜索子命令,或者使用自定义组能够通过顶级命令和子命令来实现此功能?


解决这个问题的一种方法是构建一个与自定义装饰器配对的自定义装饰器click.Group class:

自定义装饰器生成器:

def make_exclude_hook_group(callback):
    """ for any command that is not decorated, call the callback """

    hook_attr_name = 'hook_' + callback.__name__

    class HookGroup(click.Group):
        """ group to hook context invoke to see if the callback is needed"""

        def invoke(self, ctx):
            """ group invoke which hooks context invoke """
            invoke = ctx.invoke

            def ctx_invoke(*args, **kwargs):
                """ monkey patched context invoke """
                sub_cmd = ctx.command.commands[ctx.invoked_subcommand]
                if not isinstance(sub_cmd, click.Group) and \
                        getattr(sub_cmd, hook_attr_name, True):
                    # invoke the callback
                    callback()
                return invoke(*args, **kwargs)

            ctx.invoke = ctx_invoke

            return super(HookGroup, self).invoke(ctx)

        def group(self, *args, **kwargs):
            """ new group decorator to make sure sub groups are also hooked """
            if 'cls' not in kwargs:
                kwargs['cls'] = type(self)
            return super(HookGroup, self).group(*args, **kwargs)

    def decorator(func=None):
        if func is None:
            # if called other than as decorator, return group class
            return HookGroup

        setattr(func, hook_attr_name, False)

    return decorator

使用装饰器构建器:

要使用装饰器,我们首先需要构建装饰器,如下所示:

bypass_upgrade_check = make_exclude_hook_group(do_upgrade)

然后我们需要将它用作自定义类click.group() like:

@click.group(cls=bypass_upgrade_check())
...

最后,我们可以将任何命令或子命令装饰到不需要使用回调的组中,例如:

@bypass_upgrade_check
@my_group.command()
def my_click_command_without_upgrade():
     ...

这是如何运作的?

这是可行的,因为 click 是一个设计良好的 OO 框架。这@click.group()装饰器通常会实例化一个click.Group对象,但允许用cls范围。所以继承是一件比较容易的事情click.Group在我们自己的类中并重写所需的方法。

在本例中,我们构建一个装饰器,在不需要调用回调的任何单击函数上设置属性。然后在我们的自定义组中,我们猴子补丁click.Context.invoke()我们的上下文,如果即将执行的命令尚未被修饰,我们调用回调。

测试代码:

import click

def do_upgrade():
    print("Performing upgrade")

bypass_upgrade_check = make_exclude_hook_group(do_upgrade)

@click.group(cls=bypass_upgrade_check())
@click.pass_context
def cli(ctx):
    pass

@bypass_upgrade_check
@cli.command()
def top_cmd1():
    click.echo('cmd1')

@cli.command()
def top_cmd2():
    click.echo('cmd2')

@cli.group()
def sub_cmd_group():
    click.echo('sub_cmd_group')

@bypass_upgrade_check
@sub_cmd_group.command()
def sub_cmd1():
    click.echo('sub_cmd1')

@sub_cmd_group.command()
def sub_cmd2():
    click.echo('sub_cmd2')

if __name__ == "__main__":
    commands = (
        'top_cmd1',
        'top_cmd2',
        'sub_cmd_group sub_cmd1',
        'sub_cmd_group sub_cmd2',
        '--help',
    )

    import sys, time

    time.sleep(1)
    print('Click Version: {}'.format(click.__version__))
    print('Python Version: {}'.format(sys.version))
    for cmd in commands:
        try:
            time.sleep(0.1)
            print('-----------')
            print('> ' + cmd)
            time.sleep(0.1)
            cli(cmd.split())

        except BaseException as exc:
            if str(exc) != '0' and \
                    not isinstance(exc, (click.ClickException, SystemExit)):
                raise

Results:

Click Version: 6.7
Python Version: 3.6.3 (v3.6.3:2c5fed8, Oct  3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
-----------
> top_cmd1
cmd1
-----------
> top_cmd2
Performing upgrade
cmd2
-----------
> sub_cmd_group sub_cmd1
sub_cmd_group
sub_cmd1
-----------
> sub_cmd_group sub_cmd2
Performing upgrade
sub_cmd_group
sub_cmd2
-----------
> --help
Usage: test.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  sub_cmd_group
  top_cmd1
  top_cmd2
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

单击:如何将操作应用于所有命令和子命令,但允许命令选择退出? 的相关文章

随机推荐

  • Altair 中的平行坐标

    我想做一个具有多个 y 轴的平行坐标图 我已经找到了如何在 Vega Lite 中做到这一点here但我还没有找到使用 Altair 的方法 只有一个非常简单的例子其中所有 y 轴都相同 有什么办法吗这个情节在牵牛星 请注意 这种图表不是
  • 真实用户ID、有效用户ID和保存用户ID之间的区别

    我已经知道真实用户ID 它是系统中用户的唯一编号 在我的系统上 我的uid is echo UID 1014 另外两个ID代表什么 以及有什么用有效用户id and 已保存的用户 ID我们在系统中的什么地方使用它们 区分真实用户 ID 和有
  • 在iOS7半透明导航栏中获取正确的颜色

    如何为 iOS 7 中的半透明导航栏获得正确的颜色 导航栏只是将给定的颜色调整为更亮的颜色 更改颜色的亮度或饱和度也无法提供正确的结果 有人有同样的烦恼吗 看看 Facebook 它似乎以某种方式起作用 他们有自己的颜色和半透明的导航栏 编
  • SSIS 循环遍历 Excel 工作表

    我正在使用SSIS2012 我试图将大约25个excel文件 每个文件包含大约70个 变量 表 导入到SQLserver2008中 我已经构建了它 以便它将循环遍历所有 Excel 工作表并导入第一个工作表 但这没有用 我如何循环所有 Ex
  • 将文件直接上传到 GAE 应用的 Google Cloud Storage

    我正在考虑从 Blobstore 切换到 Google Cloud Storage 以处理项目中的图像上传等问题 因为 Google 称 Blobstore 为 取代 在 Blobstore 中 多部分表单将直接提交 上传 到 Blobst
  • 在 ansible playbook 中使用 gitlab-ci vars

    我想使用 Ansible playbook 在 docker 容器内设置远程环境 该剧本将从 gitlab ci 运行 其中包含我在 Gitlab CI CD 配置中设置的变量 我怎样才能做到这一点 这是我想使用的模板 我该如何设置user
  • C 中的常量返回类型

    我正在阅读一些代码示例 它们返回了 const int 当我尝试编译示例代码时 出现了有关返回类型冲突的错误 所以我开始搜索 认为 const 是问题所在 当我删除它时 代码工作正常 不仅可以编译 而且按预期工作 但我从未能够找到专门与 c
  • 查找字符串中长度最大的所有单词

    我想从字符串中找到长度最大的所有单词 目前 结果只是第一个长度最大的 jumped1 而我想要它们全部 jumped1 jumped2 我该如何调整以下内容 function test str var newStr str split va
  • 同步多个 UITableView 实例的滚动位置

    我有一个项目 我需要在其中显示多个UITableViewiPad 上同一视图内的实例 它们也恰好被轮换 但我相当确定这是无关紧要的 用户应该不知道视图是由多个表视图组成的 因此 我想做到这一点 以便当我滚动一个表视图时 其他表视图也会同时滚
  • Hibernate Envers:跟踪 OneToMany 关系拥有方的修订

    我有两个经过审计的实体 A 和 B 实体 A 拥有实体 B 的集合 注释为一对多关系 将 A 的新实例插入数据库时 A 和 B 的所有行都处于同一修订版 假设为修订版 1 然后 A 上有更新 仅影响实体 B 的实例 级联类型为合并 因此 更
  • 如何使用 Puppeteer 访问 React 事件处理程序

    我不完全确定我明白我的要求 我希望有人能解释一下 我正在尝试在 NodeJS 上使用 Puppeteer 抓取网站 我已经选择了我需要的元素并访问它的属性 但是 我无法访问我需要的属性来提取我想要的信息 我想要的信息在下面的绿色框中 但是我
  • 为什么即使使用前向声明,我也不能在 BEGIN 块中调用稍后定义的 sub?

    这有效 use strict X xxxxxx sub X print shift 这会产生一个错误 use strict BEGIN X xxxxxx sub X print shift Error Undefined subroutin
  • 通过 REST 在超级账本上部署链代码时出现“获取链代码包字节时出错”

    我正在尝试通过 POST REST 在 hyperledger Bluemix 服务 上部署链码 链码 查询规范 jsonrpc 2 0 方法 部署 参数 类型 1 chaincodeID 路径 https github com romeo
  • 翻译微风验证消息

    改进我的示例 了解如何使用获得的元数据在淘汰赛中创建验证规则 http stackoverflow com questions 13662446 knockout validation using breeze utility 现在我使用微
  • 防止 GDB 单步执行函数(或文件)

    我有一些像这样的 C 代码 我正在使用 GDB 逐步执行 void foo int num void main Baz baz foo baz get 当我在main 我想步入foo 但我想跨过去baz get The GDB docs说
  • 如何推送(即刷新)发送到 TCP 流的数据

    RFC 793说TCP定义了一个 推送 函数来确保接收者收到数据 有时用户需要确保他们拥有的所有数据 提交给TCP已经传输了 为此目的一推 函数已定义 确保提交给 TCP 的数据是 实际传输的发送用户表明它应该是 推送给接收用户 推送会导致
  • 报亭应用程序需要推送通知吗?

    如果我提交一个不使用推送通知的报刊亭应用程序 而是在每次用户启动该应用程序时向我的服务器查询新内容 苹果会拒绝我的应用程序吗 IE 用户是否期望在所有报亭应用上推送 Thanks 不 Apple 的指南并不强制要求使用推送通知 并且您的应用
  • 在服务内调用 getSystemService

    我正在尝试编写一项在 Gear Live 上获取心率的服务 遵循此处的问题从 传感器 Samsung Gear Live 获取心率 如果我把这部分 Log d TAG prepare to call getSystemService mSe
  • 使用 Nodejs 和 pug 进行客户端模板化

    我正在构建一个网络应用程序 它在客户端构建了动态小部件 目前我使用nodejs和pug作为我的服务器端模板库 我喜欢pug的简单性 我希望在服务器上有一系列小的 pug 文件 客户端可以将其用作构建块来构造用户所需的小部件 我尝试使用此处找
  • 单击:如何将操作应用于所有命令和子命令,但允许命令选择退出?

    我有一个案例 我想自动运行一个常用函数 check upgrade 对于我的大多数单击命令和子命令 但在少数情况下我不想运行它 我想我可以有一个可以添加的装饰器 例如 bypass upgrade check 对于命令 其中check up