You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 18:39:21