88 lines
3.7 KiB
Python
88 lines
3.7 KiB
Python
# -*- coding: utf-8 -*-
|
||
import logging
|
||
import re
|
||
from typing import Dict, Any, Tuple
|
||
import json
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
def is_camel_case(s: str) -> bool:
|
||
"""
|
||
检查字符串是否为小驼峰式命名 (lowerCamelCase)。
|
||
小驼峰式命名要求:
|
||
1. 以小写字母开头。
|
||
2. 后续可以跟字母或数字。
|
||
3. 不能包含下划线或短横线。
|
||
"""
|
||
if not s or not isinstance(s, str):
|
||
return False
|
||
# 检查是否以小写字母开头,并且不包含下划线或中划线
|
||
return re.match(r'^[a-z]+([A-Z0-9][a-z0-9]*)*$', s) is not None
|
||
|
||
|
||
def extract_json_from_response(response: Any) -> Tuple[Any, str]:
|
||
"""
|
||
从API响应中安全地提取JSON数据。
|
||
这个响应可以是一个 requests.Response 对象,也可以是一个字典(模拟的响应)。
|
||
"""
|
||
if hasattr(response, 'json'): # 类似 requests.Response 对象
|
||
try:
|
||
return response.json(), ""
|
||
except json.JSONDecodeError as e:
|
||
return None, f"JSON解析失败: {e}"
|
||
except Exception as e:
|
||
return None, f"从响应中提取JSON时发生未知错误: {e}"
|
||
elif isinstance(response, dict): # 模拟的响应字典
|
||
return response, ""
|
||
elif isinstance(response, str):
|
||
try:
|
||
return json.loads(response), ""
|
||
except json.JSONDecodeError as e:
|
||
return None, f"JSON字符串解析失败: {e}"
|
||
return None, "不支持的响应类型"
|
||
|
||
|
||
def format_url_with_path_params(path_template: str, path_params: Dict[str, Any]) -> str:
|
||
"""
|
||
使用提供的路径参数格式化URL路径模板。
|
||
例如, path_template='/users/{userId}/items/{itemId}'
|
||
path_params={'userId': 123, 'itemId': 'abc'}
|
||
-> '/users/123/items/abc'
|
||
|
||
Args:
|
||
path_template: 包含占位符的URL路径,例如 /resource/{id}。
|
||
path_params: 包含占位符名称及其值的字典。
|
||
|
||
Returns:
|
||
格式化后的URL路径。
|
||
"""
|
||
url = path_template
|
||
try:
|
||
# 优先使用 .format(**path_params) 如果所有占位符都能匹配
|
||
# 这要求 path_params 中的键与模板中的占位符完全对应
|
||
# url = path_template.format(**path_params) # 更简洁,但如果参数不完全匹配会报错
|
||
|
||
# 使用正则表达式逐个替换更安全,可以处理部分参数或额外参数的情况
|
||
for param_name, param_value in path_params.items():
|
||
placeholder = f"{{{param_name}}}"
|
||
if placeholder in url:
|
||
url = url.replace(placeholder, str(param_value))
|
||
else:
|
||
# Log if a path param was provided but not found in template. Could be optional.
|
||
logger.debug(f"Path parameter '{param_name}' provided but placeholder '{placeholder}' not found in template '{path_template}'.")
|
||
|
||
# 检查是否还有未替换的占位符 (可选,但推荐)
|
||
remaining_placeholders = re.findall(r"({[^{}]+?})", url)
|
||
if remaining_placeholders:
|
||
logger.warning(f"URL '{url}' 中仍有未替换的路径参数占位符: {remaining_placeholders}。原始模板: '{path_template}', 提供参数: {path_params}")
|
||
|
||
except KeyError as e:
|
||
logger.error(f"格式化URL路径 '{path_template}' 失败:路径参数 '{e}' 未在提供的 path_params 中找到。可用参数: {list(path_params.keys())}")
|
||
# 根据需要,这里可以选择是返回原始模板还是抛出异常
|
||
# return path_template
|
||
raise ValueError(f"Missing path parameter {e} for URL template {path_template}") from e
|
||
except Exception as e:
|
||
logger.error(f"格式化URL路径 '{path_template}' 时发生未知错误: {e}")
|
||
raise # 或者返回原始模板
|
||
|
||
return url |