"""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