mirror of
https://github.com/vinta/awesome-python.git
synced 2026-10-02 08:23:10 +08:00
feat: serve redirect stubs for renamed and dissolved category slugs
Renamed or dissolved category slugs (e.g. /categories/web-servers/rpc/, /categories/code-analysis/code-linters/) returned 404, Search Console lists 7 of them, and each audit re-home was dropping the old URL's ranking. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ __pycache__/
|
||||
website/output/
|
||||
website/data/*
|
||||
!website/data/pypi_name_overrides.json
|
||||
!website/data/redirects.json
|
||||
|
||||
# agents
|
||||
.playwright-cli/
|
||||
|
||||
@@ -772,6 +772,18 @@ def build(repo_root: Path) -> None:
|
||||
parent_category=cat_by_slug[cat_slug],
|
||||
)
|
||||
|
||||
redirects_file = website / "data" / "redirects.json"
|
||||
redirects = json.loads(redirects_file.read_text(encoding="utf-8")) if redirects_file.exists() else {}
|
||||
for old_path, new_path in redirects.items():
|
||||
tpl_redirect = env.get_template("redirect.html")
|
||||
stub = site_dir / old_path.strip("/") / "index.html"
|
||||
if stub.exists():
|
||||
raise ValueError(f"redirect source {old_path} is a live page; remove it from redirects.json")
|
||||
if not (site_dir / new_path.strip("/") / "index.html").exists():
|
||||
raise ValueError(f"redirect target {new_path} for {old_path} does not exist")
|
||||
stub.parent.mkdir(parents=True, exist_ok=True)
|
||||
stub.write_text(tpl_redirect.render(target_url=SITE_URL + new_path.lstrip("/")), encoding="utf-8")
|
||||
|
||||
static_src = website / "static"
|
||||
static_dst = site_dir / "static"
|
||||
if static_src.exists():
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"/categories/": "/",
|
||||
"/categories/ai-and-agents/pre-trained-models-and-inference/": "/categories/ai-and-agents/pre-trained-models/",
|
||||
"/categories/cli-tools/productivity-tools/": "/categories/cli-tools/",
|
||||
"/categories/code-analysis/code-linters/": "/categories/code-analysis/linters-and-formatters/",
|
||||
"/categories/data-analysis/financial-data/": "/categories/data-ingestion-etl/financial-data/",
|
||||
"/categories/distributed-computing/batch-processing/": "/categories/distributed-computing/",
|
||||
"/categories/gui-development/terminal/": "/categories/cli-development/tui-frameworks/",
|
||||
"/categories/web-servers/rpc/": "/categories/web-apis/rpc/"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting…</title>
|
||||
<link rel="canonical" href="{{ target_url }}">
|
||||
<script>location="{{ target_url }}"</script>
|
||||
<meta http-equiv="refresh" content="0; url={{ target_url }}">
|
||||
<meta name="robots" content="noindex">
|
||||
<h1>Redirecting…</h1>
|
||||
<a href="{{ target_url }}">Click here if you are not redirected.</a>
|
||||
</html>
|
||||
@@ -955,6 +955,56 @@ class TestBuild:
|
||||
assert 'data-url="/categories/ai-ml/"' in index_html
|
||||
assert 'data-url="/categories/deep-learning/vision/"' in index_html
|
||||
|
||||
_REDIRECT_README = textwrap.dedent("""\
|
||||
# Awesome Python
|
||||
|
||||
Intro.
|
||||
|
||||
## Projects
|
||||
|
||||
**Tools**
|
||||
|
||||
### Widgets
|
||||
|
||||
- [w1](https://example.com/w1) - A widget.
|
||||
|
||||
## Contributing
|
||||
|
||||
Help!
|
||||
""")
|
||||
|
||||
def _write_redirects(self, tmp_path, redirects):
|
||||
data_dir = tmp_path / "website" / "data"
|
||||
data_dir.mkdir(parents=True)
|
||||
(data_dir / "redirects.json").write_text(json.dumps(redirects), encoding="utf-8")
|
||||
|
||||
def test_build_writes_redirect_stub_outside_sitemap(self, tmp_path):
|
||||
self._copy_real_templates(tmp_path)
|
||||
(tmp_path / "README.md").write_text(self._REDIRECT_README, encoding="utf-8")
|
||||
self._write_redirects(tmp_path, {"/categories/old-widgets/": "/categories/widgets/"})
|
||||
build(tmp_path)
|
||||
|
||||
site = tmp_path / "website" / "output"
|
||||
stub = (site / "categories" / "old-widgets" / "index.html").read_text(encoding="utf-8")
|
||||
assert '<link rel="canonical" href="https://awesome-python.com/categories/widgets/">' in stub
|
||||
assert '<meta http-equiv="refresh" content="0; url=https://awesome-python.com/categories/widgets/">' in stub
|
||||
assert '<meta name="robots" content="noindex">' in stub
|
||||
assert "old-widgets" not in (site / "sitemap.xml").read_text(encoding="utf-8")
|
||||
|
||||
def test_build_rejects_redirect_to_missing_page(self, tmp_path):
|
||||
self._copy_real_templates(tmp_path)
|
||||
(tmp_path / "README.md").write_text(self._REDIRECT_README, encoding="utf-8")
|
||||
self._write_redirects(tmp_path, {"/categories/old-widgets/": "/categories/gone/"})
|
||||
with pytest.raises(ValueError, match="does not exist"):
|
||||
build(tmp_path)
|
||||
|
||||
def test_build_rejects_redirect_over_live_page(self, tmp_path):
|
||||
self._copy_real_templates(tmp_path)
|
||||
(tmp_path / "README.md").write_text(self._REDIRECT_README, encoding="utf-8")
|
||||
self._write_redirects(tmp_path, {"/categories/widgets/": "/"})
|
||||
with pytest.raises(ValueError, match="is a live page"):
|
||||
build(tmp_path)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# extract_github_repo
|
||||
|
||||
Reference in New Issue
Block a user