如何为Click所有命令/子命令统一执行操作并支持命令选择退出?
解决Click命令嵌套层级下的升级检查自动执行问题
要实现所有层级的Click命令(包括嵌套子命令)自动执行check_upgrade(),仅标记@bypass_upgrade_check的命令跳过,核心是递归定位最终被调用的具体命令,可以通过自定义click.Group类来实现。下面是完整的解决方案:
1. 定义跳过检查的装饰器
首先我们需要一个装饰器,给不需要检查的命令打上标记:
import click def bypass_upgrade_check(f): # 给命令函数添加自定义属性,标记需要跳过升级检查 f._bypass_upgrade = True return f
2. 自定义Group类处理递归查找
重写click.Group的invoke方法,递归遍历命令树,找到最终被用户调用的具体命令(而非中间的子命令组),然后判断是否需要执行升级检查:
class UpgradeCheckGroup(click.Group): def invoke(self, ctx): # 递归查找最终被调用的目标命令 def find_target_command(current_ctx): invoked_subcmd = current_ctx.invoked_subcommand if invoked_subcmd: # 获取子命令对象 sub_cmd = current_ctx.command.get_command(current_ctx, invoked_subcmd) if isinstance(sub_cmd, click.Group): # 如果是子命令组,继续递归查找下一层 return find_target_command(current_ctx) else: # 找到具体执行的命令 return sub_cmd else: # 没有子命令,当前命令就是目标 return current_ctx.command # 获取最终目标命令 target_command = find_target_command(ctx) # 检查是否需要跳过升级检查 if not getattr(target_command, '_bypass_upgrade', False): check_upgrade() # 执行原始的命令逻辑 return super().invoke(ctx) # 示例升级检查函数,替换成你的实际逻辑 def check_upgrade(): click.echo("🔍 Running upgrade check...")
3. 使用自定义Group构建命令树
在顶层命令组使用cls=UpgradeCheckGroup参数,之后所有嵌套的子命令组和命令都会自动应用升级检查逻辑:
@click.group(cls=UpgradeCheckGroup) def cli(): pass # 顶层普通命令 - 会执行升级检查 @cli.command() def sync(): click.echo("✅ Syncing data...") # 顶层跳过检查的命令 @cli.command() @bypass_upgrade_check def version(): click.echo("📦 Version 1.0.0") # 嵌套子命令组 @cli.group() def config(): pass # 子组下的普通命令 - 会执行升级检查 @config.command() def set(): click.echo("⚙️ Setting configuration...") # 子组下跳过检查的命令 @config.command() @bypass_upgrade_check def show(): click.echo("📋 Showing configuration...") # 更深层的嵌套子组 @config.group() def advanced(): pass @advanced.command() def reset(): click.echo("🔄 Resetting advanced config...") @advanced.command() @bypass_upgrade_check def export(): click.echo("📤 Exporting advanced config...") if __name__ == "__main__": cli()
关键逻辑说明
- 递归查找命令:通过
find_target_command函数,从当前上下文开始,逐层深入子命令组,直到找到最终执行的具体命令(非Group类型)。 - 标记检查:利用自定义属性
_bypass_upgrade判断是否跳过检查,这个属性通过装饰器添加到命令函数上。 - 兼容性:自定义Group完全兼容Click的原有语法,嵌套多少层子命令都能正常工作。
额外场景处理
如果希望直接调用子命令组(比如cli config)时也跳过升级检查,只需给子组添加@bypass_upgrade_check装饰器:
@cli.group() @bypass_upgrade_check def config(): click.echo("📂 Config subgroup")
这样执行cli config时就不会触发升级检查了。
内容的提问来源于stack exchange,提问作者agelter
相关产品推荐
相关产品推荐

