如何在Python中自动构建CLI工具的调用类?
动态构建CLI链式调用方案
核心思路
利用Python的__getattr__动态属性创建和__call__实例调用特性,递归构建插件层级结构,实现类似命令行补全的链式调用体验,自动拼接命令并执行或返回命令列表。
实现代码
基础CLI命令类
import subprocess class CLICommand: def __init__(self, parent_command=None): # 存储当前已拼接的命令片段,初始为根命令(如['command.exe']) self._command = parent_command.copy() if parent_command else [] def __getattr__(self, name): # 动态生成子插件/子命令实例 new_command = self._command + [name] sub_cmd = CLICommand(new_command) # 缓存属性,避免重复创建 setattr(self, name, sub_cmd) return sub_cmd def __call__(self, *args, **kwargs): # 拼接最终命令:基础命令 + 位置参数 + 关键字参数(转成flag格式) final_command = self._command.copy() final_command.extend(args) for flag, value in kwargs.items(): final_command.append(f"-{flag}") final_command.append(str(value)) # 执行命令并返回结果,若仅需命令列表直接return final_command exec_result = subprocess.run( final_command, capture_output=True, text=True, check=False # 不主动抛出异常,由测试环境处理返回码 ) return { 'full_command': final_command, 'stdout': exec_result.stdout, 'stderr': exec_result.stderr, 'return_code': exec_result.returncode }
初始化与调用示例
# 初始化根CLI,传入你的基础命令 cli = CLICommand(['command.exe']) # 链式调用生成并执行命令 # 对应终端命令:command.exe plugin subplugin subsubplugin arg1 arg2 -a flaga -b flagb test_result = cli.plugin.subplugin.subsubplugin('arg1', 'arg2', a='flaga', b='flagb') # 查看结果 print(test_result['full_command']) print(test_result['stdout'])
基于现有命令字典预加载结构(支持IDE补全)
如果需要从你的commands字典预加载所有插件层级,让IDE能识别属性(实现类命令补全),可以添加递归加载函数:
def preload_commands(cli_instance, cmd_dict): def _recursive_load(parent, current_level): for key, content in current_level.items(): # 触发属性创建并缓存 sub_cmd = getattr(parent, key) if isinstance(content, dict): # 递归加载子层级 _recursive_load(sub_cmd, content) _recursive_load(cli_instance, cmd_dict) # 你的现有命令字典示例 your_commands = { "plugin": { "subplugin": { "subsubplugin": ['-a', 'flaga', '-b', 'flagb'] } } } # 预加载后,IDE输入cli.plugin.时会提示subplugin preload_commands(cli, your_commands)
关键特性说明
- 动态属性创建:通过
__getattr__自动生成未定义的插件/子插件属性,无需手动编写每个命令的函数。 - 灵活参数处理:支持位置参数和关键字参数(自动转成
-flag value格式),适配不同CLI参数需求。 - 可扩展性:无论是执行命令返回结果,还是仅返回命令列表注入现有测试架构,只需修改
__call__方法的返回逻辑即可。
内容的提问来源于stack exchange,提问作者Shahaf Edri
相关产品推荐
相关产品推荐

