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

如何使用Python的AST检测内置函数、类名被意外赋值的Bug

用AST检测内置名被覆盖Bug的实现方案

对应报错场景复现:

>>> list("abc")
['a', 'b', 'c']
>>> list = 42
>>> list("xyz")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable

这类给内置类、内置函数赋值导致原有定义被覆盖的问题,完全可以用Python标准库的ast模块做静态扫描检测,不需要运行代码就能提前发现问题,实现逻辑非常直接:

  • 先拿到所有内置名称的基线名单
    不用手动整理内置函数、内置类、内置异常的名称,直接从builtins模块取就行,自动适配不同Python版本的内置名差异:
    import builtins
    # 收集所有内置名称作为检测黑名单
    BUILTIN_BLACKLIST = set(dir(builtins))
    
  • 解析源码生成AST后,遍历所有会产生名称绑定的节点
    要覆盖所有会把名称绑定到当前作用域的语法场景,漏了就会出现检测不到的情况:
    • 普通赋值语句:对应AST节点ast.Assign,比如list = 42这类直接赋值的场景,提取赋值目标的变量名
    • 函数/类定义:对应ast.FunctionDef、ast.AsyncFunctionDef、ast.ClassDef节点,比如自定义函数叫eval、自定义类叫dict的场景
    • 导入语句:对应ast.Import、ast.ImportFrom节点,比如from os import open这种导入语句覆盖内置open的场景,要同时处理别名的情况
  • 命中黑名单直接告警
    把提取到的每个待绑定名称和内置黑名单做匹配,只要命中就记录告警信息,标注清楚问题所在的行号、被覆盖的内置名即可。
  • (可选优化)按作用域降低误报
    如果觉得检测太严格,可以区分作用域:全局作用域下覆盖内置名的影响范围最大,优先告警;函数/类内部的局部作用域如果定义了和内置名同名的变量,只要作用域内没有把该名称当原有内置功能调用,影响范围有限,可以调整告警级别。

下面是可以直接跑的最简检测脚本:

import ast
import builtins

BUILTIN_BLACKLIST = set(dir(builtins))

class BuiltinOverwriteChecker(ast.NodeVisitor):
    def __init__(self):
        self.alerts = []

    def _match_builtin(self, name, line_no):
        if name in BUILTIN_BLACKLIST:
            self.alerts.append(f"Line {line_no}: 内置名称 '{name}' 被覆盖,可能触发'int object is not callable'这类运行时报错")

    # 检测普通赋值
    def visit_Assign(self, node):
        for target in node.targets:
            if isinstance(target, ast.Name):
                self._match_builtin(target.id, target.lineno)
        self.generic_visit(node)

    # 检测函数/异步函数定义
    def visit_FunctionDef(self, node):
        self._match_builtin(node.name, node.lineno)
        self.generic_visit(node)
    def visit_AsyncFunctionDef(self, node):
        self._match_builtin(node.name, node.lineno)
        self.generic_visit(node)

    # 检测类定义
    def visit_ClassDef(self, node):
        self._match_builtin(node.name, node.lineno)
        self.generic_visit(node)

    # 检测import导入
    def visit_Import(self, node):
        for alias in node.names:
            bind_name = alias.asname if alias.asname else alias.name.split('.')[0]
            self._match_builtin(bind_name, node.lineno)
        self.generic_visit(node)
    def visit_ImportFrom(self, node):
        for alias in node.names:
            bind_name = alias.asname if alias.asname else alias.name
            self._match_builtin(bind_name, node.lineno)
        self.generic_visit(node)

if __name__ == "__main__":
    # 测试用的问题代码
    test_src = """
list("abc")
list = 42
list("xyz")
"""
    ast_tree = ast.parse(test_src)
    checker = BuiltinOverwriteChecker()
    checker.visit(ast_tree)
    for alert in checker.alerts:
        print(alert)

运行上面的脚本,会直接输出Line 3: 内置名称 'list' 被覆盖,可能触发'int object is not callable'这类运行时报错,不需要运行代码就能精准定位问题位置。

内容的提问来源于stack exchange,提问作者Ummulkhair Kadri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 13:57:32