首页
/ awesome-python ADR 0001 详解:把列表从"目录"重构为"显而易见选择的短名单"

awesome-python ADR 0001 详解:把列表从"目录"重构为"显而易见选择的短名单"

2026-09-04 19:46:43作者:柏廷章Berta

本文基于 awesome-python 仓库中的架构决策记录 docs/adr/0001-shortlist-not-catalog.md(状态:accepted),完整还原一次编辑策略重构的决策过程:为什么一个条目超过 576 条、分类无限膨胀的 awesome-* 列表要放弃"目录(catalog)"定位,转而成为回答"I want to do X in Python, which tool should I use?"这一唯一问题的"短名单(shortlist)"。读完后,你将掌握这套 curation 体系的完整规则(Use Case、Obvious Choice、Challenger、Displacement、Cap、Split)、其证据链来源(PyPI 下载量而非 GitHub stars)、以及规则如何在 CONTRIBUTING.mdCONTEXT.mddocs/audit-logs.md 和网站构建管线中落地执行。

1. 背景:旧的收录模型如何失效

ADR 记录了触发重构的三个事实(均以 2026 年中为时间基准):

  • 列表当时持有 576 个条目,分布在 75 个 Section。当前 README.md 中的 ### 章节数为 77(含后续增减),与这一量级吻合;
  • 条目流入量同比增长 24 倍:过去 12 个月新增 96 条,而前一年只有 4 条,且增量集中在 AI and Agents 这类 Section(该 Section 曾达 41 个条目);
  • 旧的收录模型是"三条泳道"——Industry Standard、Rising Star、Hidden Gem,且只有第一条泳道有上限。这个模型会接纳任何"孤立来看足够好"的项目,结果是分类无限增长,列表不再回答读者的实际问题:"X 这件事我用什么?"

重构的哲学依据被直接写进 ADR,即 Zen of Python 的一条:

There should be one — and preferably only one — obvious way to do it.

也就是说,列表的定位从"好的 Python 项目大全"收窄为"每个使用场景下显而易见的少数选择"。

2. 决策内容:新规则的完整定义

这一节逐条继承 ADR《The decision》部分,并结合仓库文件补充其执行细节。

2.1 收录边界测试:从"用 Python 写的"变为"服务 Python 开发者"

ADR 用一个新测试替换了旧要求"primarily written in Python (>50%)":

  • 新测试(Serves Python Developers):只要 Python 开发者在自己的 Python 工作中使用它,实现语言和打包方式就无关紧要——uvty 是 Rust 写的,agent skill packs 是 markdown,都属于;反过来,一个没人会在 Python 工作中使用的纯 Python 项目不收录;
  • CONTEXT.md 中显式标注旧术语为应避免项:_Avoid_: Python-first, written-in-Python (old requirement — removed),防止旧心智模型回流。

CONTRIBUTING.md 的"Quality Requirements"第一条与该测试完全对齐,并给出四个并行的硬性质量门槛:Active(12 个月内有提交)、Stable(非 alpha/beta/experimental)、Documented(README 有示例和用例说明)、Established(仓库至少 1 个月历史)。

2.2 Use Case 结构:投稿者不能创造自己需要的分类

ADR 规定 Use Case 由列表既有结构定义,"an entry PR can never create the subcategory it needs"。结合 CONTEXT.md 的结构词汇表,列表的层级是:

层级 定义 与规则的关系
Thematic Group 粗体分组行(如 "AI & ML"、"Web Development"),在 TOC 和正文中聚类 Section 纯组织,不绑定规则
Section README.md 中的 ### 标题(如 "Testing"、"AI and Agents"),位于某个 Thematic Group 之下 无子分类时,整个 Section 就是单个 Use Case
Subcategory Section 内带缩进条目的命名项目符号(如 "Testing" 下的 "Mock") 每个 Subcategory 就是一个 Use Case
Entry 单个条目,格式 - name - Description.,是"被收录/被替换/被修剪"的最小单位 有 PyPI 包名时以 PyPI 包名为显示名,否则用仓库名

一个值得注意的辨析:CONTEXT.md 特意警告不要用 "Category" 一词——TOC 里虽然叫 categories,但规则绑定的是 Use Case 而非 Section。投稿 PR 不能新建 Section 或 Subcategory 来"给项目安家",结构变更(新 Section、新 Subcategory、把过大的 Use Case 拆细)是维护者专属操作,CONTRIBUTING.md 的 "Automatic Rejection" 第一条就把"PR 创建新的 section 或 subcategory 并往里填内容"列为直接关闭项。

2.3 Cap:每 Use Case 至多 3 个 Obvious Choice + 2 个 Challenger,硬上限 5

ADR 给出的数字是 最多 3 个 Obvious Choices,外加最多 2 个标记为 Challenger 的条目,硬上限 5,并注明"numbers provisional, to be reviewed after the prune"——数字是临时性的,要在清剪完成后复审。

CONTRIBUTING.md 对此的表述进一步澄清了数字与质的关系:

Hard maximum: 5 entries per use case. This is a qualitative bar first and a numeric backstop second — most use cases should carry fewer.

先定性门槛,后数量兜底,是天花板而非地板:一个新建的 Use Case 完全可以只放 1 个条目。CONTEXT.md 中 Cap 词条的措辞与之一致("a ceiling, not a floor")。

两层条目的区别与准入标准:

  • Obvious Choice:资深 Python 开发者被问到"这件事用什么"时会不假思索说出的名字。注意 CONTEXT.md 标注旧名 "Industry Standard" 为应避免项;
  • Challenger:还不是显而易见选择、但是某个在位者的可信继任者。其准入要求采用轨迹证据(adoption-trajectory evidence),而不是单纯的流行度。旧泳道名 "Rising Star" 和 "Hidden Gem" 均被标注废弃——ADR《Considered options》解释了原因:Rising Star 的势头在新模型下应作为 Challenger 名额或 Displacement 论据的证据,Hidden Gem 则与"obvious"在定义上互斥。

条目文本中没有任何 Challenger 标记——位置即标记:Use Case 内 Obvious Choices 排前、Challengers 排后,各自按 PyPI 月下载量降序;标准库模块一律排在使用场景最前;无下载量信号的项目(agent skill packs、非 PyPI 分发的项目)在同层内按字母序殿后。这意味着一个 Use Case 的末尾条目可能就是 Challenger 而非普通在位者。

2.4 证据信号:PyPI 下载量优先于 GitHub stars,且判断保留裁量

ADR 规定收录由维护者编辑判断决定,判断"primarily"参考的信号是 PyPI 下载量而非 GitHub stars,且判断最终生效(stated as final)。判断权同时覆盖该信号的已知失效模式

  1. CI / 依赖拉高导致的虚高下载量;
  2. 模型类项目以权重文件形式被下载消费,而非 pip 安装(CONTEXT.md 将这一条扩展到一切非 pip 消费形式,如 renpy 的 SDK 下载、thumbor 这类部署型服务);
  3. 大而特定的受众被误读为"niche"。

这套"下载量优先"的证据链在仓库中有对应工具支撑:website/ 目录提供了三条抓取 PyPI 下载量的实现——website/fetch_pypi_downloads_via_bigquery.py(Google BigQuery 官方数据集)、website/fetch_pypi_downloads_via_pepy.py(PePy API)和 website/fetch_pypi_downloads_via_clickpy.py(ClickPy),维护者可交叉验证下载量数据;另有 website/fetch_github_stars.py 抓取 stars 作为对照参考。而 AGENTS.md 进一步规定:每个保留/移除理由都必须在决策时对照实时在线数据核验(下载量、仓库活跃度与归档状态、PyPI 元数据、项目文档),层级判定(obvious choice vs challenger)还需检索证据(采用轨迹、社区口碑),"训练数据的记忆不是证据,无法核验的必须标注为判断"。

2.5 Displacement:满员后的唯一入口

ADR 规定:一旦 Use Case 达到上限,唯一的进入方式是 Displacement——PR 必须指名它替换的条目,并论证新项目把那个条目的活儿干得更好。CONTRIBUTING.md 用 "One in, one out" 概括,且 CONTRIBUTING.md 的 "Automatic Rejection" 明确:Use Case 已满且 PR 没有 Displacement 论证的,直接关闭。CONTEXT.md 中还记录了一个相关概念 Second Tier:Challenger 名额可以由"被降级的在位者"占据(如 clickhouse-driver 排在官方客户端之后、django-haystack 排在 Search 场景的后面),它同样占用两个 Challenger 名额、同样以位置为标记;采用轨迹门槛只约束新准入,不约束降级。

2.6 标准库、资源类 Section 与追溯清剪

  • 标准库:只有当 stdlib 模块本身就是该 Use Case 的显而易见选择时才占位——ADR 与 CONTRIBUTING.md 共用同一个判例:"tomllib yes, unittest no";
  • Resources 类 Section(Newsletters、Podcasts、Websites)暂时不在改革范围内,且从源码结构看,网站构建管线根本不解析它们(CLAUDE.md:"Resources sections are not project entries: out of audit scope, and the website never parses them");
  • 追溯清剪(retroactive prune):存量条目接受同一测试,执行方式是分阶段、最差先行的清剪(per-section sweep commits,每个 Section 一个提交,提交体逐一列出移除原因);被移除条目直接删除——git 历史就是档案。这一提交纪律在 AGENTS.md 中成文:"a prune sweep is one commit per section, its body listing each removal with its reason",与常规"每次提交一个条目"的规则互为例外。

3. 被否决的备选方案:ADR 的价值所在

ADR《Considered options》完整保留了三条被否决路线及其否决理由,这是理解"为什么是现在这套规则"的关键:

备选方案 否决理由
保留三条泳道、每条泳道都加上限 一旦准入变成比较性的,"够不够好才能进来"就是答错了的问题;Rising Star 的势头应当转化为 Challenger 名额或 Displacement 的证据,Hidden Gem 与"obvious"定义上互斥
只立新规则、不做追溯清剪 每个被拒 PR 都会遭遇"但 X 还在列表上"的先例论证,且读者看不到任何变化
把被移除条目存档到单独文件 等于在"一次点击可达"处重建目录,稀释这次变革要恢复的列表身份

这三条否决理由分别对应规则体系中的三处设计:Displacement 机制(否决项 1)、分阶段 prune(否决项 2)、"git history is the archive"(否决项 3)。读 ADR 时应把决策与备选对照着读,才能明白为什么"直接删除"不是粗暴而是刻意选择。

4. 后果:ADR 明确接受的代价

ADR《Consequences》部分逐条列出已接受的代价,每一条都值得工程/内容项目做类似重构时参照:

  1. 列表大幅缩水。ADR 给出的量化事实是:维护者对三个最大 Section 的预览保留了 80 条中的 45 条,并预判"most future PRs will be rejected for fullness, not badness"——未来多数 PR 会因"满员"而非"项目差"被拒;
  2. 主动放弃长尾搜索流量。awesome-python.com 将不再承载数百个小众工具名,因此失去对应的长尾检索流量。ADR 的表态是明确接受的:"reader trust over search surface"(读者信任优先于搜索覆盖面);
  3. 快速变化领域靠 Displacement 吸收流失。AI and Agents 这类领域按当前用量列出领跑者,人员/项目更替通过 Displacement 完成;过大的 Use Case 由维护者修剪或 Split(拆分为更细的 Use Case)。CONTEXT.md 对 Split 的定义是"在规模反映真正不同的活儿时,优先于任何修剪考虑的、维护者专属的结构重组";
  4. 外链 awesome-* 列表作为泄压阀CONTRIBUTING.md 明确指向这类列表(例如 awesome-python-testing):"they exist precisely so this list doesn't have to be one"——想要穷举目录的读者走那些列表,本列表因此不必成为目录。

5. 规则如何落地:仓库中的执行证据

ADR 是"决定",而本仓库中至少有四处机制保证决定被执行,且彼此交叉印证:

5.1 CONTEXT.md:给规则和 LLM 的统一词汇表

CONTEXT.md 全文定义了本决策引入的领域语言(Entry、Sub-item、Use Case、Obvious Choice、Cap、Displacement、Challenger、Second Tier、Override、Split、Audit),并为每个词条附 "Avoid" 旧名对照。ADR 结尾的 "See CONTEXT.md for the vocabulary" 正是指向这里。值得一提的是,website/ 的构建管线会渲染出 website/templates/llms.txt 对应的 llms.txt 文件——这套结构化词汇同时服务于人类维护者和 LLM 消费者。

5.2 CONTRIBUTING.md:可执行的准入与拒绝清单

CONTRIBUTING.md 把 ADR 的抽象规则翻译成 PR 评审可直接套用的检查项:5 条 Quality Requirements、Admission 上限、Displacement、Dual-listing(同一工具在多个 Use Case 各占一席须独立挣得每席,且是维护者决策)、Override 条款(维护者可对特定条目/场景显式超限,但逐案生效、不可被投稿者引用),以及 5 步 Review Process(格式、分类、查重、活跃度、准入)和 9 条 Automatic Rejection 条款。

5.3 docs/audit-logs.md:超越单次提交的决策登记册

docs/audit-logs.md 是"单个 commit 无法展示的维护者决策"一览登记册,与 ADR 的"git history is the archive"原则互补:普通移除理由写在 prune sweep 的提交体里,而结构性例外记在此处。目前已登记两类:

  • Naming Exceptions:显示名偏离 PyPI 包名规则的 10 个条目(如 pytorch 对应包名 torchjinja 对应 Jinja2);
  • Mature-stable Keeps:5 个超出"12 个月活动"要求但按"成熟、稳定、无继任者"的编辑判断保留的条目(ftfy、itsdangerous、jieba、jinja、sortedcontainers)。

这正对应 CONTEXT.md 中 Override 词条的定义:维护者在 Audit 中做出、显式记录、逐案生效的超限决定,且"submitters cannot cite one"。

5.4 构建管线:结构词汇与解析器的对应关系

website/readme_parser.py 用 markdown-it-py 把 README.md 解析为结构化数据,其类型定义与 ADR/CONTEXT 词汇一一对应:ParsedGroup ↔ Thematic Group、ParsedSection ↔ Section、ParsedEntry.subcategory ↔ Subcategory、ParsedEntry.also_see ↔ Sub-item(缩进的 awesome-* 链接,不占名额、不计入 Cap、随父条目存亡)。模块 docstring 还记录了几个对 README 编辑者很重要的经验事实:## Projects 之前的内容被忽略;新增 Subcategory 不需要改解析器(无前置链接的项目符号 + 缩进条目即可被识别);构建输出的 "Total entries" 统计包含 Sub-item 而不仅是 Entry。这说明 ADR 的"结构由列表既有结构定义"在技术上是可自举的——结构词汇表与解析器、CONTRIBUTING 规则、审计流程形成了闭环。

6. 小结:一份可复用的"列表瘦身"决策模板

ADR 0001 的价值不止于 awesome-python 自身,它完整示范了内容型/索引型开源项目从"目录"转向"短名单"时的决策结构:

  1. 先用数据定义病态(576 条、24x 流入、AI 类 41 条),再给重构立一个可检验的单句目标(Zen of Python 的 one obvious way);
  2. 规则按"边界测试 → 单位(Use Case)→ 名额(Cap)→ 信号(PyPI 下载量)→ 满员入口(Displacement)→ 追溯清剪"顺序定义,并明确数字是临时值、留待清剪后复审;
  3. 备选方案与否决理由同文保留,防止规则在后续评审中被"还原";
  4. 代价显式入账(缩水幅度、搜索流量、快速领域的更替机制),避免重构后各方对结果各执一词;
  5. 词汇表(CONTEXT.md)、操作规范(CONTRIBUTING.md)、决策登记册(audit-logs.md)、构建管线(readme_parser.py / llms.txt)四件套保证决策不悬浮在文档层,而是可执行、可审计、可被机器消费的。

如果需要在 awesome-python 中提交或评审条目,请以 CONTRIBUTING.md 为操作准绳、以 CONTEXT.md 为术语基准,并留意 docs/audit-logs.md 中已登记的例外——那正是本 ADR 所说的"判断保留裁量,但裁量被记录"的具体体现。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384