json rpc commander 封装
This commit is contained in:
17
python_core/utils/commander/__init__.py
Normal file
17
python_core/utils/commander/__init__.py
Normal 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"
|
||||
]
|
||||
178
python_core/utils/commander/base.py
Normal file
178
python_core/utils/commander/base.py
Normal 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)
|
||||
103
python_core/utils/commander/parser.py
Normal file
103
python_core/utils/commander/parser.py
Normal 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
|
||||
54
python_core/utils/commander/simple.py
Normal file
54
python_core/utils/commander/simple.py
Normal 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)
|
||||
15
python_core/utils/commander/types.py
Normal file
15
python_core/utils/commander/types.py
Normal 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]]
|
||||
@@ -2,7 +2,6 @@
|
||||
Helper utilities for MixVideo V2
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Union
|
||||
import ffmpeg
|
||||
|
||||
@@ -191,42 +191,3 @@ def parse_request(request_str: str) -> Dict[str, Any]:
|
||||
except json.JSONDecodeError as e:
|
||||
raise ValueError(f"Invalid JSON: {e}")
|
||||
|
||||
|
||||
def example_video_generation():
|
||||
"""Example of how to use JSON-RPC for video generation"""
|
||||
rpc = create_response_handler("video_gen_001")
|
||||
progress = create_progress_reporter()
|
||||
|
||||
try:
|
||||
# Report progress steps
|
||||
progress.step("upload", "[1/4] 正在上传图片到云存储...")
|
||||
# ... upload logic ...
|
||||
|
||||
progress.step("submit", "[2/4] 正在提交视频生成任务...")
|
||||
# ... submit logic ...
|
||||
|
||||
progress.step("wait", "[3/4] 正在等待视频生成完成...")
|
||||
# ... wait logic ...
|
||||
|
||||
progress.step("download", "[4/4] 正在下载视频到本地...")
|
||||
# ... download logic ...
|
||||
|
||||
progress.complete("[完成] 视频生成并下载成功")
|
||||
|
||||
# Send final result
|
||||
result = {
|
||||
"status": True,
|
||||
"video_path": "/path/to/video.mp4",
|
||||
"video_url": "https://example.com/video.mp4",
|
||||
"message": "视频生成并下载成功"
|
||||
}
|
||||
rpc.success(result)
|
||||
|
||||
except Exception as e:
|
||||
progress.error(f"生成失败: {str(e)}")
|
||||
rpc.error(JSONRPCError.GENERATION_FAILED, "Video generation failed", str(e))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test the JSON-RPC module
|
||||
example_video_generation()
|
||||
|
||||
@@ -7,7 +7,6 @@ from pathlib import Path
|
||||
from loguru import logger
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
from ..config import settings
|
||||
|
||||
|
||||
22
python_core/utils/progress/__init__.py
Normal file
22
python_core/utils/progress/__init__.py
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
进度管理模块
|
||||
"""
|
||||
|
||||
from .types import ProgressInfo, TaskResult
|
||||
from .task import ProgressiveTask
|
||||
from .reporter import ProgressReporter
|
||||
from .generator import ProgressGenerator
|
||||
from .decorators import with_progress
|
||||
from .commander import ProgressJSONRPCCommander, create_progress_commander
|
||||
|
||||
__all__ = [
|
||||
"ProgressInfo",
|
||||
"TaskResult",
|
||||
"ProgressiveTask",
|
||||
"ProgressReporter",
|
||||
"ProgressGenerator",
|
||||
"with_progress",
|
||||
"ProgressJSONRPCCommander",
|
||||
"create_progress_commander"
|
||||
]
|
||||
216
python_core/utils/progress/commander.py
Normal file
216
python_core/utils/progress/commander.py
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
带进度的JSON-RPC Commander
|
||||
"""
|
||||
|
||||
import time
|
||||
from abc import abstractmethod
|
||||
from typing import Dict, Any, Callable
|
||||
from contextlib import contextmanager
|
||||
|
||||
from .types import ProgressInfo, TaskResult
|
||||
from .task import ProgressiveTask
|
||||
from .reporter import ProgressReporter
|
||||
from ..commander import JSONRPCCommander
|
||||
from ..logger import logger
|
||||
|
||||
class ProgressJSONRPCCommander(JSONRPCCommander):
|
||||
"""带进度条的JSON-RPC Commander基类"""
|
||||
|
||||
def __init__(self, service_name: str):
|
||||
super().__init__(service_name)
|
||||
self.progress_reporter = ProgressReporter(service_name)
|
||||
|
||||
@contextmanager
|
||||
def create_task(self, task_name: str, total_steps: int = 100):
|
||||
"""创建带进度的任务上下文"""
|
||||
task = ProgressiveTask(task_name, total_steps)
|
||||
task.set_progress_callback(self.progress_reporter.report_progress)
|
||||
|
||||
try:
|
||||
task.start()
|
||||
yield task
|
||||
task.finish()
|
||||
except Exception as e:
|
||||
task._report_progress(f"任务失败: {str(e)}")
|
||||
raise
|
||||
|
||||
def execute_progressive_command(self, command: str, args: Dict[str, Any]) -> TaskResult:
|
||||
"""
|
||||
执行带进度的命令
|
||||
|
||||
Args:
|
||||
command: 命令名称
|
||||
args: 命令参数
|
||||
|
||||
Returns:
|
||||
任务结果
|
||||
"""
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
# 调用子类实现的进度命令执行
|
||||
result = self._execute_with_progress(command, args)
|
||||
|
||||
total_time = time.time() - start_time
|
||||
|
||||
return TaskResult(
|
||||
success=True,
|
||||
result=result,
|
||||
total_time=total_time
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
total_time = time.time() - start_time
|
||||
logger.error(f"Progressive command failed: {e}")
|
||||
|
||||
return TaskResult(
|
||||
success=False,
|
||||
error=str(e),
|
||||
total_time=total_time
|
||||
)
|
||||
|
||||
@abstractmethod
|
||||
def _execute_with_progress(self, command: str, args: Dict[str, Any]) -> Any:
|
||||
"""
|
||||
执行带进度的命令 - 子类必须实现
|
||||
|
||||
Args:
|
||||
command: 命令名称
|
||||
args: 命令参数
|
||||
|
||||
Returns:
|
||||
命令执行结果
|
||||
"""
|
||||
pass
|
||||
|
||||
def execute_command(self, command: str, args: Dict[str, Any]) -> Any:
|
||||
"""
|
||||
执行命令(重写基类方法以支持进度)
|
||||
|
||||
Args:
|
||||
command: 命令名称
|
||||
args: 命令参数
|
||||
|
||||
Returns:
|
||||
命令执行结果
|
||||
"""
|
||||
# 先进行参数类型转换
|
||||
converted_args = self._convert_args_types(command, args)
|
||||
|
||||
# 检查是否是需要进度报告的命令
|
||||
if self._is_progressive_command(command):
|
||||
task_result = self.execute_progressive_command(command, converted_args)
|
||||
|
||||
if task_result.success:
|
||||
return task_result.result
|
||||
else:
|
||||
raise Exception(task_result.error)
|
||||
else:
|
||||
# 普通命令,调用子类实现
|
||||
return self._execute_simple_command(command, converted_args)
|
||||
|
||||
def _convert_args_types(self, command: str, args: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
转换参数类型
|
||||
|
||||
Args:
|
||||
command: 命令名称
|
||||
args: 原始参数
|
||||
|
||||
Returns:
|
||||
转换后的参数
|
||||
"""
|
||||
if command not in self.commands:
|
||||
return args
|
||||
|
||||
command_config = self.commands[command]
|
||||
converted_args = args.copy()
|
||||
|
||||
# 转换可选参数的类型
|
||||
for arg_name, arg_config in command_config.optional_args.items():
|
||||
if arg_name in converted_args:
|
||||
arg_type = arg_config.get('type', str)
|
||||
try:
|
||||
if arg_type == bool:
|
||||
# 布尔类型特殊处理
|
||||
value = converted_args[arg_name]
|
||||
if isinstance(value, str):
|
||||
converted_args[arg_name] = value.lower() in ('true', '1', 'yes', 'on')
|
||||
elif arg_type != str and isinstance(converted_args[arg_name], str):
|
||||
# 其他类型从字符串转换
|
||||
converted_args[arg_name] = arg_type(converted_args[arg_name])
|
||||
except (ValueError, TypeError) as e:
|
||||
logger.warning(f"Failed to convert argument {arg_name}: {e}")
|
||||
|
||||
return converted_args
|
||||
|
||||
def _is_progressive_command(self, command: str) -> bool:
|
||||
"""
|
||||
判断是否是需要进度报告的命令
|
||||
子类可以重写此方法来指定哪些命令需要进度报告
|
||||
|
||||
Args:
|
||||
command: 命令名称
|
||||
|
||||
Returns:
|
||||
是否需要进度报告
|
||||
"""
|
||||
# 默认所有命令都需要进度报告
|
||||
return True
|
||||
|
||||
def _execute_simple_command(self, command: str, args: Dict[str, Any]) -> Any:
|
||||
"""
|
||||
执行简单命令(不需要进度报告)
|
||||
子类可以重写此方法来处理不需要进度的命令
|
||||
|
||||
Args:
|
||||
command: 命令名称
|
||||
args: 命令参数
|
||||
|
||||
Returns:
|
||||
命令执行结果
|
||||
"""
|
||||
# 默认调用带进度的执行方法
|
||||
return self._execute_with_progress(command, args)
|
||||
|
||||
# 便捷函数
|
||||
def create_progress_commander(service_name: str):
|
||||
"""创建带进度的JSON-RPC Commander"""
|
||||
|
||||
class SimpleProgressCommander(ProgressJSONRPCCommander):
|
||||
def __init__(self):
|
||||
super().__init__(service_name)
|
||||
self.command_handlers: Dict[str, Callable] = {}
|
||||
self._progressive_commands = set()
|
||||
|
||||
def _register_commands(self):
|
||||
pass
|
||||
|
||||
def add_command(self, name: str, handler: Callable, description: str,
|
||||
required_args: list = None, optional_args: dict = None,
|
||||
progressive: bool = True):
|
||||
"""添加命令"""
|
||||
self.register_command(name, description, required_args, optional_args)
|
||||
self.command_handlers[name] = handler
|
||||
|
||||
# 标记是否需要进度报告
|
||||
if progressive:
|
||||
self._progressive_commands.add(name)
|
||||
|
||||
def _is_progressive_command(self, command: str) -> bool:
|
||||
return command in self._progressive_commands
|
||||
|
||||
def _execute_with_progress(self, command: str, args: Dict[str, Any]) -> Any:
|
||||
if command in self.command_handlers:
|
||||
return self.command_handlers[command](**args)
|
||||
else:
|
||||
raise ValueError(f"No handler for command: {command}")
|
||||
|
||||
def _execute_simple_command(self, command: str, args: Dict[str, Any]) -> Any:
|
||||
if command in self.command_handlers:
|
||||
return self.command_handlers[command](**args)
|
||||
else:
|
||||
raise ValueError(f"No handler for command: {command}")
|
||||
|
||||
return SimpleProgressCommander()
|
||||
27
python_core/utils/progress/decorators.py
Normal file
27
python_core/utils/progress/decorators.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
进度装饰器
|
||||
"""
|
||||
|
||||
def with_progress(total_steps: int = 100, task_name: str = None):
|
||||
"""
|
||||
为函数添加进度报告的装饰器
|
||||
|
||||
Args:
|
||||
total_steps: 总步数
|
||||
task_name: 任务名称
|
||||
"""
|
||||
def decorator(func):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
name = task_name or func.__name__
|
||||
|
||||
if hasattr(self, 'create_task'):
|
||||
with self.create_task(name, total_steps) as task:
|
||||
# 将task对象传递给函数
|
||||
return func(self, task, *args, **kwargs)
|
||||
else:
|
||||
# 如果不是ProgressJSONRPCCommander,直接执行
|
||||
return func(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
return decorator
|
||||
44
python_core/utils/progress/generator.py
Normal file
44
python_core/utils/progress/generator.py
Normal file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
进度生成器工具
|
||||
"""
|
||||
|
||||
from .task import ProgressiveTask
|
||||
|
||||
class ProgressGenerator:
|
||||
"""进度生成器工具类"""
|
||||
|
||||
@staticmethod
|
||||
def for_iterable(iterable, task: ProgressiveTask, description: str = "处理中"):
|
||||
"""为可迭代对象添加进度报告"""
|
||||
total = len(iterable) if hasattr(iterable, '__len__') else 100
|
||||
task.total_steps = total
|
||||
|
||||
for i, item in enumerate(iterable):
|
||||
task.update(i, f"{description} {i+1}/{total}")
|
||||
yield item
|
||||
|
||||
task.finish(f"{description}完成")
|
||||
|
||||
@staticmethod
|
||||
def for_range(start: int, end: int, task: ProgressiveTask, description: str = "处理中"):
|
||||
"""为范围添加进度报告"""
|
||||
total = end - start
|
||||
task.total_steps = total
|
||||
|
||||
for i in range(start, end):
|
||||
task.update(i - start, f"{description} {i+1}/{total}")
|
||||
yield i
|
||||
|
||||
task.finish(f"{description}完成")
|
||||
|
||||
@staticmethod
|
||||
def for_steps(steps: int, task: ProgressiveTask, description: str = "处理中"):
|
||||
"""为步数添加进度报告"""
|
||||
task.total_steps = steps
|
||||
|
||||
for i in range(steps):
|
||||
task.update(i, f"{description} {i+1}/{steps}")
|
||||
yield i
|
||||
|
||||
task.finish(f"{description}完成")
|
||||
58
python_core/utils/progress/reporter.py
Normal file
58
python_core/utils/progress/reporter.py
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
进度报告器
|
||||
"""
|
||||
|
||||
from .types import ProgressInfo
|
||||
from ..jsonrpc import create_progress_reporter
|
||||
from ..logger import logger
|
||||
|
||||
class ProgressReporter:
|
||||
"""进度报告器"""
|
||||
|
||||
def __init__(self, service_name: str):
|
||||
self.service_name = service_name
|
||||
self.rpc_progress_reporter = None
|
||||
self._setup_progress_reporter()
|
||||
|
||||
def _setup_progress_reporter(self) -> None:
|
||||
"""设置进度报告器"""
|
||||
try:
|
||||
self.rpc_progress_reporter = create_progress_reporter()
|
||||
logger.debug(f"Progress reporter initialized for {self.service_name}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to initialize progress reporter: {e}")
|
||||
self.rpc_progress_reporter = None
|
||||
|
||||
def report_progress(self, progress: ProgressInfo) -> None:
|
||||
"""报告进度"""
|
||||
if self.rpc_progress_reporter:
|
||||
# JSON-RPC进度报告
|
||||
self.rpc_progress_reporter.report(
|
||||
step=self.service_name,
|
||||
progress=progress.percentage / 100.0, # 转换为0-1范围
|
||||
message=progress.message,
|
||||
details={
|
||||
"current": progress.current,
|
||||
"total": progress.total,
|
||||
"elapsed_time": progress.elapsed_time,
|
||||
"estimated_remaining": progress.estimated_remaining
|
||||
}
|
||||
)
|
||||
else:
|
||||
# 简单的控制台输出
|
||||
print(f"Progress: {progress.percentage:.1f}% - {progress.message}")
|
||||
|
||||
def report_step(self, step_name: str, message: str) -> None:
|
||||
"""报告步骤"""
|
||||
if self.rpc_progress_reporter:
|
||||
self.rpc_progress_reporter.step(step_name, message)
|
||||
else:
|
||||
print(f"Step: {step_name} - {message}")
|
||||
|
||||
def report_complete(self, message: str = "完成") -> None:
|
||||
"""报告完成"""
|
||||
if self.rpc_progress_reporter:
|
||||
self.rpc_progress_reporter.complete(message)
|
||||
else:
|
||||
print(f"Complete: {message}")
|
||||
69
python_core/utils/progress/task.py
Normal file
69
python_core/utils/progress/task.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
渐进式任务管理
|
||||
"""
|
||||
|
||||
import time
|
||||
from typing import Optional, Callable
|
||||
|
||||
from .types import ProgressInfo
|
||||
from ..logger import logger
|
||||
|
||||
class ProgressiveTask:
|
||||
"""渐进式任务包装器"""
|
||||
|
||||
def __init__(self, task_name: str, total_steps: int = 100):
|
||||
self.task_name = task_name
|
||||
self.total_steps = total_steps
|
||||
self.current_step = 0
|
||||
self.start_time = None
|
||||
self.progress_callback: Optional[Callable[[ProgressInfo], None]] = None
|
||||
|
||||
def set_progress_callback(self, callback: Callable[[ProgressInfo], None]):
|
||||
"""设置进度回调"""
|
||||
self.progress_callback = callback
|
||||
|
||||
def start(self):
|
||||
"""开始任务"""
|
||||
self.start_time = time.time()
|
||||
self.current_step = 0
|
||||
self._report_progress("任务开始")
|
||||
logger.debug(f"Task started: {self.task_name}")
|
||||
|
||||
def update(self, step: int = None, message: str = ""):
|
||||
"""更新进度"""
|
||||
if step is not None:
|
||||
self.current_step = step
|
||||
else:
|
||||
self.current_step += 1
|
||||
|
||||
self._report_progress(message)
|
||||
|
||||
def finish(self, message: str = "任务完成"):
|
||||
"""完成任务"""
|
||||
self.current_step = self.total_steps
|
||||
self._report_progress(message)
|
||||
logger.debug(f"Task finished: {self.task_name}")
|
||||
|
||||
def _report_progress(self, message: str):
|
||||
"""报告进度"""
|
||||
if self.progress_callback and self.start_time:
|
||||
elapsed = time.time() - self.start_time
|
||||
|
||||
# 估算剩余时间
|
||||
if self.current_step > 0:
|
||||
avg_time_per_step = elapsed / self.current_step
|
||||
remaining_steps = self.total_steps - self.current_step
|
||||
estimated_remaining = avg_time_per_step * remaining_steps
|
||||
else:
|
||||
estimated_remaining = 0.0
|
||||
|
||||
progress = ProgressInfo(
|
||||
current=self.current_step,
|
||||
total=self.total_steps,
|
||||
message=message,
|
||||
elapsed_time=elapsed,
|
||||
estimated_remaining=estimated_remaining
|
||||
)
|
||||
|
||||
self.progress_callback(progress)
|
||||
31
python_core/utils/progress/types.py
Normal file
31
python_core/utils/progress/types.py
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
进度相关的数据类型定义
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Optional
|
||||
|
||||
@dataclass
|
||||
class ProgressInfo:
|
||||
"""进度信息"""
|
||||
current: int
|
||||
total: int
|
||||
message: str = ""
|
||||
percentage: float = 0.0
|
||||
elapsed_time: float = 0.0
|
||||
estimated_remaining: float = 0.0
|
||||
|
||||
def __post_init__(self):
|
||||
"""计算百分比"""
|
||||
if self.total > 0:
|
||||
self.percentage = (self.current / self.total) * 100
|
||||
|
||||
@dataclass
|
||||
class TaskResult:
|
||||
"""任务结果"""
|
||||
success: bool
|
||||
result: Any = None
|
||||
error: str = None
|
||||
total_time: float = 0.0
|
||||
final_progress: Optional[ProgressInfo] = None
|
||||
@@ -5,9 +5,6 @@ File validation utilities for MixVideo V2
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
from ..config import settings
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user