☰
轻量级CLI工具如何实现代码扫描、生成与统计:零依赖Python实践
2026/10/13 12:54:09 网站建设 项目流程

1. 项目整体设计与思路拆解

1.1 为什么会有 t3code 这个项目

t3code,全称是 Tiny Three Tools for Code,翻译成人话就是“三个小到不能再小的编码工具”。这名字听起来可能有点随意,但恰恰是我刻意追求的结果——在真正动笔写第一行代码之前,我就想清楚了:市场上那些重量级脚手架、代码分析平台、CI 质量门禁,动不动就是几百 MB 的运行时依赖、十几个微服务、一个插件市场,对于个人开发者或者小团队来说,安装配置的时间比用的时间还长,这本身就是一种开销。

t3code 想解决的不是“大而全”的问题,而是“日常重复劳动”的问题。我把平时写代码、维护代码库过程中最高频的三类动作,压缩成了三个子命令:gencode(快速生成代码骨架)、scan(扫描代码中的待办标记和潜在问题)、stat(统计代码量和基础复杂度指标)。这三个模块互不依赖、各自能独立运行,合在一起又是一个完整的开发辅助闭环。

如果你是一个正在学习命令行为什么这么好用、或者想给自己的代码库建立一些轻量级质量习惯的开发者,这个项目非常合适;如果你带了一个三五个人的小队,不想引入笨重的项目管理平台,只想每天花十秒钟看看代码健康状况,t3code 的 scan 和 stat 正好能填上这个空档。我自己就是这两个场景都经历过,所以做这套工具时的目标非常明确:装了能用、用完能删、删了也不心疼。

1.2 工具选型背后的关键权衡

在决定用 Python 标准库裸写,而不引入任何第三方依赖之前,我列了一个对比表,里面罗列了几种候选方案:

候选方案优势劣势结论
Python + argparse/正则零依赖、跨平台、启动快解析复杂语法时能力有限最终选择
Node.js + Babel AST生态强、AST 解析能力完整依赖体积大、安装慢不适合轻量目标
Go 编译单二进制部署最优雅、性能最好开发迭代速度一般、样本代码长后续可考虑
Shell 脚本组合零依赖、极简跨平台兼容性差、难以做参数解析直接排除

这里要给小白读者解释一个误区:不是说regex(正则表达式)就能解决所有代码分析问题,也不是说必须有 AST(抽象语法树)才算高级。关键在于你要分析的对象是谁。t3code 的 scan 模块主要面向的是代码里的标记注释(TODO、FIXME、HACK)和基础问题模式(超长行、连续空行、调试残留),这些用正则已经能覆盖九成场景;但如果要理解“这个函数到底有多少条独立路径”,那就不行了,那种需求我会明确引导你上真正的静态分析工具。

这个取舍背后的核心逻辑是想清楚“项目到底服务于谁”。t3code 的用户大概率是个人开发者,或者对工具链有“随手拿到就能用”的需求,那么每一毫秒的启动延迟、每一个下载失败的依赖,都是在消耗用户耐心。所以我把“零依赖”当成硬性指标,连第三方模板引擎(比如 Jinja2)都放弃了,改用 Python 标准库的string.Template,虽然变量替换形式简单一点,但胜在不需要装任何东西,拿到源码就能跑,这样才是最贴合场景的答案。

1.3 架构设计与模块划分

整个 t3code 的结构就只有三层:

  • 入口层:一个t3code.py主脚本,用argparse注册三个子命令,负责解析参数和分发调用。
  • 核心层:三个相互独立的模块文件(gencode_core.py、scan_core.py、stat_core.py),每个模块只做自己的事。
  • 公共层:一个config.py,负责统一读配置、定义项目根目录、输出格式化。

之所以把模块切得这么干净,是因为我受够了那种“一个入口文件长达三千行”的工具。你在维护的时候,改一处配置可能牵动五六个函数。t3code 里你要新增一个子命令,只需要三步:新建一个模块文件、在入口注册一下、在config.py里加一节默认配置,不需要理解其他模块的内部实现。这种“插件感”带来的好处是:就算你只喜欢其中某一个命令,也可以把那个模块文件单独抽出来放进自己的项目里,依然能跑。

还有一个细节值得说:为什么统一用自然语言风格的命令输出,而不是像很多 CLI 工具那样只回一个OK?因为 t3code 的目标用户是人,不是流水线机器。当你执行t3code scan,看到输出里写着“发现 3 处 TODO、2 处超长行,预计清理所需时间 15 分钟”,会比看到一串 JSON 格式的原始数据更直观。当然,我也留了--json参数,机器要读的时候再切格式,人机两相宜。

2. 三大核心模块实现细节

2.1 gencode:从模板快速生成代码骨架

gencode模块做的核心事情,就是“给你一个定制的起点”。很多人在新项目里最头疼的往往不是业务逻辑本身,而是第一步的配置工程:requirements.txt、setup.py、目录结构、.gitignore、CI 模板,这些内容每开一个项目就要重写一遍,绝大多数还是 Ctrl+C / Ctrl+V,还容易漏。

gcode 的设计思路基于一个关键观察:80% 的代码骨架是重复的,但每个人重复的那 80% 不一样。所以它的核心能力不是内置多少套项目模板,而是提供一套“模板变量替换 + 文件复制分发”机制。你只需要在项目根目录放一个.t3code/templates/文件夹,里面按自己的习惯放好模板文件,再用{{project_name}}、{{author}}这种占位符标记可变位置,然后一条命令就能生成一个新项目。

具体流程是:先执行t3code init生成默认配置和模板目录,然后编辑模板,再执行t3code gencode new_app --name my-project --author "John",它就会读取配置文件中定义的目录结构模板,把里面的占位符全部替换成你传入的参数,最终在./my-project/下生成整个骨架。这里我要分享一下实现里最重要的一个权衡:生成时用的模板引擎,我排除了 Jinja2,选了标准库string.Template。前者虽然支持循环和条件判断,但问题是:一旦模板里允许写逻辑,模板就会慢慢长成另一个项目——这个陷阱我在早期版本里踩过,后来把模板从“带流程控制的渲染器”简化成“纯变量替换器”,反而让模板保持在一个非常容易理解的水平。如果你的模板需要条件分支,我更倾向于维护两个不同的模板文件目录,而不是在模板文件里写逻辑。

2.2 scan:精准定位代码中的待办事项

scan模块是整个 t3code 里我个人用得最多的功能,也是最能直接体现“工具价值”的模块。它做的是在全项目范围内扫描代码注释中的标记,比如TODO、FIXME、HACK、XXX这些约定俗成的标记,同时也会检查一些明显的基础问题,比如单行超过 120 个字符、文件结尾缺少换行符、连续两个以上空行等。

扫描实现的底层逻辑并不复杂,核心就是两个部分:文件遍历和逐行匹配。文件遍历时我先读取.gitignore,把里面列出的目录和文件模式摘出来,同时加上代码本身的一些硬性排除项(比如常见的node_modules、vendor、dist目录),这样避免把第三方依赖的源码也扫进来,出一个几千行的噪音报告。逐行匹配部分用的是一组精心组合的正则表达式,比如匹配 TODO 标记的模式是#\s*(TODO|FIXME|HACK|XXX)\s*[::]?\s*(.*),同时要兼容 Python、JavaScript、Go、Java 这几种主流语言的注释风格。这里有一个细节:不同语言的注释前缀不一样,#只适用于 Python、Shell 等,而//适用于 JavaScript 等股份制语言,所以我做了一组语言到注释前缀的映射表,在扫描时根据文件后缀自动切换匹配模式。

实际扫描时,每个标记还会附带上所在文件的路径、行号、完整注释内容,以及一个按优先级排序的等级标签:FIXME和HACK标记为高优先级,TODO为中优先级,XXX为低优先级。执行完t3code scan --report后,你可以看到一份按文件归类、按优先级排序的纯文本报告,也可以加--json参数拿到结构化输出,方便自己写脚本去接 CI。这个模块最大的实用价值是:每隔两周清理一次技术债标记,项目的健康度会肉眼可见地提升,而不再是一堆“做完某功能就删除”的 TODO 永远躺在那里。

2.3 stat:摸清代码库的“体检指标”

stat模块像一个体检医生,不评判代码好坏,只提供客观数据指标。它能统计的东西包括:各类文件的数量、每个文件的代码行数、空行数、注释行数、总代码行数,以及一个简化的圈复杂度估算指标。这些数据合在一起,能让你在十秒内判断出一个代码库的基本健康程度。

代码行数统计看似简单,但里面有个容易踩坑的细节:怎么区分“代码行”和“注释行”以及“空行”?最简单的做法是按行遍历然后做正则匹配,但如果遇到多行注释或者字符串中的注释符号,纯行匹配就会误判。我最初做统计模块时就踩过这个坑,曾把一个字符串常量里的#当作注释符处理,导致注释行数虚高。后来我采用了一个折中方案:先判断当前行是否处于多行字符串/多行注释的上下文(通过扫描前后文的关键字符),再决定是否计入注释;不在上下文里的,简单按行首识别处理。也就是说,虽然 t3code 没有完整 AST,但对于多行注释的处理,用了一个简单的上下文状态机,准确率已经能到九成以上。

圈复杂度这个指标,我用的是经典的理解公式:考虑每个if、for、while、case、&&、||等分支点,遇到一个就加一,最低值是 1。这个简化版本虽然不如专业工具那么精确,但它胜在:实现只有二十多行,而且对一般项目已经能给出足够参考。当某个文件的圈复杂度统计值超过 20 时,我就会认真安排时间看看是不是需要重构了。这是 stat 模块最实用的一个场景:不用装复杂的分析工具,你就可以在提交之前快速判断“这次改动是不是让函数变得太复杂了”。

3. 实操过程与部署记录

3.1 环境准备与安装

我写 t3code 时选了 Python 3.8+ 作为实现语言,核心原因只有一个:在大多数开发环境里,Python 是预装程度最高的运行时,不用额外安装任何东西。你要做的就是把项目代码下载下来,或者干脆把几个.py文件复制到你的工具目录里。

这里我提供一个可以直接照做的安装步骤:

# 如果你通过 Git 管理,直接 clone 下来 git clone https://github.com/yourname/t3code.git cd t3code # t3code 其实就是一个 Python 包结构,直接安装为本地命令 pip install -e . # 如果不想污染全局环境,也可以创建虚拟环境后安装 python -m venv .venv source .venv/bin/activate # 在 Windows PowerShell 下则是 .venv\Scripts\Activate.ps1 pip install -e .

安装完成之后直接执行t3code --help,如果输出显示三个子命令的列表,说明安装成功。我个人推荐用虚拟环境方式,不是因为全局安装会出多大事,而是因为每个小项目独立运行环境、后续要卸掉pip uninstall也干净。

安装之后的第一件事不是直接跑命令,而是初始化配置目录。执行t3code init,它会在当前目录创建.t3code/文件夹,里面包含config.yaml(控制整体配置)、templates/目录(放自定义模板)、以及默认的扫描排除规则列表。如果你是在一个已有项目里初始化,它会自动检查项目里有没有.gitignore文件,如有则读取并合并到排除规则中。

3.2 配置文件与参数详解

t3code 的配置设计有一个原则:所有配置都集中在一个config.yaml里,每一项配置都有默认值,你只需要修改自己关心的部分。下面是一个最常见的配置示例:

# .t3code/config.yaml project: name: "my-awesome-project" author: "Your Name" language: "python" # 决定注释扫描的前缀策略 templates: dir: ".t3code/templates" variable_pattern: "{{.*?}}" # 默认用双花括号做变量标记 scan: max_line_length: 120 # 超过该长度算超长行 exclude_dirs: # 额外排除的目录,会与 .gitignore 合并 - "build" - "dist" tags: todo: "中优先级" fixme: "高优先级" hack: "高优先级" xxx: "低优先级" stat: complexity_threshold: 20 # 圈复杂度超限阈值 include_comment_stats: true output_format: "table" # 可选 table / csv / json

配置文件的每个 key 我都尽量起了自解释的名字。这里特别提醒一个经验:exclude_dirs一定要把build、dist这类目录加上,否则扫描时会把第三方打包产物、生成文件全部纳入统计,数据会非常难看。我自己第一次跑 stat 时就是没加排除,统计出来的“总代码行数”接近 50 万行,实际项目代码可能只有两万行,瞬间慌了。

3.3 三个子命令的实战调用

配置好以后,我们看几个真实的调用场景。

第一个场景:创建新模块骨架。

cd myproject t3code gencode create_module --name auth --author "Alice"

这条命令会读取.t3code/templates/module_tree/目录下的模板结构,把所有{{name}}替换成auth,{{author}}替换成Alice,然后生成类似auth/、auth/tests/、auth/README.md这样的一整套结构。如果你没有配置自己的模板,它会使用内置的一套基于 Python 包的默认模板。

第二个场景:扫描待办标记。

t3code scan --path src --report

执行后的输出大致长这样:

[高优先级] auth/login.py:42 HACK:硬编码了临时 jwt secret,上线前必须替换 [中优先级] database/connect.py:17 TODO: 需要补充连接池 [低优先级] utils/string.py:98 XXX: 此处逻辑待确认

这种一眼扫过去就知道哪里该动的输出,比打开 IDE 一个一个文件翻要高效太多了。

第三个场景:统计代码指标。

t3code stat --format table --sort-by complexity

输出会是一个按圈复杂度排序的表,列出每个文件的代码行数、注释占比、空行数量、复杂度分数。这样你可以快速定位项目里最需要重构的几个文件,而不必靠“感觉”判断。

3.4 接入 CI 流程

t3code 最有价值的用法之一,是把 scan 当成代码合入门禁。假设你用的是 GitLab CI 或 GitHub Actions,只需要在流水线里加一步:

# .gitlab-ci.yml 片段 t3code-scan: stage: test script: - pip install . - t3code scan --path . --json --fail-on-error only: - merge_requests

这里新增的--fail-on-error参数的作用是:如果扫码发现了FIXME或HACK标记,脚本将以非零状态退出,导致流水线失败,从而强制开发者在上麻药前解决高优先级标记。我在团队里推行这个规则后,新代码引入高优先级技术债的次数直接降到了接近零。对于 TODO 这种中优先级项,默认不会阻塞流程,但我建议在 MR 描述里注明新增了哪些 TODO,这样至少保证了技术债是“显性化”的,而不是等代码腐烂到根本不敢动的时候才发现。

4. 典型业务场景与调优实例

4.1 场景一:新项目三天完成脚手架配置

我接到的一个真实案例是,有个同事要启动一个新的数据处理服务,按以往的节奏,他光搭目录、配依赖、写 README 就要耽误大半天时间。用 t3code 之后,我把整个环境的模板提前固化到了.t3code/templates里:包括项目结构、预置的空配置、CI 模板、Dockerfile 基础框架、pre-commit 钩子配置。

他执行t3code gencode create_app --name>t3code scan --path . --ext .py --ext .js --ext .go

这样报告里只有真正的代码文件,配置文件的注释不会再产生干扰。这个调试案例给我最大的启发是:写工具的时候,一定要给用户留一个“缩小范围”的旋钮,而不是把所有情况硬编码在工具里。因为每个项目的代码风格、文件结构都不同,灵活的参数比“包治百病”的默认逻辑更重要。

5.4 核心经验的沉淀

用 t3code 一年多下来,我最深的一个感触是:做开发工具,绝不仅仅是写几个命令那么简单。一个工具能不能真正帮助你,取决于三个层面:第一是能否准确命中高频痛点——我选择了代码骨架、待办扫描、指标统计这三件事,因为它们在每个开发者的日常里几乎每天都会出现;第二是能否保持轻量——如果一个工具要用五分钟才能安装完,那我宁可不用它;第三是最难的——是否有足够的灵活性去适配不同项目的古怪习惯,也就是参数化、配置文件、排除规则这些细节。

这三条原则不只是 t3code 的设计哲学,也是评估任何开发者工具是否值得引入的判断标准。我在选择项目里要用的第三方工具时,也会不自觉用这三条去套,结果真的帮我淘汰了好几个“看起来很火但用起来很费劲”的依赖。工具存在的意义从来不是让人变得更忙碌,而是让人把人从重复劳动里解放出来,把时间花在真正需要判断力的事情上。

t3code 后续我打算把模板系统再加强一些,比如支持从远程仓库拉取模板,这样团队可以把公共的代码规范模板维护在一个独立仓库里,各项目自动同步。同时也会考虑用 Go 重写一版编译成单一可执行文件,让分发变得更无脑。但不管怎么演化,零依赖、三个命令、秒级响应这三件事,我不会动它们——这是这个项目的根。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询