diff --git a/website/fetch_github_stars.py b/website/fetch_github_stars.py index 52d65dc1..a196b31a 100755 --- a/website/fetch_github_stars.py +++ b/website/fetch_github_stars.py @@ -114,13 +114,11 @@ def main() -> None: cache = load_stars(CACHE_FILE) now = datetime.now(UTC) - # Prune entries not in current README pruned = {k: v for k, v in cache.items() if k in current_repos} if len(pruned) < len(cache): print(f"Pruned {len(cache) - len(pruned)} stale cache entries") cache = pruned - # Determine which repos need fetching (missing or stale) max_age = timedelta(hours=CACHE_MAX_AGE_HOURS) to_fetch = [] for repo in sorted(current_repos): @@ -138,7 +136,6 @@ def main() -> None: print("Cache is up to date.") return - # Fetch in batches fetched_count = 0 skipped_repos: list[str] = [] diff --git a/website/readme_parser.py b/website/readme_parser.py index 84887d0f..ffcd434f 100644 --- a/website/readme_parser.py +++ b/website/readme_parser.py @@ -53,8 +53,6 @@ class ParsedSponsor(TypedDict): description: str # inline HTML, properly escaped -# --- Slugify ---------------------------------------------------------------- - _SLUG_NON_ALNUM_RE = re.compile(r"[^a-z0-9\s-]") _SLUG_WHITESPACE_RE = re.compile(r"[\s]+") _SLUG_MULTI_DASH_RE = re.compile(r"-+") @@ -69,9 +67,6 @@ def slugify(name: str) -> str: return slug -# --- Inline renderers ------------------------------------------------------- - - def _render_inline(children: list[SyntaxTreeNode], *, html: bool) -> str: """Render inline AST nodes to HTML or plain text.""" parts: list[str] = [] @@ -112,9 +107,6 @@ def render_inline_text(children: list[SyntaxTreeNode]) -> str: return _render_inline(children, html=False) -# --- AST helpers ------------------------------------------------------------- - - def _heading_text(node: SyntaxTreeNode) -> str: """Extract plain text from a heading node.""" for child in node.children: @@ -148,8 +140,6 @@ def _extract_description_children(nodes: list[SyntaxTreeNode]) -> list[SyntaxTre return [] -# --- Entry extraction -------------------------------------------------------- - _DESC_SEP_RE = re.compile(r"^\s*[-\u2013\u2014]\s*") _SUBCAT_TRAILING_RE = re.compile(r"[\s,\-\u2013\u2014]+(also\s+see\s*)?$", re.IGNORECASE) @@ -217,7 +207,6 @@ def _parse_list_entries( first_link = _find_child(inline, "link") if first_link is None or inline.children[0] is not first_link: - # Subcategory label: take text before the first link, strip trailing separators pre_link = [] for child in inline.children: if child.type == "link": @@ -229,12 +218,10 @@ def _parse_list_entries( entries.extend(_parse_list_entries(nested, subcategory=label)) continue - # Entry with a link name = render_inline_text(first_link.children) url = _href(first_link) desc_html = _extract_description_html(inline, first_link) - # Collect also_see from nested bullet_list also_see: list[AlsoSee] = [] nested = _find_child(list_item, "bullet_list") if nested: @@ -274,9 +261,6 @@ def _parse_section_entries(content_nodes: list[SyntaxTreeNode]) -> list[ParsedEn return entries -# --- Section splitting ------------------------------------------------------- - - def _build_section(name: str, body: list[SyntaxTreeNode]) -> ParsedSection: """Build a ParsedSection from a heading name and its body nodes.""" desc_children = _extract_description_children(body) @@ -454,7 +438,6 @@ def parse_readme(text: str) -> list[ParsedGroup]: root = SyntaxTreeNode(tokens) children = root.children - # Find Projects and section boundaries in one pass. projects_idx = None cat_end_idx = None for i, node in enumerate(children): diff --git a/website/static/main.js b/website/static/main.js index fe091e48..5944dd98 100644 --- a/website/static/main.js +++ b/website/static/main.js @@ -271,10 +271,8 @@ function updateSortIndicators() { }); } -// Expand/collapse: event delegation on tbody if (tbody) { tbody.addEventListener("click", function (e) { - // Don't toggle if clicking a link if (e.target.closest("a")) return; let row = e.target.closest("tr.row"); @@ -287,7 +285,6 @@ if (tbody) { setRowOpen(row, !row.classList.contains("open")); }); - // Keyboard: Enter or Space on focused .row toggles expand tbody.addEventListener("keydown", function (e) { if (e.key !== "Enter" && e.key !== " ") return; if (e.target.closest("a")) return; diff --git a/website/tests/test_build.py b/website/tests/test_build.py index 5b3cd15a..39b91d34 100644 --- a/website/tests/test_build.py +++ b/website/tests/test_build.py @@ -65,11 +65,6 @@ class HeadMetadataParser(HTMLParser): self.title += data -# --------------------------------------------------------------------------- -# slugify -# --------------------------------------------------------------------------- - - class TestSlugify: def test_simple(self): assert slugify("Admin Panels") == "admin-panels" @@ -89,11 +84,6 @@ class TestSubcategoryPath: assert subcategory_path("web-frameworks", "synchronous") == "/categories/web-frameworks/synchronous/" -# --------------------------------------------------------------------------- -# build (integration) -# --------------------------------------------------------------------------- - - class TestBuild: @pytest.fixture(autouse=True) def _make_sponsorship_md(self, tmp_path): @@ -563,12 +553,10 @@ class TestBuild: """) (tmp_path / "README.md").write_text(readme, encoding="utf-8") - # Copy real templates real_tpl = Path(__file__).parent / ".." / "templates" tpl_dir = tmp_path / "website" / "templates" shutil.copytree(real_tpl, tpl_dir) - # Create mock star data data_dir = tmp_path / "website" / "data" data_dir.mkdir(parents=True) stars = { @@ -583,10 +571,8 @@ class TestBuild: # Star-sorted: high-stars (5000) before low-stars (100) before no-stars (None) assert html.index("high-stars") < html.index("low-stars") assert html.index("low-stars") < html.index("no-stars") - # Formatted star counts assert "5,000" in html assert "100" in html - # Expand content present assert "expand-content" in html def test_build_with_downloads_renders_column(self, tmp_path): @@ -1313,11 +1299,6 @@ class TestBuild: build(tmp_path) -# --------------------------------------------------------------------------- -# extract_github_repo -# --------------------------------------------------------------------------- - - class TestExtractGithubRepo: def test_github_url(self): assert extract_github_repo("https://github.com/psf/requests") == "psf/requests" @@ -1341,11 +1322,6 @@ 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" @@ -1358,11 +1334,6 @@ class TestSponsorWebsiteUrl: assert sponsor_website_url("https://example.com") == "https://example.com" -# --------------------------------------------------------------------------- -# load_stars -# --------------------------------------------------------------------------- - - class TestLoadStars: def test_returns_empty_when_missing(self, tmp_path): result = load_stars(tmp_path / "nonexistent.json") @@ -1382,11 +1353,6 @@ class TestLoadStars: assert result == {} -# --------------------------------------------------------------------------- -# sort_entries -# --------------------------------------------------------------------------- - - class TestLoadPypiBadges: def test_only_entries_with_a_badge_are_returned(self): badges = load_pypi_badges() @@ -1460,11 +1426,6 @@ class TestSortEntries: assert [e["name"] for e in result] == ["starred", "builtin", "unstarred"] -# --------------------------------------------------------------------------- -# detect_source_type -# --------------------------------------------------------------------------- - - class TestDetectSourceType: def test_github_repo_returns_none(self): assert detect_source_type("https://github.com/psf/requests") is None @@ -1485,11 +1446,6 @@ class TestDetectSourceType: assert detect_source_type("https://github.com/org/repo/wiki") is None -# --------------------------------------------------------------------------- -# extract_entries -# --------------------------------------------------------------------------- - - class TestExtractEntries: def test_basic_extraction(self): readme = textwrap.dedent("""\ @@ -1592,11 +1548,6 @@ class TestExtractEntries: ] -# --------------------------------------------------------------------------- -# annotate_entries_with_stats -# --------------------------------------------------------------------------- - - class TestAnnotateEntriesWithStats: def test_appends_star_count_to_bullet(self): markdown = "- [foo](https://github.com/owner/foo) - A foo.\n" @@ -1658,11 +1609,6 @@ class TestLoadDownloads: assert load_downloads(tmp_path / "nope.tsv") == {} -# --------------------------------------------------------------------------- -# load_category_intro -# --------------------------------------------------------------------------- - - class TestLoadCategoryIntro: def test_splits_after_how_to_choose_list(self, tmp_path): path = tmp_path / "widgets.md" diff --git a/website/tests/test_fetch_github_stars.py b/website/tests/test_fetch_github_stars.py index 7b694e51..14e888c6 100644 --- a/website/tests/test_fetch_github_stars.py +++ b/website/tests/test_fetch_github_stars.py @@ -166,7 +166,6 @@ class TestMainSkipsFreshCache: from fetch_github_stars import main - # Set up a minimal README with one repo readme = tmp_path / "README.md" readme.write_text("* [req](https://github.com/psf/requests) - HTTP.\n") monkeypatch.setattr("fetch_github_stars.README_PATH", readme) @@ -207,7 +206,6 @@ class TestMainSkipsFreshCache: from fetch_github_stars import main - # Set up a minimal README with one repo readme = tmp_path / "README.md" readme.write_text("* [req](https://github.com/psf/requests) - HTTP.\n") monkeypatch.setattr("fetch_github_stars.README_PATH", readme) @@ -236,7 +234,6 @@ class TestMainSkipsFreshCache: monkeypatch.setattr("fetch_github_stars.DATA_DIR", data_dir) monkeypatch.setenv("GITHUB_TOKEN", "fake-token") - # Mock httpx.Client to avoid real API calls mock_response = MagicMock() mock_response.json.return_value = { "data": { diff --git a/website/tests/test_readme_parser.py b/website/tests/test_readme_parser.py index eb596cde..4e21f3b7 100644 --- a/website/tests/test_readme_parser.py +++ b/website/tests/test_readme_parser.py @@ -529,13 +529,11 @@ class TestParseRealReadme: inline = _find_inline(list_item) if inline is None: continue - # Check if inline text starts with '[' but has no link node has_link = any(c.type == "link" for c in inline.children) text = render_inline_text(inline.children) if not has_link and text.startswith("["): line = list_item.map[0] + 1 if list_item.map else "?" bad.append(f" line {line}: {text}") - # Recurse into nested lists for child in list_item.children: if child.type == "bullet_list": self._check_list_for_broken_links(child, bad)