feat: tag sponsor links with UTM parameters

Sponsor links carried no UTM tags, so sponsors saw awesome-python clicks at best as generic referral traffic and could not tell README clicks from website clicks. README sponsor entries now carry utm_source=awesome-python&utm_medium=sponsorship&utm_campaign=sponsor&utm_content=readme, and the website build rewrites utm_content to website for the sponsor band. New sponsor entries in README.md need the same UTM suffix.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Vinta Chen
2026-10-21 09:09:22 +08:00
co-authored by Claude
parent 94f70f8425
commit 69d35b2462
4 changed files with 65 additions and 1 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ An opinionated guide to the best Python frameworks, libraries, and tools.
## **Sponsors**
- [iPulse AI](https://ipulseai.com) - Open Agentic Investment Research Platform. Inspect deep multi-agent market forecasts and stock picks.
- [iPulse AI](https://ipulseai.com?utm_source=awesome-python&utm_medium=sponsorship&utm_campaign=sponsor&utm_content=readme) - Open Agentic Investment Research Platform. Inspect deep multi-agent market forecasts and stock picks.
> **Sponsor slots open**, from $99/month. The **#10 most-starred repo on GitHub**. Put your product in front of Python developers. [Become a sponsor](https://awesome-python.com/sponsorship/).
+2
View File
@@ -50,6 +50,8 @@ Email [sponsorship@awesome-python.com](mailto:sponsorship@awesome-python.com?sub
One upfront payment per term. Setup takes less than 24 hours.
Sponsor links carry UTM tags, so clicks show up in your own analytics as `awesome-python / sponsorship`.
## Previously Sponsored By
- [Warp](https://www.warp.dev/) - The terminal for modern developers.
+13
View File
@@ -11,6 +11,7 @@ from collections.abc import Sequence
from datetime import UTC, datetime
from pathlib import Path
from typing import TypedDict
from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
from jinja2 import Environment, FileSystemLoader
from markdown_it import MarkdownIt
@@ -130,6 +131,16 @@ def extract_github_repo(url: str) -> str | None:
return m.group(1) if m else None
def sponsor_website_url(url: str) -> str:
"""Set utm_content=website so sponsors can tell website clicks from README clicks."""
parts = urlsplit(url)
pairs = parse_qsl(parts.query, keep_blank_values=True)
if not any(k == "utm_content" for k, _ in pairs):
return url
query = [(k, "website" if k == "utm_content" else v) for k, v in pairs]
return urlunsplit(parts._replace(query=urlencode(query)))
def load_stars(path: Path) -> dict[str, dict]:
"""Load star data from JSON. Returns empty dict if file doesn't exist or is corrupt."""
if path.exists():
@@ -749,6 +760,8 @@ def build(repo_root: Path) -> None:
parsed_groups = parse_readme(readme_text)
sponsors = parse_sponsors(readme_text)
for sponsor in sponsors:
sponsor["url"] = sponsor_website_url(sponsor["url"])
categories = [cat for g in parsed_groups for cat in g["categories"]]
cat_slugs = [cat["slug"] for cat in categories]
+49
View File
@@ -26,6 +26,7 @@ from build import (
load_pypi_badges,
load_stars,
sort_entries,
sponsor_website_url,
subcategory_path,
)
from readme_parser import parse_readme, slugify
@@ -458,6 +459,37 @@ class TestBuild:
assert llms_txt != readme
assert "# Contributing" not in llms_txt
def test_homepage_sponsor_link_tags_website_clicks(self, tmp_path):
readme = textwrap.dedent("""\
# Awesome Python
Intro.
## **Sponsors**
- [Sponsor](https://sponsor.example.com/?ref=x&utm_source=awesome-python&utm_content=readme) - Sponsored tool.
## Projects
**Tools**
## Widgets
- [w1](https://example.com) - A widget.
# Contributing
Help!
""")
(tmp_path / "README.md").write_text(readme, encoding="utf-8")
self._copy_real_templates(tmp_path)
build(tmp_path)
index_html = (tmp_path / "website" / "output" / "index.html").read_text(encoding="utf-8")
assert 'href="https://sponsor.example.com/?ref=x&amp;utm_source=awesome-python&amp;utm_content=website"' in index_html
assert "utm_content=readme" not in index_html
def test_build_cleans_stale_output(self, tmp_path):
readme = textwrap.dedent("""\
# T
@@ -1278,6 +1310,23 @@ class TestExtractGithubRepo:
assert extract_github_repo("https://github.com/org") is None
# ---------------------------------------------------------------------------
# sponsor_website_url
# ---------------------------------------------------------------------------
class TestSponsorWebsiteUrl:
def test_replaces_readme_utm_content(self):
url = "https://example.com?utm_source=awesome-python&utm_medium=sponsorship&utm_campaign=sponsor&utm_content=readme"
assert sponsor_website_url(url) == "https://example.com?utm_source=awesome-python&utm_medium=sponsorship&utm_campaign=sponsor&utm_content=website"
def test_keeps_path_and_other_params(self):
assert sponsor_website_url("https://example.com/landing?ref=x&utm_content=readme") == "https://example.com/landing?ref=x&utm_content=website"
def test_leaves_untagged_url_unchanged(self):
assert sponsor_website_url("https://example.com") == "https://example.com"
# ---------------------------------------------------------------------------
# load_stars
# ---------------------------------------------------------------------------