Python自研代码与内置/第三方模块代码占比统计方案咨询
针对Python仓库自研/模块代码占比统计的可行思路
一、AST增强分析方案
你之前尝试AST没找到便捷方法,核心是要先关联导入模块和调用节点:
- 第一步:遍历AST收集所有导入的模块(包括内置、第三方),用
ast.Import和ast.ImportFrom节点提取模块名,存入集合(注意处理from xxx.yyy import zzz这种,只取顶级模块xxx)。 - 第二步:遍历所有
ast.Call调用节点,判断调用对象是否属于已收集的模块:- 比如
requests.get()的Call节点,其func是Attribute类型,value是Name(id='requests'),只要requests在导入集合里,就标记为第三方模块调用。 - 对于直接调用模块级函数(如
os.path.exists()),同理判断顶级模块名。
- 比如
- 第三步:结合节点的行号信息(
lineno和end_lineno),统计自研代码行(函数定义、类定义、自定义逻辑、非模块调用的执行代码)和模块调用代码行的占比。
示例代码片段:
import ast def collect_imported_modules(tree): imported = set() for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: imported.add(alias.name.split('.')[0]) elif isinstance(node, ast.ImportFrom): if node.module: imported.add(node.module.split('.')[0]) return imported def count_code_lines(tree, imported_modules): self_lines = 0 module_lines = 0 def is_module_call(node): if isinstance(node.func, ast.Attribute): if isinstance(node.func.value, ast.Name) and node.func.value.id in imported_modules: return True elif isinstance(node.func, ast.Name) and node.func.id in imported_modules: return True return False for node in ast.walk(tree): # 统计自研代码:函数、类、赋值、自研表达式等 if isinstance(node, (ast.FunctionDef, ast.ClassDef, ast.Assign, ast.Expr)): # 排除模块调用的表达式 if not (isinstance(node, ast.Expr) and isinstance(node.value, ast.Call) and is_module_call(node.value)): self_lines += node.end_lineno - node.lineno + 1 # 统计模块调用代码 elif isinstance(node, ast.Call) and is_module_call(node): module_lines += node.end_lineno - node.lineno + 1 return self_lines, module_lines # 使用示例 with open("example.py", "r") as f: tree = ast.parse(f.read()) imported = collect_imported_modules(tree) self, module = count_code_lines(tree, imported) print(f"自研代码行: {self}, 模块调用行: {module}, 占比: {self/(self+module):.2%}")
二、静态分析结合模块来源校验
为了避免误判自定义模块和第三方模块,可以结合Python模块的路径判断:
- 内置模块:用
sys.builtin_module_names直接判断。 - 第三方模块:通过
importlib.util.find_spec(module_name)获取模块路径,判断是否在site-packages目录下。 - 自研模块:既不是内置也不在
site-packages的模块,标记为自研(注意处理仓库内部的相对导入)。 - 把模块分类后,再结合AST统计对应调用的代码占比,能更精准区分“第三方模块调用”和“自研模块调用”。
三、动态覆盖率工具改造(适合精准统计)
如果允许批量执行仓库代码,可以改造coverage.py:
- 修改覆盖率统计逻辑,当执行到模块代码(内置/第三方)时,不将这些行计入自研代码;只统计用户仓库内的代码行。
- 优点是能处理动态导入(如
__import__、importlib)、运行时生成的代码等静态分析无法覆盖的场景;缺点是需要每个仓库有可执行的测试用例,批量执行成本较高。
四、正则+AST混合方案(快速落地)
先通过正则快速匹配所有导入语句和模块调用行,再用AST修正误判:
- 正则匹配
import\s+(\w+)、from\s+(\w+)\s+import提取模块名。 - 正则匹配
(\w+)\.\w+\(这类模块调用行,初步统计模块调用行数。 - 用AST过滤掉自定义函数/类与模块名重名的情况,比如用户自己定义了
requests函数,正则会误判,AST可以识别调用的是自定义函数而非第三方模块。
注意局限性
- 动态导入、运行时修改模块的场景,静态分析容易漏判,需要结合动态分析补充。
- 继承第三方类的自定义子类、对模块对象的二次封装(如
my_request = requests.get),需要额外逻辑区分是否属于自研代码。
内容的提问来源于stack exchange,提问作者Deep6
相关产品推荐
相关产品推荐

