"""
"""
import argparse
import inspect
import importlib.util
import os
import sys
from functools import wraps
from typing import Dict, List, Callable, Any, Optional
class Argument:
"""存储单个参数的定义"""
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
class Command:
cmd_entry_dict: Dict[Callable, 'Command'] = {}
cmd_dict: Dict[str, 'Command'] = {}
def __init__(self, name: str, help_info: str):
self.name = name
self.help_info = help_info
self.argument_list = []
self.entry = None
@classmethod
def get_entry(cls, entry: Callable):
if entry not in cls.cmd_entry_dict:
name = entry.__name__
cmd = Command(name, f'执行 {name} 命令')
cmd.set_entry(entry)
cls.cmd_entry_dict[entry] = cmd
else:
cmd = cls.cmd_entry_dict[entry]
return cmd
def add_argument(self, arg: Argument):
self.argument_list.append(arg)
def set_entry(self, entry: Callable):
self.entry = entry
def set_name_help(self, name: str, help_info: str):
self.name = name
self.help_info = help_info
def command(name: Optional[str] = None, help_info: Optional[str] = None):
"""装饰器:标记一个函数为子命令"""
def decorator(func: Callable):
cmd = Command.get_entry(func)
cmd_name = name or func.__name__
cmd_help = help_info or func.__doc__ or f"执行 {cmd_name} 命令"
cmd.set_name_help(cmd_name, cmd_help)
Command.cmd_dict[cmd_name] = cmd
return func
return decorator
def argument(*args, **kwargs):
"""装饰器:为当前命令添加参数(必须在 @command 之后使用)"""
def decorator(func: Callable):
cmd = Command.get_entry(func)
cmd.add_argument(Argument(*args, **kwargs))
return func
return decorator
def build_parser(prog: str = None, description: str = None) -> argparse.ArgumentParser:
"""根据命令注册表构建 argparse 解析器"""
parser = argparse.ArgumentParser(prog=prog, description=description)
subparsers = parser.add_subparsers(dest='command', required=True, help='可用的子命令')
for _, cmd in Command.cmd_entry_dict.items():
subparser = subparsers.add_parser(cmd.name, help=cmd.help_info)
for arg in cmd.argument_list:
subparser.add_argument(*arg.args, **arg.kwargs)
return parser
def dispatch(args: argparse.Namespace):
"""执行对应的命令函数,传入解析后的参数"""
cmd_name = args.command
if cmd_name not in Command.cmd_dict:
raise ValueError(f"Unknown command: {cmd_name}")
cmd = Command.cmd_dict[cmd_name]
kwargs = {k: v for k, v in vars(args).items() if k != 'command'}
return cmd.entry(**kwargs)
def run(main_prog: str = None, description: str = None, commands_dir: str = "commands"):
"""主入口:加载命令、构建解析器、执行"""
parser = build_parser(prog=main_prog, description=description)
parsed_args = parser.parse_args()
return dispatch(parsed_args)