修复了大量存在的问题,增加了假鸭子类型等等机制

This commit is contained in:
2026-06-25 14:49:46 +08:00
parent 19f2787db0
commit d88d11b646
827 changed files with 32617 additions and 18316 deletions

View File

@@ -1,12 +1,15 @@
"""StubGen 生成器模块 - 存根生成器主类和命令行入口"""
from __future__ import annotations
import sys
import os
import re
import argparse
import logging
import traceback
from pathlib import Path
from typing import List, Tuple
from lib.core.VLogger import get_logger as _vlog
from lib.core.stub_generator import CHeaderParser, PythonStubGenerator, CTypeMapper
from lib.StubGen.Config import StubGenConfig
from lib.StubGen.Converter import PythonToStubConverter
@@ -15,9 +18,9 @@ from lib.StubGen.Converter import PythonToStubConverter
class StubGen:
"""存根生成器主类"""
def __init__(self, config: StubGenConfig):
self.config = config
self.logger = self._SetupLogger()
def __init__(self, config: StubGenConfig) -> None:
self.config: StubGenConfig = config
self.logger: logging.Logger = self._SetupLogger()
self.GeneratedFiles: List[str] = []
self.FailedFiles: List[str] = []
@@ -27,12 +30,12 @@ class StubGen:
def _SetupLogger(self) -> logging.Logger:
"""设置日志"""
logger = logging.getLogger('StubGen')
logger: logging.Logger = logging.getLogger('StubGen')
logger.setLevel(logging.DEBUG if self.config.verbose else logging.INFO)
if not logger.handlers:
handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter(
handler: logging.StreamHandler = logging.StreamHandler(sys.stdout)
formatter: logging.Formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%H:%M:%S'
)
@@ -41,26 +44,26 @@ class StubGen:
return logger
def FindInputFiles(self) -> List[Tuple[str, str]]:
def FindInputFiles(self) -> list[tuple[str, str]]:
"""查找输入文件,返回 (文件路径, 相对路径) 列表"""
files = []
files: list[tuple[str, str]] = []
# 添加显式指定的文件
for FilePath in self.config.InputFiles:
if os.path.exists(FilePath):
RelPath = os.path.relpath(FilePath, self.config.InputDir or '.')
RelPath: str = os.path.relpath(FilePath, self.config.InputDir or '.')
files.append((FilePath, RelPath))
else:
self.logger.warning(f"Input file not found: {FilePath}")
# 从输入目录查找
if self.config.InputDir and os.path.exists(self.config.InputDir):
InputPath = Path(self.config.InputDir)
InputPath: Path = Path(self.config.InputDir)
for pattern in self.config.IncludePatterns:
for FilePath in InputPath.rglob(pattern):
# 检查是否在排除列表中
excluded = False
RelPath = os.path.relpath(str(FilePath), self.config.InputDir)
excluded: bool = False
RelPath: str = os.path.relpath(str(FilePath), self.config.InputDir)
for exclude_pattern in self.config.ExcludePatterns:
if FilePath.match(exclude_pattern) or RelPath == exclude_pattern:
@@ -71,10 +74,10 @@ class StubGen:
files.append((str(FilePath), RelPath))
# 去重并保持顺序
seen = set()
UniqueFiles = []
seen: set[str] = set()
UniqueFiles: list[tuple[str, str]] = []
for FilePath, RelPath in files:
key = FilePath
key: str = FilePath
if key not in seen:
seen.add(key)
UniqueFiles.append((FilePath, RelPath))
@@ -84,10 +87,10 @@ class StubGen:
def _GenerateGuardName(self, FilePath: str) -> str:
"""生成宏守卫名称"""
# 获取文件名(不含扩展名)
BaseName = os.path.splitext(os.path.basename(FilePath))[0]
BaseName: str = os.path.splitext(os.path.basename(FilePath))[0]
# 转换为大写,替换特殊字符为下划线
guard = re.sub(r'[^a-zA-Z0-9_]', '_', BaseName).upper()
guard: str = re.sub(r'[^a-zA-Z0-9_]', '_', BaseName).upper()
guard = re.sub(r'_+', '_', guard) # 合并多个下划线
guard = guard.strip('_')
@@ -96,12 +99,12 @@ class StubGen:
def _GetOutputPath(self, RelPath: str) -> str:
"""获取输出文件路径"""
# 更改扩展名为 .pyi (Python stub file)
BaseName = os.path.splitext(RelPath)[0]
OutputRelPath = BaseName + '.pyi'
BaseName: str = os.path.splitext(RelPath)[0]
OutputRelPath: str = BaseName + '.pyi'
# 构建完整输出路径
if self.config.OutputDir:
OutputPath = os.path.join(self.config.OutputDir, OutputRelPath)
OutputPath: str = os.path.join(self.config.OutputDir, OutputRelPath)
else:
OutputPath = OutputRelPath
@@ -113,10 +116,10 @@ class StubGen:
self.logger.info(f"Processing: {InputFile}")
# 确定输出文件路径
OutputFile = self._GetOutputPath(RelPath)
OutputFile: str = self._GetOutputPath(RelPath)
# 确保输出目录存在
OutputDir = os.path.dirname(OutputFile)
OutputDir: str = os.path.dirname(OutputFile)
if OutputDir and not os.path.exists(OutputDir):
if not self.config.DryRun:
os.makedirs(OutputDir, exist_ok=True)
@@ -127,11 +130,11 @@ class StubGen:
return True
# 根据文件类型选择处理方式
ext = os.path.splitext(InputFile)[1].lower()
ext: str = os.path.splitext(InputFile)[1].lower()
if ext in ['.h', '.c']:
# C 文件:使用 CHeaderParser
content = self._GenerateFromC(InputFile, RelPath)
content: str = self._GenerateFromC(InputFile, RelPath)
elif ext == '.py':
# Python 文件:使用 PythonToStubConverter
content = self._GenerateFromPy(InputFile, RelPath)
@@ -149,24 +152,23 @@ class StubGen:
except Exception as e:
self.logger.error(f"Failed to process {InputFile}: {e}")
import traceback
traceback.print_exc()
self.FailedFiles.append(InputFile)
return False
def _GenerateFromC(self, InputFile: str, RelPath: str) -> str:
"""从 C 文件生成存根"""
parser = CHeaderParser()
parser: CHeaderParser = CHeaderParser()
parser.parse_file(InputFile)
generator = PythonStubGenerator(parser)
ModuleName = os.path.splitext(os.path.basename(InputFile))[0]
content = generator.generate(ModuleName)
generator: PythonStubGenerator = PythonStubGenerator(parser)
ModuleName: str = os.path.splitext(os.path.basename(InputFile))[0]
content: str = generator.generate(ModuleName)
# 添加宏守卫(如果需要)
if self.config.GenerateGuards:
guard_name = self._GenerateGuardName(InputFile)
guard_comment = f"\n# Guard: {guard_name}\n"
guard_name: str = self._GenerateGuardName(InputFile)
guard_comment: str = f"\n# Guard: {guard_name}\n"
content = content + guard_comment
return content
@@ -174,15 +176,15 @@ class StubGen:
def _GenerateFromPy(self, InputFile: str, RelPath: str) -> str:
"""从 Python 文件生成存根"""
with open(InputFile, 'r', encoding='utf-8') as f:
PyContent = f.read()
PyContent: str = f.read()
ModuleName = os.path.splitext(os.path.basename(InputFile))[0]
content = PythonToStubConverter.convert(PyContent, ModuleName)
ModuleName: str = os.path.splitext(os.path.basename(InputFile))[0]
content: str = PythonToStubConverter.convert(PyContent, ModuleName)
# 添加宏守卫(如果需要)
if self.config.GenerateGuards:
guard_name = self._GenerateGuardName(InputFile)
guard_comment = f"\n# Guard: {guard_name}\n"
guard_name: str = self._GenerateGuardName(InputFile)
guard_comment: str = f"\n# Guard: {guard_name}\n"
content = content + guard_comment
return content
@@ -194,7 +196,7 @@ class StubGen:
self.logger.info("=" * 60)
# 查找输入文件
InputFiles = self.FindInputFiles()
InputFiles: list[tuple[str, str]] = self.FindInputFiles()
if not InputFiles:
self.logger.warning("No input files found!")
@@ -205,7 +207,7 @@ class StubGen:
self.logger.debug(f" - {RelPath}")
# 处理每个文件
SuccessCount = 0
SuccessCount: int = 0
for FilePath, RelPath in InputFiles:
if self.GenerateStub(FilePath, RelPath):
SuccessCount += 1
@@ -222,8 +224,8 @@ class StubGen:
return SuccessCount == len(InputFiles)
def main():
parser = argparse.ArgumentParser(
def main() -> None:
parser: argparse.ArgumentParser = argparse.ArgumentParser(
description='StubGen - Generate Python stub files from C/H/Py sources',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog='''
@@ -246,7 +248,7 @@ Examples:
)
# 输入选项
InputGroup = parser.add_mutually_exclusive_group(required=True)
InputGroup: argparse._MutuallyExclusiveGroup = parser.add_mutually_exclusive_group(required=True)
InputGroup.add_argument('-c', '--config', help='Configuration file (JSON)')
InputGroup.add_argument('-i', '--input', help='Input file')
InputGroup.add_argument('-d', '--directory', help='Input directory')
@@ -268,14 +270,14 @@ Examples:
parser.add_argument('--dry-run', action='store_true',
help='Dry run (do not create files)')
args = parser.parse_args()
args: argparse.Namespace = parser.parse_args()
# 加载配置
if args.config:
if not os.path.exists(args.config):
print(f"Error: Config file not found: {args.config}")
_vlog().error(f"配置文件未找到: {args.config}")
sys.exit(1)
config = StubGenConfig.from_file(args.config)
config: StubGenConfig = StubGenConfig.from_file(args.config)
else:
config = StubGenConfig()
config.IncludePatterns = args.include or ['*.h', '*.c', '*.py']
@@ -295,12 +297,12 @@ Examples:
config.OutputDir = args.output
# 创建生成器并运行
generator = StubGen(config)
generator: StubGen = StubGen(config)
# 如果指定了单个输出文件,直接处理
if args.input and args.output and not os.path.isdir(args.output):
RelPath = os.path.relpath(args.input, config.InputDir or '.')
success = generator.GenerateStub(args.input, RelPath)
RelPath: str = os.path.relpath(args.input, config.InputDir or '.')
success: bool = generator.GenerateStub(args.input, RelPath)
else:
success = generator.run()
@@ -308,4 +310,4 @@ Examples:
if __name__ == '__main__':
main()
main()