json rpc commander 封装

This commit is contained in:
root
2025-07-11 21:27:17 +08:00
parent 2a16067367
commit 7b50c6e28e
37 changed files with 3518 additions and 2596 deletions

View File

@@ -0,0 +1,17 @@
#!/usr/bin/env python3
"""
Commander模块
"""
from .types import CommandConfig
from .parser import ArgumentParser
from .base import JSONRPCCommander
from .simple import SimpleJSONRPCCommander, create_simple_commander
__all__ = [
"CommandConfig",
"ArgumentParser",
"JSONRPCCommander",
"SimpleJSONRPCCommander",
"create_simple_commander"
]

View File

@@ -0,0 +1,178 @@
#!/usr/bin/env python3
"""
JSON-RPC Commander基类
"""
import sys
import json
from abc import ABC, abstractmethod
from typing import Dict, Any, List
from .types import CommandConfig
from .parser import ArgumentParser
from ..jsonrpc import create_response_handler
from ..logger import logger
class JSONRPCCommander(ABC):
"""JSON-RPC Commander 基类"""
def __init__(self, service_name: str):
"""
初始化Commander
Args:
service_name: 服务名称
"""
self.service_name = service_name
self.rpc_handler = None
self.rpc_progress_reporter = None
self.commands: Dict[str, CommandConfig] = {}
self.parser = ArgumentParser(self.commands)
self._setup_rpc_handler()
self._register_commands()
# 重新创建parser以包含注册的命令
self.parser = ArgumentParser(self.commands)
def _setup_rpc_handler(self) -> None:
"""设置RPC处理器"""
try:
self.rpc_handler = create_response_handler()
logger.debug(f"JSON-RPC handler initialized for {self.service_name}")
except Exception as e:
logger.warning(f"Failed to initialize JSON-RPC handler: {e}")
self.rpc_handler = None
@abstractmethod
def _register_commands(self) -> None:
"""注册命令 - 子类必须实现"""
pass
def register_command(self,
name: str,
description: str,
required_args: List[str] = None,
optional_args: Dict[str, Dict[str, Any]] = None) -> None:
"""
注册命令
Args:
name: 命令名称
description: 命令描述
required_args: 必需参数列表
optional_args: 可选参数配置
"""
self.commands[name] = CommandConfig(
name=name,
description=description,
required_args=required_args or [],
optional_args=optional_args or {}
)
def parse_arguments(self, args: List[str]) -> tuple:
"""
解析命令行参数
Args:
args: 命令行参数列表
Returns:
(command, parsed_args) 元组
"""
try:
return self.parser.parse_arguments(args)
except ValueError as e:
self._send_error("INVALID_ARGS", str(e))
sys.exit(1)
def _show_usage(self) -> None:
"""显示使用说明"""
usage_info = {
"service": self.service_name,
"usage": f"python -m {self.service_name} <command> [args...]",
"commands": {}
}
for cmd_name, cmd_config in self.commands.items():
cmd_info = {
"description": cmd_config.description,
"required_args": cmd_config.required_args,
"optional_args": {}
}
for arg_name, arg_config in cmd_config.optional_args.items():
cmd_info["optional_args"][arg_name] = {
"type": arg_config.get('type', str).__name__,
"default": arg_config.get('default'),
"choices": arg_config.get('choices'),
"description": arg_config.get('description', '')
}
usage_info["commands"][cmd_name] = cmd_info
self._send_response(usage_info)
def _send_response(self, result: Any) -> None:
"""发送成功响应"""
if self.rpc_handler:
self.rpc_handler.success(result)
else:
print(json.dumps(result, indent=2, ensure_ascii=False))
def _send_error(self, error_code: str, message: str) -> None:
"""发送错误响应"""
if self.rpc_handler:
self.rpc_handler.error(error_code, message)
else:
error_response = {
"error": {
"code": error_code,
"message": message
}
}
print(json.dumps(error_response, indent=2, ensure_ascii=False))
@abstractmethod
def execute_command(self, command: str, args: Dict[str, Any]) -> Any:
"""
执行命令 - 子类必须实现
Args:
command: 命令名称
args: 解析后的参数
Returns:
命令执行结果
"""
pass
def run(self, argv: List[str] = None) -> None:
"""
运行Commander
Args:
argv: 命令行参数默认使用sys.argv[1:]
"""
if argv is None:
argv = sys.argv[1:]
if len(argv) == 0:
self._show_usage()
return
try:
# 解析参数
command, args = self.parse_arguments(argv)
# 执行命令
result = self.execute_command(command, args)
# 发送响应
self._send_response(result)
except KeyboardInterrupt:
self._send_error("INTERRUPTED", "Command interrupted by user")
sys.exit(1)
except Exception as e:
logger.error(f"Command execution failed: {e}")
self._send_error("INTERNAL_ERROR", str(e))
sys.exit(1)

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""
参数解析器
"""
import sys
from typing import List, Dict, Any, Tuple
from .types import CommandConfig
from ..logger import logger
class ArgumentParser:
"""命令行参数解析器"""
def __init__(self, commands: Dict[str, CommandConfig]):
self.commands = commands
def parse_arguments(self, args: List[str]) -> Tuple[str, Dict[str, Any]]:
"""
解析命令行参数
Args:
args: 命令行参数列表
Returns:
(command, parsed_args) 元组
"""
if len(args) < 1:
raise ValueError("No command provided")
command = args[0]
if command not in self.commands:
raise ValueError(f"Unknown command: {command}")
command_config = self.commands[command]
# 解析参数
parsed_args = {}
remaining_args = args[1:]
# 处理必需参数
if len(remaining_args) < len(command_config.required_args):
missing_args = command_config.required_args[len(remaining_args):]
raise ValueError(f"Missing required arguments: {missing_args}")
# 设置必需参数
for i, arg_name in enumerate(command_config.required_args):
parsed_args[arg_name] = remaining_args[i]
# 处理可选参数
optional_start = len(command_config.required_args)
i = optional_start
while i < len(remaining_args):
arg = remaining_args[i]
if arg.startswith('--'):
arg_name = arg[2:]
if arg_name in command_config.optional_args:
arg_config = command_config.optional_args[arg_name]
# 检查是否需要值
if arg_config.get('type') == bool:
parsed_args[arg_name] = True
i += 1
elif i + 1 < len(remaining_args) and not remaining_args[i + 1].startswith('--'):
value_str = remaining_args[i + 1]
# 类型转换
try:
arg_type = arg_config.get('type', str)
if arg_type == bool:
value = value_str.lower() in ('true', '1', 'yes', 'on')
else:
value = arg_type(value_str)
# 检查选择范围
choices = arg_config.get('choices')
if choices and value not in choices:
raise ValueError(f"Invalid value for {arg_name}: {value}. Choices: {choices}")
parsed_args[arg_name] = value
i += 2
except (ValueError, TypeError) as e:
raise ValueError(f"Invalid value for {arg_name}: {value_str}. {e}")
else:
raise ValueError(f"Missing value for argument: {arg_name}")
else:
logger.warning(f"Unknown optional argument: {arg_name}")
i += 1
else:
i += 1
# 设置默认值
for arg_name, arg_config in command_config.optional_args.items():
if arg_name not in parsed_args:
default_value = arg_config.get('default')
if default_value is not None:
parsed_args[arg_name] = default_value
return command, parsed_args

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env python3
"""
简化的JSON-RPC Commander
"""
from typing import Dict, Any, List, Callable
from .base import JSONRPCCommander
class SimpleJSONRPCCommander(JSONRPCCommander):
"""简化的JSON-RPC Commander用于快速创建命令行工具"""
def __init__(self, service_name: str):
self.command_handlers: Dict[str, Callable] = {}
super().__init__(service_name)
def _register_commands(self) -> None:
"""默认不注册任何命令"""
pass
def add_command(self,
name: str,
handler: Callable,
description: str,
required_args: List[str] = None,
optional_args: Dict[str, Dict[str, Any]] = None) -> None:
"""
添加命令处理器
Args:
name: 命令名称
handler: 命令处理函数
description: 命令描述
required_args: 必需参数列表
optional_args: 可选参数配置
"""
self.register_command(name, description, required_args, optional_args)
self.command_handlers[name] = handler
# 重新创建parser以包含新命令
from .parser import ArgumentParser
self.parser = ArgumentParser(self.commands)
def execute_command(self, command: str, args: Dict[str, Any]) -> Any:
"""执行命令"""
if command not in self.command_handlers:
raise ValueError(f"No handler for command: {command}")
handler = self.command_handlers[command]
return handler(**args)
# 便捷函数
def create_simple_commander(service_name: str) -> SimpleJSONRPCCommander:
"""创建简单的JSON-RPC Commander"""
return SimpleJSONRPCCommander(service_name)

View File

@@ -0,0 +1,15 @@
#!/usr/bin/env python3
"""
Commander相关的数据类型定义
"""
from dataclasses import dataclass
from typing import Dict, Any, List
@dataclass
class CommandConfig:
"""命令配置"""
name: str
description: str
required_args: List[str]
optional_args: Dict[str, Dict[str, Any]]