Files
TransPyC/lib/StubGen/Converter.py
2026-07-18 19:25:40 +08:00

983 lines
47 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""StubGen 转换器模块 - 将 Python 文件转换为存根格式"""
from __future__ import annotations
import ast
import re
import fnmatch
from typing import List, Dict
from lib.core.SymbolUtils import IsListAnnotation
class PythonToStubConverter:
"""将 Python 文件转换为存根格式"""
# __include 别名映射表 {alias: ModuleName}
IncludeAliasMap: Dict[str, str] = {}
# 变量排除列表 - 匹配这些模式的变量不会被添加到存根文件
VarExcludeList: List[str] = []
# 宏排除列表 - 匹配这些模式的宏不会被添加到存根文件
MacroExcludeList: List[str] = []
@classmethod
def SetIncludeAliasMap(cls, alias_map: Dict[str, str]) -> None:
"""设置 __include 别名映射表"""
cls.IncludeAliasMap = alias_map
@classmethod
def SetVarExcludeList(cls, exclude_list: List[str]) -> None:
"""设置变量排除列表"""
cls.VarExcludeList = exclude_list
@classmethod
def SetMacroExcludeList(cls, exclude_list: List[str]) -> None:
"""设置宏排除列表"""
cls.MacroExcludeList = exclude_list
@classmethod
def _ShouldExcludeVar(cls, VarName: str) -> bool:
"""检查变量是否应该被排除"""
for pattern in cls.VarExcludeList:
if fnmatch.fnmatch(VarName, pattern):
return True
return False
@classmethod
def _ShouldExcludeMacro(cls, MacroName: str) -> bool:
"""检查宏是否应该被排除"""
for pattern in cls.MacroExcludeList:
if fnmatch.fnmatch(MacroName, pattern):
return True
return False
@staticmethod
def convert(PyContent: str, ModuleName: str) -> str:
"""将 Python 代码转换为存根格式"""
lines: list[str] = PyContent.split('\n')
StubLines: list[str] = []
# 添加文件头
StubLines.append('"""')
StubLines.append(f'Auto-generated Python stub file from {ModuleName}.py')
StubLines.append(f'Module: {ModuleName}')
StubLines.append('"""')
StubLines.append('')
# 解析 Python 代码,检查是否已经导入了 t 和 c
HasImportT: bool = False
HasImportC: bool = False
try:
tree: ast.Module = ast.parse(PyContent)
for node in tree.body:
if isinstance(node, ast.Import):
for alias in node.names:
if alias.name == 't':
HasImportT = True
elif alias.name == 'c':
HasImportC = True
except:
pass
# 添加默认导入(如果源代码中没有)
if not HasImportT:
StubLines.append('import t')
if not HasImportC:
StubLines.append('import c')
if not HasImportT or not HasImportC:
StubLines.append('')
# 添加 c.CPragma("once")
# StubLines.append('c.CPragma("once")')
StubLines.append('')
# 生成文件级别宏守卫名称
#FileGuardName = f'__{ModuleName.upper()}_DEFINE__'
# 添加文件开头宏守卫
#StubLines.append(f'c.CIfndef({FileGuardName})')
#StubLines.append(f'{FileGuardName}: t.CDefine')
#StubLines.append('')
# 解析 Python 代码,提取类型定义
try:
tree = ast.parse(PyContent)
# 第一遍扫描:收集 Postdefinition 变量
PostdefVars: dict[str, list[tuple[str, str, ast.AnnAssign]]] = {} # {ClassName: [(VarName, TypeStr, node), ...]}
for node in tree.body:
if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
TypeStr: str = PythonToStubConverter._GetTypeString(node.annotation)
# 检查是否是 Postdefinition 类型
if 't.Postdefinition' in TypeStr:
# 提取引用的类名
ClassName: str | None = PythonToStubConverter._ExtractPostdefClass(node.annotation)
if ClassName:
if ClassName not in PostdefVars:
PostdefVars[ClassName] = []
PostdefVars[ClassName].append((node.target.id, TypeStr, node))
# 按原始顺序处理节点,保持代码的原始顺序
PrevType: str | None = None # 记录前一个节点的类型
for node in tree.body:
CurrentType: str | None = None
if isinstance(node, ast.ClassDef):
CurrentType = 'class'
# 如果前一个不是类,添加空行分隔
if PrevType is not None and PrevType != 'class':
StubLines.append('')
# 检查是否有对应的 Postdefinition 变量
ClassPostdefs: list[tuple[str, str, ast.AnnAssign]] = PostdefVars.get(node.name, [])
ClassLines: list[str] = PythonToStubConverter._ConvertClass(
node, SourceLines=lines, PostdefVars=ClassPostdefs
)
StubLines.extend(ClassLines)
elif isinstance(node, ast.FunctionDef):
CurrentType = 'function'
# 如果前一个是类或者是第一个函数,添加空行分隔
if PrevType == 'class' or (PrevType is not None and PrevType != 'function'):
StubLines.append('')
FuncLines: list[str] = PythonToStubConverter._ConvertFunction(node, SourceLines=lines)
StubLines.extend(FuncLines)
StubLines.append('') # 函数后空行
elif isinstance(node, ast.AnnAssign):
CurrentType = 'variable'
# 跳过 Postdefinition 变量(已经在类中处理)
TypeStr = PythonToStubConverter._GetTypeString(node.annotation)
if 't.Postdefinition' in TypeStr:
continue
# 检查是否带有 t.CStatic 标志,如果有则不排除
HasStatic: bool = 't.CStatic' in TypeStr
# 检查变量是否应该被排除
ShouldExclude: bool = False
if isinstance(node.target, ast.Name) and not HasStatic:
VarName: str = node.target.id
if PythonToStubConverter._ShouldExcludeVar(VarName):
ShouldExclude = True
if ShouldExclude:
continue
# 如果前一个不是变量,添加空行分隔
if PrevType is not None and PrevType != 'variable':
StubLines.append('')
VarLines: list[str] = PythonToStubConverter._ConvertVariable(node, SourceLines=lines)
# 移除多余的空行
VarLines = [line for line in VarLines if line.strip()]
StubLines.extend(VarLines)
elif isinstance(node, ast.Import):
# 处理导入语句
CurrentType = 'import'
ImportStr: str = PythonToStubConverter._GetImportString(node, SourceLines=lines)
if ImportStr:
if PrevType is not None and PrevType != 'import':
StubLines.append('')
StubLines.append(ImportStr)
elif isinstance(node, ast.Assign):
CurrentType = 'variable'
# 处理模块级别的全局变量(无类型标注)
if PrevType is not None and PrevType != 'variable':
StubLines.append('')
AssignLines: list[str] = PythonToStubConverter._ConvertGlobalAssign(node)
StubLines.extend(AssignLines)
elif isinstance(node, ast.ImportFrom):
# 处理 from ... import ... 语句
CurrentType = 'import'
ImportStr = PythonToStubConverter._GetImportFromString(node, SourceLines=lines)
if ImportStr:
if PrevType is not None and PrevType != 'import':
StubLines.append('')
StubLines.append(ImportStr)
elif isinstance(node, ast.Expr):
# 处理模块级别的宏调用,如 c.CIf(...), c.CEndif()
ExprStr: str = PythonToStubConverter._GetExprString(node.value)
if ExprStr:
CurrentType = 'macro'
StubLines.append(ExprStr)
StubLines.append('') # 宏后空行
elif isinstance(node, ast.If):
# 处理模块级别的 if 宏条件,如 if c.CIf(FF_MULTI_PARTITION):
IfStr: str = PythonToStubConverter._GetModuleIfMacroString(node)
if IfStr:
CurrentType = 'macro'
StubLines.append(IfStr)
StubLines.append('') # 宏后空行
if CurrentType:
PrevType = CurrentType
# 添加文件结尾宏守卫
#StubLines.append('')
#StubLines.append('c.CEndif()')
except SyntaxError as e:
# 如果解析失败,添加注释说明
StubLines.append(f'# Warning: Failed to parse Python code: {e}')
StubLines.append('# Original content:')
StubLines.append('"""')
StubLines.append(PyContent[:1000]) # 只显示前1000字符
StubLines.append('"""')
return '\n'.join(StubLines)
@staticmethod
def _ConvertClass(node: ast.ClassDef, SourceLines: list[str] | None = None, PostdefVars: list[tuple[str, str, ast.AnnAssign]] | None = None, indent: int = 0) -> list[str]:
"""转换类定义
Args:
node: 类定义节点
SourceLines: 源代码行列表
PostdefVars: Postdefinition 变量列表 [(VarName, TypeStr, node), ...]
indent: 缩进级别(用于嵌套类)
"""
lines: list[str] = []
PostdefVars = PostdefVars or []
IndentStr: str = ' ' * indent
# 处理类装饰器
for decorator in node.decorator_list:
DecoratorStr: str = PythonToStubConverter._GetDecoratorString(decorator)
if DecoratorStr:
lines.append(f'{IndentStr}@{DecoratorStr}')
# 检查是否是结构体、联合体或枚举
BaseNames: list[str] = [base.attr if isinstance(base, ast.Attribute) else base.id
for base in node.bases
if isinstance(base, (ast.Name, ast.Attribute))]
# 保留原始基类
if node.bases:
BaseStrs: list[str] = []
for base in node.bases:
if isinstance(base, ast.Name):
BaseStrs.append(base.id)
elif isinstance(base, ast.Attribute):
BaseStrs.append(f'{base.value.id}.{base.attr}')
elif isinstance(base, ast.Subscript):
# 处理泛型类型,如 memory_block_t[MAX_ORDER + 1]
BaseStrs.append(PythonToStubConverter._GetTypeString(base))
ClassTypeParamStr: str = ''
if hasattr(node, 'type_params') and node.type_params:
param_names: list[str] = [tp.name for tp in node.type_params]
ClassTypeParamStr = f'[{", ".join(param_names)}]'
if BaseStrs:
lines.append(f'{IndentStr}class {node.name}{ClassTypeParamStr}({", ".join(BaseStrs)}):')
else:
lines.append(f'{IndentStr}class {node.name}{ClassTypeParamStr}:')
else:
ClassTypeParamStr = ''
if hasattr(node, 'type_params') and node.type_params:
param_names = [tp.name for tp in node.type_params]
ClassTypeParamStr = f'[{", ".join(param_names)}]'
lines.append(f'{IndentStr}class {node.name}{ClassTypeParamStr}:')
# 处理类成员
HasMembers: bool = False
seen_member_names: set[str] = set()
for item in node.body:
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
seen_member_names.add(item.target.id)
init_members: list[ast.AnnAssign] = []
for item in node.body:
if isinstance(item, ast.FunctionDef) and item.name == '__init__':
for stmt in item.body:
if (isinstance(stmt, ast.AnnAssign)
and isinstance(stmt.target, ast.Attribute)
and isinstance(stmt.target.value, ast.Name)
and stmt.target.value.id == 'self'):
init_members.append(stmt)
untyped_self_members: list[ast.Assign] = []
for m_name in [m.target.attr for m in init_members if isinstance(m.target, ast.Attribute)]:
seen_member_names.add(m_name)
for item in node.body:
if isinstance(item, ast.FunctionDef):
for stmt in item.body:
if (isinstance(stmt, ast.Assign)
and len(stmt.targets) == 1
and isinstance(stmt.targets[0], ast.Attribute)
and isinstance(stmt.targets[0].value, ast.Name)
and stmt.targets[0].value.id == 'self'):
attr_name: str = stmt.targets[0].attr
if attr_name not in seen_member_names:
seen_member_names.add(attr_name)
untyped_self_members.append(stmt)
for item in list(node.body) + init_members + untyped_self_members:
if isinstance(item, ast.Assign):
if (len(item.targets) == 1
and isinstance(item.targets[0], ast.Attribute)
and isinstance(item.targets[0].value, ast.Name)
and item.targets[0].value.id == 'self'):
HasMembers = True
MemberIndent: str = IndentStr + ' '
attr_name = item.targets[0].attr
if attr_name in seen_member_names:
continue
InferredType: str = 'int'
if item.value and isinstance(item.value, ast.Constant):
if isinstance(item.value.value, float):
InferredType = 'float'
elif isinstance(item.value.value, bool):
InferredType = 'bool'
elif isinstance(item.value.value, str):
InferredType = 'str'
lines.append(f'{MemberIndent}{attr_name}: {InferredType}')
else:
HasMembers = True
MemberIndent = IndentStr + ' '
if SourceLines and hasattr(item, 'lineno'):
StartLine: int = item.lineno - 1
EndLine: int = item.end_lineno if hasattr(item, 'end_lineno') and item.end_lineno else StartLine + 1
for i in range(StartLine, min(EndLine, len(SourceLines))):
lines.append(MemberIndent + SourceLines[i].lstrip())
elif isinstance(item, ast.AnnAssign):
HasMembers = True
TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation)
MemberIndent = IndentStr + ' '
# __provides__/__requires__/__require_must__ 元数据:保留值以便跨模块加载
if isinstance(item.target, ast.Name) and item.target.id in ('__provides__', '__requires__', '__require_must__'):
if item.value is not None:
try:
value_str: str = ast.unparse(item.value)
except Exception:
value_str = ''
lines.append(f'{MemberIndent}{item.target.id}: {TypeStr} = {value_str}')
else:
lines.append(f'{MemberIndent}{item.target.id}: {TypeStr}')
continue
# 检查是否是宏变量(类型包含 t.CDefine
if 't.CDefine' in TypeStr:
# 检查宏变量是否应该被排除
if isinstance(item.target, ast.Name):
MacroName: str = item.target.id
if PythonToStubConverter._ShouldExcludeMacro(MacroName):
continue # 跳过该宏
# 宏变量保持原样(包括赋值)
if SourceLines and hasattr(item, 'lineno'):
StartLine = item.lineno - 1
EndLine = item.end_lineno if hasattr(item, 'end_lineno') and item.end_lineno else StartLine + 1
for i in range(StartLine, min(EndLine, len(SourceLines))):
lines.append(MemberIndent + SourceLines[i].lstrip())
else:
lines.append(f'{MemberIndent}{item.target.id}: {TypeStr}')
else:
if isinstance(item.target, ast.Attribute):
MemberName: str = item.target.attr
else:
MemberName = item.target.id
FinalTypeStr: str = TypeStr
if IsListAnnotation(item.annotation):
slice_node: ast.AST = item.annotation.slice
has_size: bool = False
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2:
count_node: ast.AST = slice_node.elts[1]
if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int) and count_node.value > 0:
has_size = True
else:
try:
count_expr: str = ast.unparse(count_node)
except Exception:
count_expr = ''
if count_expr and count_expr != 'None':
has_size = True
elem_type_str: str = PythonToStubConverter._GetTypeString(slice_node.elts[0])
FinalTypeStr = f't.CArray[{elem_type_str}, {count_expr}]'
if not has_size:
elem_type_str = PythonToStubConverter._GetTypeString(slice_node if not isinstance(slice_node, ast.Tuple) else slice_node.elts[0])
init_len: int = 0
if item.value is not None:
if isinstance(item.value, ast.List):
init_len = len(item.value.elts)
elif isinstance(item.value, ast.Constant) and isinstance(item.value.value, str):
init_len = len(item.value.value) + 1
if init_len > 0:
FinalTypeStr = f't.CArray[{elem_type_str}, {init_len}]'
else:
FinalTypeStr = f't.CArray[{elem_type_str}, None]'
if item.value is not None and isinstance(item.value, ast.Constant):
try:
value_repr: str = ast.unparse(item.value)
lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr} = {value_repr}')
except Exception:
lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr}')
else:
lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr}')
elif isinstance(item, ast.FunctionDef):
HasMembers = True
FuncStub: list[str] = PythonToStubConverter._ConvertFunction(item, indent=indent+1, SourceLines=SourceLines, class_name=node.name)
lines.extend(FuncStub)
elif isinstance(item, ast.Expr):
# 处理宏调用,如 c.CIf(...), c.CEndif(), c.CElif(), c.CElse()
ExprStr: str = PythonToStubConverter._GetExprString(item.value)
if ExprStr:
lines.append(f'{IndentStr} {ExprStr}')
elif isinstance(item, ast.If):
# 处理 if c.CIf(...): 形式的宏条件
IfStr: str = PythonToStubConverter._get_if_macro_string(item, indent=indent+1)
if IfStr:
lines.append(IfStr)
elif isinstance(item, ast.ClassDef):
# 处理嵌套类
HasMembers = True
NestedClassLines: list[str] = PythonToStubConverter._ConvertClass(item, SourceLines=SourceLines, PostdefVars=[], indent=indent+1)
# 移除嵌套类的宏守卫(因为已经在父类的宏守卫内)
FilteredLines: list[str] = []
for line in NestedClassLines:
# 跳过宏守卫相关行
if line.strip().startswith('c.CIfndef(') or line.strip().startswith('c.CEndif()'):
continue
if ': t.CDefine' in line and '__' in line:
continue
FilteredLines.append(line)
lines.extend(FilteredLines)
if not HasMembers:
lines.append(f'{IndentStr} pass')
# 添加 Postdefinition 变量
if PostdefVars:
lines.append('')
for VarName, TypeStr, _ in PostdefVars:
lines.append(f'{VarName}: {TypeStr}')
return lines
@staticmethod
def _ConvertFunction(node: ast.FunctionDef, indent: int = 0, SourceLines: list[str] | None = None, class_name: str | None = None) -> list[str]:
"""转换函数定义"""
lines: list[str] = []
IndentStr: str = ' ' * indent
# 检查是否是宏函数(返回类型包含 t.CDefine
is_macro_func: bool = False
if node.returns:
ReturnType: str = PythonToStubConverter._GetTypeString(node.returns)
if 't.CDefine' in ReturnType:
is_macro_func = True
# 如果是宏函数,检查是否应该被排除
if is_macro_func:
if PythonToStubConverter._ShouldExcludeMacro(node.name):
return lines # 返回空列表,排除该宏
# 保持原样(包括代码块)
if SourceLines:
StartLine: int = node.lineno - 1
EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1
for i in range(StartLine, min(EndLine, len(SourceLines))):
lines.append(SourceLines[i])
if lines and lines[-1].strip() and not lines[-1].rstrip().endswith(':'):
pass
else:
lines.append(f'{IndentStr} pass')
return lines
# 处理函数装饰器
for decorator in node.decorator_list:
DecoratorStr: str = PythonToStubConverter._GetDecoratorString(decorator)
if DecoratorStr:
lines.append(f'{IndentStr}@{DecoratorStr}')
# 构建参数列表
params: list[str] = []
for arg_idx, arg in enumerate(node.args.args):
ArgName: str = arg.arg
if arg.annotation:
TypeStr: str = PythonToStubConverter._GetTypeString(arg.annotation)
params.append(f'{ArgName}: {TypeStr}')
elif class_name and arg_idx == 0 and ArgName == 'self':
params.append(f'self: {class_name}')
else:
params.append(ArgName)
# 处理 *args
if node.args.vararg:
ArgName = node.args.vararg.arg
if node.args.vararg.annotation:
TypeStr = PythonToStubConverter._GetTypeString(node.args.vararg.annotation)
params.append(f'*{ArgName}: {TypeStr}')
else:
params.append(f'*{ArgName}')
# 处理 **kwargs
if node.args.kwarg:
ArgName = node.args.kwarg.arg
if node.args.kwarg.annotation:
TypeStr = PythonToStubConverter._GetTypeString(node.args.kwarg.annotation)
params.append(f'**{ArgName}: {TypeStr}')
else:
params.append(f'**{ArgName}')
ParamStr: str = ', '.join(params)
# 返回类型 - 保持原始类型不添加t.State
if node.returns:
ReturnType = PythonToStubConverter._GetTypeString(node.returns)
else:
ReturnType = 't.CInt'
TypeParamStr: str = ''
if hasattr(node, 'type_params') and node.type_params:
param_names: list[str] = [tp.name for tp in node.type_params]
TypeParamStr = f'[{", ".join(param_names)}]'
lines.append(f'{IndentStr}def {node.name}{TypeParamStr}({ParamStr}) -> {ReturnType}: pass')
return lines
@staticmethod
def _ConvertVariable(node: ast.AnnAssign, SourceLines: list[str] | None = None) -> list[str]:
"""转换变量定义"""
lines: list[str] = []
if isinstance(node.target, ast.Name):
VarName: str = node.target.id
TypeStr: str = PythonToStubConverter._GetTypeString(node.annotation)
# 检查是否是宏变量(类型包含 t.CDefine
if 't.CDefine' in TypeStr:
# 检查宏变量是否应该被排除
if PythonToStubConverter._ShouldExcludeMacro(VarName):
return lines # 返回空列表,排除该宏
# 宏变量保持原样(包括赋值)
if SourceLines and hasattr(node, 'lineno'):
StartLine: int = node.lineno - 1
EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1
for i in range(StartLine, min(EndLine, len(SourceLines))):
lines.append(SourceLines[i])
else:
lines.append(f'{VarName}: {TypeStr}')
return lines
# 非宏变量:不加 t.State
# 含有 typedef 的变量不加 extern普通变量需要加 extern
has_typedef: bool = 't.CTypedef' in TypeStr or 'CTypedef' in TypeStr
if has_typedef and node.value is not None:
ValueTypeStr: str = PythonToStubConverter._GetTypeString(node.value)
lines.append(f'{VarName}: {TypeStr} = {ValueTypeStr}')
else:
if not has_typedef and 't.CExtern' not in TypeStr and 't.CStatic' not in TypeStr:
TypeStr = f't.CExtern | {TypeStr}'
lines.append(f'{VarName}: {TypeStr}')
return lines
@staticmethod
def _ConvertGlobalAssign(node: ast.Assign) -> list[str]:
"""转换模块级别的全局变量(无类型标注)"""
lines: list[str] = []
if not node.targets or not isinstance(node.targets[0], ast.Name):
return lines
VarName: str = node.targets[0].id
# 跳过私有变量(以下划线开头)
if VarName.startswith('_'):
return lines
# 从值推断类型
inferred_type: str | None = PythonToStubConverter._InferTypeFromValue(node.value)
if inferred_type:
lines.append(f'{VarName}: {inferred_type}')
else:
lines.append(f'{VarName}: t.CInt')
return lines
@staticmethod
def _InferTypeFromValue(value: ast.AST) -> str | None:
"""从值推断 C 类型"""
if isinstance(value, ast.Constant):
if isinstance(value.value, int):
return 't.CInt'
elif isinstance(value.value, float):
return 't.CDouble'
elif isinstance(value.value, str):
return 't.CCharPtr'
elif isinstance(value.value, bool):
return 't.CInt'
elif isinstance(value, ast.List):
return 't.CVoidPtr'
elif isinstance(value, ast.Dict):
return 't.CVoidPtr'
elif isinstance(value, ast.Name):
return 't.CInt'
elif isinstance(value, ast.BinOp):
left_type: str | None = PythonToStubConverter._InferTypeFromValue(value.left)
if left_type:
return left_type
return 't.CInt'
elif isinstance(value, ast.Call):
# 函数调用返回值,假设为指针类型
if isinstance(value.func, ast.Name):
func_name: str = value.func.id
if func_name == 'malloc':
return 't.CVoidPtr'
elif func_name in ('ctypes.cast', 'cast'):
return 't.CVoidPtr'
return 't.CVoidPtr'
return None
@staticmethod
def _GetTypeString(annotation: ast.AST) -> str:
"""获取类型字符串"""
if isinstance(annotation, ast.Name):
return annotation.id
elif isinstance(annotation, ast.Attribute):
if isinstance(annotation.value, ast.Name):
return f'{annotation.value.id}.{annotation.attr}'
else:
# 处理嵌套属性访问,如 t.attr.packed
ParentStr: str = PythonToStubConverter._GetTypeString(annotation.value)
return f'{ParentStr}.{annotation.attr}'
elif isinstance(annotation, ast.Subscript):
base: str = PythonToStubConverter._GetTypeString(annotation.value)
if isinstance(annotation.slice, ast.Tuple):
slice_strs: list[str] = [PythonToStubConverter._GetTypeString(e) for e in annotation.slice.elts]
SliceStr: str = ', '.join(slice_strs)
else:
SliceStr = PythonToStubConverter._GetTypeString(annotation.slice)
return f'{base}[{SliceStr}]'
elif isinstance(annotation, ast.BinOp):
# 处理 BinOp如 t.CExtern | fd_table.__set_default__(...)
left: ast.AST = annotation.left
right: ast.AST = annotation.right
# 检查右侧是否是 __set_default__ 调用
if isinstance(right, ast.Call) and isinstance(right.func, ast.Attribute) and right.func.attr in ('__set_default__', '__set_default'):
# 去除 __set_default__保留左侧右侧替换为函数名部分
LeftStr: str = PythonToStubConverter._GetTypeString(left)
RightStr: str = PythonToStubConverter._GetTypeString(right.func.value)
if isinstance(annotation.op, ast.BitOr):
return f'{LeftStr} | {RightStr}'
LeftStr = PythonToStubConverter._GetTypeString(left)
RightStr = PythonToStubConverter._GetTypeString(right)
if isinstance(annotation.op, ast.BitOr):
return f'{LeftStr} | {RightStr}'
elif isinstance(annotation.op, ast.Add):
return f'{LeftStr} + {RightStr}'
elif isinstance(annotation.op, ast.Sub):
return f'{LeftStr} - {RightStr}'
elif isinstance(annotation.op, ast.Mult):
return f'{LeftStr} * {RightStr}'
elif isinstance(annotation.op, ast.Div):
return f'{LeftStr} / {RightStr}'
elif isinstance(annotation.op, ast.Mod):
return f'{LeftStr} % {RightStr}'
elif isinstance(annotation, ast.UnaryOp):
if isinstance(annotation.op, ast.Not):
operand: str = PythonToStubConverter._GetTypeString(annotation.operand)
return f'not {operand}'
elif isinstance(annotation.op, ast.Invert):
operand = PythonToStubConverter._GetTypeString(annotation.operand)
return f'~{operand}'
elif isinstance(annotation.op, ast.USub):
operand = PythonToStubConverter._GetTypeString(annotation.operand)
return f'-{operand}'
elif isinstance(annotation, ast.Compare):
# 处理比较操作,如 FF_MAX_SS != FF_MIN_SS
left: str = PythonToStubConverter._GetTypeString(annotation.left)
comparators: list[str] = [PythonToStubConverter._GetTypeString(c) for c in annotation.comparators]
ops: list[str] = []
for op in annotation.ops:
if isinstance(op, ast.Eq):
ops.append('==')
elif isinstance(op, ast.NotEq):
ops.append('!=')
elif isinstance(op, ast.Lt):
ops.append('<')
elif isinstance(op, ast.LtE):
ops.append('<=')
elif isinstance(op, ast.Gt):
ops.append('>')
elif isinstance(op, ast.GtE):
ops.append('>=')
else:
ops.append('?')
result: str = left
for i, op in enumerate(ops):
result += f' {op} {comparators[i]}'
return result
elif isinstance(annotation, ast.Constant):
return repr(annotation.value)
elif isinstance(annotation, ast.Index):
return PythonToStubConverter._GetTypeString(annotation.value)
elif isinstance(annotation, ast.List):
# 处理列表,如 [MAX_FILE_DESCRIPTORS]
elements: list[str] = [PythonToStubConverter._GetTypeString(elem) for elem in annotation.elts]
return f'[{", ".join(elements)}]'
elif isinstance(annotation, ast.Call):
# 处理函数调用形式的类型注解,如 t.Postdefinition(xxa) 或 callable(t.CVoid, app_id = t.CInt)
# 如果是 __set_default__ 调用,只返回函数名部分
if isinstance(annotation.func, ast.Attribute) and annotation.func.attr in ('__set_default__', '__set_default'):
return PythonToStubConverter._GetTypeString(annotation.func.value)
FuncStr: str = PythonToStubConverter._GetTypeString(annotation.func)
ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in annotation.args])
# 处理关键字参数
keywords_str: str = ', '.join([f'{kw.arg} = {PythonToStubConverter._GetTypeString(kw.value)}' for kw in annotation.keywords])
# 合并位置参数和关键字参数
if ArgsStr and keywords_str:
return f'{FuncStr}({ArgsStr}, {keywords_str})'
elif keywords_str:
return f'{FuncStr}({keywords_str})'
else:
return f'{FuncStr}({ArgsStr})'
return 't.CVoid'
@staticmethod
def _ExtractPostdefClass(annotation: ast.AST) -> str | None:
"""从 Postdefinition 类型注解中提取类名
例如t.Postdefinition(__buddy_system) -> '__buddy_system'
t.Postdefinition(xxx) | t.CTypedef -> 'xxx'
"""
# 处理 BinOp (如: t.Postdefinition(xxx) | t.CTypedef)
if isinstance(annotation, ast.BinOp):
# 递归检查左操作数
left_result: str | None = PythonToStubConverter._ExtractPostdefClass(annotation.left)
if left_result:
return left_result
# 递归检查右操作数
right_result: str | None = PythonToStubConverter._ExtractPostdefClass(annotation.right)
if right_result:
return right_result
return None
# 处理函数调用 (如: t.Postdefinition(__buddy_system))
if isinstance(annotation, ast.Call):
FuncStr: str = PythonToStubConverter._GetTypeString(annotation.func)
if FuncStr == 't.Postdefinition' and annotation.args:
# 提取第一个参数
first_arg: ast.AST = annotation.args[0]
if isinstance(first_arg, ast.Name):
return first_arg.id
elif isinstance(first_arg, ast.Attribute):
return f'{first_arg.value.id}.{first_arg.attr}'
return None
@staticmethod
def _GetImportString(node: ast.Import, SourceLines: list[str] | None = None) -> str:
"""获取导入语句字符串"""
parts: list[str] = []
IncludeAliasMap: dict[str, str] = PythonToStubConverter.IncludeAliasMap
for alias in node.names:
ModuleName: str = alias.name
# 检查是否是 __include.xxx 格式的导入
if ModuleName.startswith('__include.'):
# 提取别名xxx 部分)
SubModule: str = ModuleName[len('__include.'):]
if SubModule in IncludeAliasMap:
# 替换为实际模块路径
RealModule: str = IncludeAliasMap[SubModule]
if alias.asname:
parts.append(f'{RealModule} as {alias.asname}')
else:
parts.append(f'{RealModule} as {SubModule}')
else:
# 不在映射表中,保持原样
if alias.asname:
parts.append(f'{ModuleName} as {alias.asname}')
else:
parts.append(ModuleName)
else:
if alias.asname:
parts.append(f'{alias.name} as {alias.asname}')
else:
parts.append(alias.name)
result: str = f'import {", ".join(parts)}'
# 提取并保留注释
if SourceLines and hasattr(node, 'lineno'):
LineIdx: int = node.lineno - 1
if 0 <= LineIdx < len(SourceLines):
line: str = SourceLines[LineIdx]
CommentMatch: re.Match | None = re.search(r'#.*$', line)
if CommentMatch:
result += ' ' + CommentMatch.group(0)
return result
@staticmethod
def _GetImportFromString(node: ast.ImportFrom, SourceLines: list[str] | None = None) -> str:
"""获取 from ... import ... 语句字符串"""
module: str = node.module or ''
parts: list[str] = []
IncludeAliasMap: dict[str, str] = PythonToStubConverter.IncludeAliasMap
# 处理相对导入 (from . import xxx 或 from ..package import xxx)
if node.level > 0:
if module:
if node.level == 1:
full_module: str = f'.{module}'
else:
full_module = '.' * node.level + module
else:
full_module = '.' * node.level
for alias in node.names:
if alias.asname:
parts.append(f'{alias.name} as {alias.asname}')
else:
parts.append(alias.name)
return f'from {full_module} import {", ".join(parts)}'
# 提取注释(在生成结果前获取)
comment: str = ''
if SourceLines and hasattr(node, 'lineno'):
LineIdx = node.lineno - 1
if 0 <= LineIdx < len(SourceLines):
line = SourceLines[LineIdx]
CommentMatch: re.Match | None = re.search(r'#.*$', line)
if CommentMatch:
comment = ' ' + CommentMatch.group(0)
# 检查是否是 from __include import xxx 格式
if module == '__include':
for alias in node.names:
name: str = alias.name
if name in IncludeAliasMap:
# 替换为实际模块路径
RealModule: str = IncludeAliasMap[name]
if alias.asname:
parts.append(f'import {RealModule} as {alias.asname}{comment}')
else:
parts.append(f'import {RealModule} as {name}{comment}')
else:
# 不在映射表中,保持原样
if alias.asname:
parts.append(f'from {module} import {name} as {alias.asname}{comment}')
else:
parts.append(f'from {module} import {name}{comment}')
return '\n'.join(parts)
# 普通 from ... import ... 语句
for alias in node.names:
if alias.asname:
parts.append(f'{alias.name} as {alias.asname}')
else:
parts.append(alias.name)
return f'from {module} import {", ".join(parts)}{comment}'
@staticmethod
def _GetDecoratorString(decorator: ast.AST) -> str:
"""获取装饰器字符串"""
if isinstance(decorator, ast.Name):
return decorator.id
elif isinstance(decorator, ast.Attribute):
if isinstance(decorator.value, ast.Name):
return f'{decorator.value.id}.{decorator.attr}'
return decorator.attr
elif isinstance(decorator, ast.Call):
# 处理带参数的装饰器,如 @c.Attribute(t.attr.packed)
FuncStr: str = PythonToStubConverter._GetDecoratorString(decorator.func)
ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in decorator.args])
return f'{FuncStr}({ArgsStr})'
return ''
@staticmethod
def _GetExprString(expr: ast.AST) -> str:
"""获取表达式字符串(用于宏调用)"""
if isinstance(expr, ast.Call):
# 处理函数调用,如 c.CIf(...), c.CEndif(), c.CUndef(...), c.CPragma(...)
FuncStr: str = PythonToStubConverter._GetDecoratorString(expr.func)
if FuncStr and ('CIf' in FuncStr or 'CEndif' in FuncStr or 'CElif' in FuncStr or 'CElse' in FuncStr or 'CUndef' in FuncStr or 'CPragma' in FuncStr):
ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in expr.args])
return f'{FuncStr}({ArgsStr})' if ArgsStr else f'{FuncStr}()'
return ''
@staticmethod
def _get_if_macro_string(node: ast.If, indent: int = 1) -> str:
"""获取类内部的 if 宏字符串(如 if c.CIf(...):"""
IndentStr: str = ' ' * indent
InnerIndent: str = ' ' * (indent + 1)
# 检查条件是否是宏调用
if isinstance(node.test, ast.Call):
FuncStr: str = PythonToStubConverter._GetDecoratorString(node.test.func)
if FuncStr and 'CIf' in FuncStr:
ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in node.test.args])
result: list[str] = [f'{IndentStr}if {FuncStr}({ArgsStr}):' if ArgsStr else f'{IndentStr}if {FuncStr}():']
# 处理 if 体内的语句
for item in node.body:
if isinstance(item, ast.AnnAssign):
TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation)
result.append(f'{InnerIndent}{item.target.id}: {TypeStr}')
elif isinstance(item, ast.Expr):
ExprStr: str = PythonToStubConverter._GetExprString(item.value)
if ExprStr:
result.append(f'{InnerIndent}{ExprStr}')
return '\n'.join(result)
return ''
@staticmethod
def _GetModuleIfMacroString(node: ast.If) -> str:
"""获取模块级别的 if 宏字符串(如 if c.CIf(FF_MULTI_PARTITION):"""
return PythonToStubConverter._ProcessIfMacro(node, indent=0)
@staticmethod
def _ProcessIfMacro(node: ast.If, indent: int = 0) -> str:
"""处理 if 宏节点,支持 elif 和 else"""
IndentStr: str = ' ' * indent
# 检查条件是否是宏调用
if isinstance(node.test, ast.Call):
FuncStr: str = PythonToStubConverter._GetDecoratorString(node.test.func)
if FuncStr and 'CIf' in FuncStr:
ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in node.test.args])
result: list[str] = [f'{IndentStr}if {FuncStr}({ArgsStr}):' if ArgsStr else f'{IndentStr}if {FuncStr}():']
# 处理 if 体内的语句
for item in node.body:
result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1))
# 处理 elif 和 else
current: ast.If = node
while current.orelse:
if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If):
# elif 情况
elif_node: ast.If = current.orelse[0]
if isinstance(elif_node.test, ast.Call):
ElifFuncStr: str = PythonToStubConverter._GetDecoratorString(elif_node.test.func)
if ElifFuncStr and 'CIf' in ElifFuncStr:
ElifArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in elif_node.test.args])
result.append(f'{IndentStr}elif {ElifFuncStr}({ElifArgsStr}):' if ElifArgsStr else f'{IndentStr}elif {ElifFuncStr}():')
for item in elif_node.body:
result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1))
current = elif_node
continue
break
else:
# else 情况
result.append(f'{IndentStr}else:')
for item in current.orelse:
result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1))
break
return '\n'.join(result)
return ''
@staticmethod
def _ProcessMacroBodyItem(item: ast.AST, indent: int = 0) -> list[str]:
"""处理宏体内的单个语句"""
IndentStr: str = ' ' * indent
result: list[str] = []
if isinstance(item, ast.ClassDef):
class_stub: list[str] = PythonToStubConverter._ConvertClass(item)
for line in class_stub:
if line.strip():
result.append(f'{IndentStr}{line}')
else:
result.append(line)
elif isinstance(item, ast.FunctionDef):
FuncStub: list[str] = PythonToStubConverter._ConvertFunction(item, indent=indent)
result.extend(FuncStub)
elif isinstance(item, ast.AnnAssign):
TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation)
result.append(f'{IndentStr}{item.target.id}: {TypeStr}')
elif isinstance(item, ast.Expr):
ExprStr: str = PythonToStubConverter._GetExprString(item.value)
if ExprStr:
result.append(f'{IndentStr}{ExprStr}')
elif isinstance(item, ast.If):
# 处理嵌套的 if 宏
NestedIf: str = PythonToStubConverter._ProcessIfMacro(item, indent=indent)
if NestedIf:
result.append(NestedIf)
return result