From 2aa87f880bfaae2e27f1e4b2a67a8fa9d961ab0a Mon Sep 17 00:00:00 2001 From: Vinta Chen Date: Fri, 2 Oct 2026 14:43:32 +0800 Subject: [PATCH] fix: drop doubled noun from category page titles Category names ending in a plural noun (DevOps Tools, Web Frameworks, HTTP Clients, Stdlib) got titles like "Python DevOps Tools Libraries" in title, og:title, twitter:title, and JSON-LD name. Co-Authored-By: Claude --- website/build.py | 5 ++++- website/tests/test_build.py | 39 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/website/build.py b/website/build.py index 0f299340..11a6e706 100644 --- a/website/build.py +++ b/website/build.py @@ -24,6 +24,7 @@ ANCHOR_LINK_ATTRS_RE = re.compile(r'href="(#[^"]*)" target="_blank" rel="noopene SITE_URL = "https://awesome-python.com/" SITEMAP_URL = f"{SITE_URL}sitemap.xml" SITEMAP_NS = "http://www.sitemaps.org/schemas/sitemap/0.9" +PLURAL_NOUNS = {"Clients", "Drivers", "Engines", "Files", "Frameworks", "Generators", "Implementations", "Panels", "Queues", "Repositories", "Schedulers", "Servers", "Stdlib", "Tools"} BUILTIN_FILTER = "Stdlib" BUILTIN_SLUG = "built-in" @@ -230,7 +231,9 @@ def category_meta_title(name: str, parent_name: str | None = None) -> str: if len(title) <= 60: return title return f"{name} - Awesome Python" - title = f"Python {name} Libraries - Awesome Python" + # Names ending in one of these nouns already say what the entries are. + noun = "" if name.rsplit(" ", 1)[-1] in PLURAL_NOUNS else " Libraries" + title = f"Python {name}{noun} - Awesome Python" if len(title) <= 60: return title return f"{name} - Awesome Python" diff --git a/website/tests/test_build.py b/website/tests/test_build.py index 42a44e0c..b1592450 100644 --- a/website/tests/test_build.py +++ b/website/tests/test_build.py @@ -745,6 +745,45 @@ class TestBuild: assert collection["url"] == "https://awesome-python.com/categories/ai-ml/" assert collection["description"] == "Explore 1 curated Python projects in AI & ML. Part of the Awesome Python catalog." + def test_category_title_skips_libraries_after_plural_noun(self, tmp_path): + readme = textwrap.dedent("""\ + # T + + ## Projects + + **Web Development** + + ## Web Frameworks + + - [wf1](https://example.com/wf1) - WF. + + ## Web APIs + + - [api1](https://example.com/api1) - API. + + # Contributing + + Done. + """) + self._copy_real_templates(tmp_path) + (tmp_path / "README.md").write_text(readme, encoding="utf-8") + build(tmp_path) + + categories_dir = tmp_path / "website" / "output" / "categories" + frameworks_html = (categories_dir / "web-frameworks" / "index.html").read_text(encoding="utf-8") + apis_html = (categories_dir / "web-apis" / "index.html").read_text(encoding="utf-8") + parser = HeadMetadataParser() + parser.feed(frameworks_html) + marker = '", start) + graph = {node["@type"]: node for node in json.loads(frameworks_html[start:end])["@graph"]} + + assert parser.title.strip() == "Python Web Frameworks - Awesome Python" + assert parser.meta_by_property["og:title"] == "Python Web Frameworks - Awesome Python" + assert graph["CollectionPage"]["name"] == "Python Web Frameworks" + assert "Python Web APIs Libraries - Awesome Python" in apis_html + def test_build_creates_subcategory_pages(self, tmp_path): readme = textwrap.dedent("""\ # T