feat: rename Built-in source type to Stdlib and show it in downloads column

Standard-library entries rendered "Not on PyPI" in the PyPI Downloads
column, which read like missing data rather than a deliberate category
— the build already forces downloads to None for them so they never
pick up a same-named PyPI backport. They now render a "Stdlib" badge
instead, while genuine non-PyPI entries keep "Not on PyPI".

The filter tag is renamed to match, so the source-type value, the row
filter tag, and the synthetic category heading all read Stdlib now.
The literal "Built-in" strings scattered through build.py are routed
through the existing BUILTIN_FILTER constant so the label lives in one
place. The category page slug stays "built-in" so the public URL
/categories/built-in/ does not break.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Vinta Chen
2026-08-23 01:01:17 +08:00
co-authored by Claude
parent b1f473319c
commit 2fc0edd565
4 changed files with 26 additions and 24 deletions
+9 -9
View File
@@ -22,7 +22,7 @@ SITE_URL = "https://awesome-python.com/"
SITEMAP_URL = f"{SITE_URL}sitemap.xml"
SITEMAP_NS = "http://www.sitemaps.org/schemas/sitemap/0.9"
BUILTIN_FILTER = "Built-in"
BUILTIN_FILTER = "Stdlib"
BUILTIN_SLUG = "built-in"
BUILTIN_PATH = f"/categories/{BUILTIN_SLUG}/"
BUILTIN_PUBLIC_URL = f"{SITE_URL}categories/{BUILTIN_SLUG}/"
@@ -32,7 +32,7 @@ SPONSORSHIP_PUBLIC_URL = f"{SITE_URL}sponsorship/"
SPONSORSHIP_DESCRIPTION = "Sponsorship for awesome-python: tiers, audience, and how to get your product in front of professional Python developers evaluating tools for production use."
SOURCE_TYPE_DOMAINS = {
"docs.python.org": "Built-in",
"docs.python.org": BUILTIN_FILTER,
"gitlab.com": "GitLab",
"bitbucket.org": "Bitbucket",
}
@@ -128,9 +128,9 @@ def sort_entries(entries: Sequence[TemplateEntry]) -> list[TemplateEntry]:
if downloads is not None:
return (0, -downloads, 0, name)
if stars is not None:
builtin = 1 if entry.get("source_type") == "Built-in" else 0
builtin = 1 if entry.get("source_type") == BUILTIN_FILTER else 0
return (1, -stars, builtin, name)
if entry.get("source_type") == "Built-in":
if entry.get("source_type") == BUILTIN_FILTER:
return (2, 0, 0, name)
return (3, 0, 0, name)
@@ -445,9 +445,9 @@ def annotate_entries_with_stats(
parts: list[str] = []
if links:
name, url = links[0]
# Category-index bullets link into the site itself, and Built-in entries
# Category-index bullets link into the site itself, and stdlib entries
# would hit same-named PyPI backports (e.g. logging) — no counts for either.
if not url.startswith((SITE_URL, "#")) and detect_source_type(url) != "Built-in":
if not url.startswith((SITE_URL, "#")) and detect_source_type(url) != BUILTIN_FILTER:
downloads = downloads_data.get(normalize(name))
if downloads is not None:
parts.append(f"PyPI downloads/month: {downloads}")
@@ -581,15 +581,15 @@ def build(repo_root: Path) -> None:
for entry in entries:
repo_key = extract_github_repo(entry["url"])
if not repo_key and entry.get("source_type") == "Built-in":
if not repo_key and entry.get("source_type") == BUILTIN_FILTER:
repo_key = "python/cpython"
if repo_key and repo_key in stars_data:
sd = stars_data[repo_key]
entry["stars"] = sd["stars"]
entry["owner"] = sd["owner"]
entry["last_commit_at"] = sd.get("last_commit_at", "")
# Built-in entries would hit same-named PyPI backports (e.g. asyncio), not the stdlib.
if entry.get("source_type") != "Built-in":
# Stdlib entries would hit same-named PyPI backports (e.g. asyncio), not the stdlib.
if entry.get("source_type") != BUILTIN_FILTER:
entry["downloads"] = downloads_data.get(normalize(entry["name"]))
entries = sort_entries(entries)
+6 -5
View File
@@ -138,7 +138,7 @@
{% for entry in entries %}
<tr
class="row"
data-tags="{{ entry.categories | join('||') }}{% if entry.subcategories %}||{{ entry.subcategories | map(attribute='value') | join('||') }}{% endif %}||{{ entry.groups | join('||') }}{% if entry.source_type == 'Built-in' %}||Built-in{% endif %}"
data-tags="{{ entry.categories | join('||') }}{% if entry.subcategories %}||{{ entry.subcategories | map(attribute='value') | join('||') }}{% endif %}||{{ entry.groups | join('||') }}{% if entry.source_type == 'Stdlib' %}||Stdlib{% endif %}"
tabindex="0"
aria-expanded="false"
aria-controls="expand-{{ loop.index }}"
@@ -155,7 +155,8 @@
</td>
<td class="col-downloads">
{% if entry.downloads is not none %}{{
"{:,}".format(entry.downloads) }}{% else %}<span
"{:,}".format(entry.downloads) }}{% elif entry.source_type ==
'Stdlib' %}<span class="source-badge">Stdlib</span>{% else %}<span
class="source-badge"
>Not on PyPI</span
>{% endif %}
@@ -207,14 +208,14 @@
{{ entry.groups[0] }}
</a>
{% endif %}
{% if entry.source_type == 'Built-in' %}
{% if entry.source_type == 'Stdlib' %}
<a
class="tag tag-source{% if '/categories/built-in/' == current_path %} active{% endif %}"
href="/categories/built-in/"
data-value="Built-in"
data-value="Stdlib"
data-url="/categories/built-in/"
>
Built-in
Stdlib
</a>
{% endif %}
</td>
+6 -5
View File
@@ -175,7 +175,7 @@
{% for entry in entries %}
<tr
class="row"
data-tags="{{ entry.categories | join('||') }}{% if entry.subcategories %}||{{ entry.subcategories | map(attribute='value') | join('||') }}{% endif %}||{{ entry.groups | join('||') }}{% if entry.source_type == 'Built-in' %}||Built-in{% endif %}"
data-tags="{{ entry.categories | join('||') }}{% if entry.subcategories %}||{{ entry.subcategories | map(attribute='value') | join('||') }}{% endif %}||{{ entry.groups | join('||') }}{% if entry.source_type == 'Stdlib' %}||Stdlib{% endif %}"
tabindex="0"
aria-expanded="false"
aria-controls="expand-{{ loop.index }}"
@@ -192,7 +192,8 @@
</td>
<td class="col-downloads">
{% if entry.downloads is not none %}{{
"{:,}".format(entry.downloads) }}{% else %}<span
"{:,}".format(entry.downloads) }}{% elif entry.source_type ==
'Stdlib' %}<span class="source-badge">Stdlib</span>{% else %}<span
class="source-badge"
>Not on PyPI</span
>{% endif %}
@@ -240,14 +241,14 @@
>
{{ entry.groups[0] }}
</a>
{% if entry.source_type == 'Built-in' %}
{% if entry.source_type == 'Stdlib' %}
<a
class="tag tag-source"
href="/categories/built-in/"
data-value="Built-in"
data-value="Stdlib"
data-url="/categories/built-in/"
>
Built-in
Stdlib
</a>
{% endif %}
</td>
+5 -5
View File
@@ -445,7 +445,7 @@ class TestBuild:
- [My-Lib](https://github.com/org/mylib) - On PyPI.
- [no-pypi](https://example.com/none) - Not on PyPI.
- [asyncio](https://docs.python.org/3/library/asyncio.html) - Built-in.
- [asyncio](https://docs.python.org/3/library/asyncio.html) - Stdlib.
# Contributing
@@ -466,7 +466,7 @@ class TestBuild:
html = (tmp_path / "website" / "output" / "index.html").read_text(encoding="utf-8")
assert "1,234,567" in html
# Built-in entries never show PyPI counts: the asyncio row is the backport package
# Stdlib entries never show PyPI counts: the asyncio row is the backport package
assert "26,305,454" not in html
# Default sort: entries with download counts come first
assert html.index("My-Lib") < html.index("no-pypi")
@@ -1060,7 +1060,7 @@ class TestSortEntries:
def test_builtin_between_starred_and_unstarred(self):
entries = [
_template_entry("builtin", None, "Built-in"),
_template_entry("builtin", None, "Stdlib"),
_template_entry("starred", 100),
_template_entry("unstarred", None),
]
@@ -1078,7 +1078,7 @@ class TestDetectSourceType:
assert detect_source_type("https://github.com/psf/requests") is None
def test_stdlib_url(self):
assert detect_source_type("https://docs.python.org/3/library/asyncio.html") == "Built-in"
assert detect_source_type("https://docs.python.org/3/library/asyncio.html") == "Stdlib"
def test_gitlab_url(self):
assert detect_source_type("https://gitlab.com/org/repo") == "GitLab"
@@ -1167,7 +1167,7 @@ class TestExtractEntries:
groups = parse_readme(readme)
categories = [c for g in groups for c in g["categories"]]
entries = extract_entries(categories, groups)
assert entries[0]["source_type"] == "Built-in"
assert entries[0]["source_type"] == "Stdlib"
def test_subcategory_includes_slug_and_url(self):
readme = textwrap.dedent("""\