167 lines
4.7 KiB
Python
167 lines
4.7 KiB
Python
"""
|
|
错误处理和重试机制
|
|
"""
|
|
|
|
import logging
|
|
import time
|
|
from typing import Callable, Any, Dict, Optional
|
|
from functools import wraps
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def retry(max_retries: int = 3, delay: float = 1.0, backoff: float = 2.0,
|
|
exceptions: tuple = (Exception,)):
|
|
"""
|
|
重试装饰器
|
|
|
|
Args:
|
|
max_retries: 最大重试次数
|
|
delay: 初始延迟(秒)
|
|
backoff: 退避倍数
|
|
exceptions: 需要重试的异常类型
|
|
"""
|
|
def decorator(func: Callable) -> Callable:
|
|
@wraps(func)
|
|
def wrapper(*args, **kwargs):
|
|
current_delay = delay
|
|
last_exception = None
|
|
|
|
for attempt in range(max_retries):
|
|
try:
|
|
return func(*args, **kwargs)
|
|
except exceptions as e:
|
|
last_exception = e
|
|
if attempt < max_retries - 1:
|
|
logger.warning(
|
|
f"{func.__name__} 失败 (尝试 {attempt + 1}/{max_retries}): {e}, "
|
|
f"{current_delay}秒后重试"
|
|
)
|
|
time.sleep(current_delay)
|
|
current_delay *= backoff
|
|
else:
|
|
logger.error(f"{func.__name__} 最终失败: {e}")
|
|
|
|
# 所有重试都失败,抛出最后一个异常
|
|
raise last_exception
|
|
|
|
return wrapper
|
|
return decorator
|
|
|
|
|
|
class ErrorHandler:
|
|
"""错误处理器"""
|
|
|
|
@staticmethod
|
|
def handle_ui_error(error: Exception, context: str = "") -> Dict[str, Any]:
|
|
"""
|
|
处理UI操作错误
|
|
|
|
Args:
|
|
error: 异常对象
|
|
context: 上下文信息
|
|
|
|
Returns:
|
|
错误信息字典
|
|
"""
|
|
error_msg = str(error)
|
|
|
|
# 常见错误分类
|
|
if "timeout" in error_msg.lower() or "等待" in error_msg:
|
|
return {
|
|
"success": False,
|
|
"error": "操作超时",
|
|
"type": "timeout",
|
|
"context": context,
|
|
"suggestion": "检查元素是否存在或增加等待时间"
|
|
}
|
|
elif "not found" in error_msg.lower() or "未找到" in error_msg:
|
|
return {
|
|
"success": False,
|
|
"error": "元素未找到",
|
|
"type": "element_not_found",
|
|
"context": context,
|
|
"suggestion": "检查选择器或使用AI Agent模式"
|
|
}
|
|
elif "permission" in error_msg.lower() or "权限" in error_msg:
|
|
return {
|
|
"success": False,
|
|
"error": "权限不足",
|
|
"type": "permission_denied",
|
|
"context": context,
|
|
"suggestion": "检查ADB权限或Root权限"
|
|
}
|
|
else:
|
|
return {
|
|
"success": False,
|
|
"error": error_msg,
|
|
"type": "unknown",
|
|
"context": context,
|
|
"suggestion": "查看日志获取详细信息"
|
|
}
|
|
|
|
@staticmethod
|
|
def should_retry(error: Exception) -> bool:
|
|
"""
|
|
判断是否应该重试
|
|
|
|
Args:
|
|
error: 异常对象
|
|
|
|
Returns:
|
|
是否应该重试
|
|
"""
|
|
error_msg = str(error).lower()
|
|
|
|
# 这些错误可以重试
|
|
retryable_errors = [
|
|
"timeout",
|
|
"等待",
|
|
"network",
|
|
"连接",
|
|
"temporary",
|
|
"临时"
|
|
]
|
|
|
|
# 这些错误不应该重试
|
|
non_retryable_errors = [
|
|
"permission",
|
|
"权限",
|
|
"not found",
|
|
"未找到",
|
|
"invalid",
|
|
"无效"
|
|
]
|
|
|
|
for retryable in retryable_errors:
|
|
if retryable in error_msg:
|
|
return True
|
|
|
|
for non_retryable in non_retryable_errors:
|
|
if non_retryable in error_msg:
|
|
return False
|
|
|
|
# 默认可以重试
|
|
return True
|
|
|
|
|
|
def with_error_handling(context: str = ""):
|
|
"""
|
|
错误处理装饰器
|
|
|
|
Args:
|
|
context: 上下文信息
|
|
"""
|
|
def decorator(func: Callable) -> Callable:
|
|
@wraps(func)
|
|
def wrapper(*args, **kwargs):
|
|
try:
|
|
result = func(*args, **kwargs)
|
|
return result
|
|
except Exception as e:
|
|
logger.error(f"{func.__name__} 执行失败: {e}")
|
|
return ErrorHandler.handle_ui_error(e, context or func.__name__)
|
|
|
|
return wrapper
|
|
return decorator
|