awesome-python 构建时报 slug collision in /categories/ namespace 怎么排查
【免费下载链接】awesome-pythonThe definitive list that answers "I want to do X in Python, which tool should I use?"项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-python
用make build构建 awesome-python 网站时,如果 README.md 里两个分类或分组的标题 slug 化后相同,website/build.py会直接抛出ValueError,构建终止、不产出任何站点。本文覆盖这个报错的完整排查路径:确认报错内容 → 理解 slug 如何由标题生成 → 在 README.md 中定位撞名来源 → 改名并验证构建通过。
先确认你看到的是不是这个错误
在仓库根目录执行构建:
make buildmake build实际执行的是uv run python website/build.py(见 Makefile)。如果 README 结构没问题,构建会正常结束并打印三行信息:groups/categories 数量、Total entries、Output 路径。
如果 slug 撞名,则不会走到渲染阶段,而是抛出:
ValueError: slug collision in /categories/ namespace: [<slug 列表>]. Rename a category or group so their slugs differ.报错信息本身给出了两样东西:冲突的具体 slug(可能不止一个,按字母排序的列表),以及处理方向——把其中某个 category 或 group 改名,让 slug 不再相同。这是 build.py 中的检查逻辑,也是修复的唯一文档化入口。
slug 是怎么生成的,什么情况下会撞
每个顶层 slug 由标题名经 readme_parser.py 中的slugify()生成,规则是:
- 转小写;
- 删除所有非
a-z0-9、空白和连字符的字符(下划线、&、!等一律去掉); - 空白压缩替换为单个
-; - 多个连续
-压缩为一个。
tests/test_build.py 里的用例展示了几个真实映射,可直接用于心算核对:
| 标题名 | slug |
|---|---|
Admin Panels | admin-panels |
RESTful API | restful-api |
Command-line Tools | command-line-tools |
Date and Time | date-and-time |
由此可以推出三类典型撞名:
- 两个名字只差被
slugify删除的字符:例如 "CLI Tools" 与 "CLI+Tools" 都会变成cli-tools; - 一个 Thematic Group 与一个 Section 同名:仓库自带的回归测试 test_build_fails_when_group_and_category_slug_collide 用的最小 README 正是这种情况——分组标记
**Widgets**和分类标题## Widgets并存,构建必然失败; - 与保留 slug
built-in撞名:构建在检查重复时,会把常量BUILTIN_SLUG(值为built-in,见 build.py)一并计入,所以任何 slug 化为built-in的标题(如 "Built in"、"Built-in")也会触发该错误。
定位撞名来源:从报错 slug 反查 README.md
AGENTS.md 明确 README.md 是站点内容的唯一数据源,website/只是把它渲染成静态站点,所以修复位置一定在 README.md 的标题里,不在website/下。
两类会产生顶层 slug 的标题,位置不同:
- 分类(category/Section):
## Projects与## Resources(或## Contributing)之间,##/###级别的标题(如## Testing); - 分组(Thematic Group):Projects 区域内的加粗独占段落,如
**AI & ML**、**Web Development**。没有加粗标记之前的分类会归入名为Other的组,其 slug 为other,这是 readme_parser.py 的解析规则。
排查步骤:
- 记下报错列表中的每个 slug;
- 在 README.md 的 Projects 区域内检索可能 slug 化为该值的标题文本(考虑
slugify会删掉&、下划线等字符,搜索时放宽匹配,例如找built-in要同时搜 "Built-in" 和 "Built in"); - 对候选标题用
slugify实际计算确认,而不是只看名字相似。仓库已提供现成函数:
uv run python -c "from readme_parser import slugify; print(slugify('你的标题文本'))"slugify('你的标题文本')中引号内替换为你在 README 里找到的候选标题。把冲突列表里的每个 slug 都对应到具体标题后,就能判断该改哪一个。
修复:改一个标题让 slug 区分开
按报错信息给出的处理方式,在 README.md 中重命名其中一个分类标题或分组标记,使两者的 slug 不同。例如把## Built in改成## Built-in Utilities(slug 变为built-in-utilities),或与Widgets撞名的分组标记**Widgets**改成**Widget Tools**。
两点注意:
- 改哪个都行,报错信息不强制区分"category 优先还是 group 优先",任选一个改名即可;
- 改名后,README.md 顶部
## Categories里的目录链接锚点(如[#widgets](#widgets))也应同步指向新标题,否则目录项会指向不存在的锚点——目录与 Projects 区域标题的对应关系在 readme_parser.py 的解析里并不强制,但站点渲染会按新标题生成页面路径/categories/<新slug>/。
验证修复
- 重新构建:
make build成功判据是不再抛出ValueError,并打印构建完成信息(groups/categories 数量、Total entries、Output 路径),产物写入website/output/。 2. 运行 slug 冲突回归测试,确认现有测试不受影响:
uv run pytest website/tests/ -v其中 test_build_fails_when_group_and_category_slug_collide 会用最小 README 复现该错误,验证构建对撞名场景的行为保持不变。
需要注意的边界:slug 检查只覆盖顶层命名空间(所有 category slug + 所有 group slug + 保留的built-in),子分类(Section 内的缩进 bullet 标签)的 slug 只用于/categories/<分类>/<子分类>/这类更深层路径,不参与这次重复检查。所以修复完顶层冲突后构建即应通过;如果改名又引出其他问题,再按新的报错信息重复上面的定位流程。
【免费下载链接】awesome-pythonThe definitive list that answers "I want to do X in Python, which tool should I use?"项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-python
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考