The Geolocation category page had no intro, so its meta description fell back to generic text and readers got no guidance on which library to pick.
Co-Authored-By: Claude <noreply@anthropic.com>
The Email category page had no intro, so its meta description fell back to generic text and readers got no guidance on which library to pick.
Co-Authored-By: Claude <noreply@anthropic.com>
The Functional Programming category page had no intro, so its meta description fell back to generic text and readers got no guidance on which library to pick.
Co-Authored-By: Claude <noreply@anthropic.com>
The Asynchronous Programming category page had no intro, so its meta description fell back to generic text and readers got no guidance on which library to pick.
Co-Authored-By: Claude <noreply@anthropic.com>
Static Site Generators had no intro, so its meta description fell back to generic text and readers got no guidance on which library to pick.
Co-Authored-By: Claude <noreply@anthropic.com>
Database Drivers had no intro, so its meta description fell back to generic text and readers got no guidance on which library to pick.
Co-Authored-By: Claude <noreply@anthropic.com>
Cryptography had no intro, so its meta description fell back to generic text and readers got no guidance on which library to pick.
Co-Authored-By: Claude <noreply@anthropic.com>
Task Queues had no intro, so its meta description fell back to generic text and readers got no guidance on which library to pick.
Co-Authored-By: Claude <noreply@anthropic.com>
Distributed Computing had no intro, so its meta description fell back to generic text and readers got no guidance on which tool to pick.
Co-Authored-By: Claude <noreply@anthropic.com>
Package Management had no intro, so its meta description fell back to generic text and readers got no guidance on which tool to pick.
Co-Authored-By: Claude <noreply@anthropic.com>
Admin Panels had no intro, so its meta description fell back to generic text and readers got no guidance on which tool to pick.
Co-Authored-By: Claude <noreply@anthropic.com>
Nothing in the project instructions said entry checks stay read-only, and an agent once proposed installing a listed project to test whether it imports, which runs untrusted code.
Co-Authored-By: Claude <noreply@anthropic.com>
Agents editing the website had no written guide to the site's layout model, width caps, font sizing, or peer-element consistency, so those conventions weren't applied to new changes.
Co-Authored-By: Claude <noreply@anthropic.com>
The page had no intro, so readers got no pick among Channels, Flask-SocketIO, websockets, and Autobahn|Python.
Co-Authored-By: Claude <noreply@anthropic.com>
The Web Servers page had no intro, so readers got no pick among Gunicorn, Uvicorn, Waitress, Granian, and Hypercorn.
Co-Authored-By: Claude <noreply@anthropic.com>
The Web APIs category page had no intro, leaving readers without a pick among Django REST framework, Django Ninja, FastAPI, and the GraphQL and RPC libraries.
Co-Authored-By: Claude <noreply@anthropic.com>
The page had no intro, so readers got no pick among Django, Flask, and the other full-stack and async frameworks.
Co-Authored-By: Claude <noreply@anthropic.com>
The page had no intro, so readers got no pick among spaCy, NLTK, Stanza, and the Chinese text libraries.
Co-Authored-By: Claude <noreply@anthropic.com>
The page had no intro, so readers got no pick among scikit-learn, the boosting libraries, pgmpy, Feature-engine, and TimesFM.
Co-Authored-By: Claude <noreply@anthropic.com>
The Deep Learning page had no intro, leaving readers with no pick among PyTorch, Keras, JAX, Lightning, and the reinforcement learning libraries.
Co-Authored-By: Claude <noreply@anthropic.com>
The page had no guidance on which Python game library to pick. Recommends pygame-ce over upstream pygame, which fails to install on Python 3.14 (no cp314 wheel, open issues #4627/#4810).
Co-Authored-By: Claude <noreply@anthropic.com>
The web scraping category page had no intro, leaving readers without guidance for picking among crawling frameworks, LLM-ready crawlers, AI browser agents, and content extractors.
Co-Authored-By: Claude <noreply@anthropic.com>
The page had no intro, so readers got no pick among the encoding detection, fuzzy matching, parser, slug, and ID libraries.
Co-Authored-By: Claude <noreply@anthropic.com>
The category page had no intro, giving readers no guidance for picking among the PDF, Office, Markdown, and config libraries listed.
Co-Authored-By: Claude <noreply@anthropic.com>
On section and subcategory pages, every row repeated its use case and current page as tags (and as the label under the name on phones), right under a heading that already said the same thing. They are now hidden in editorial order and shown again when a column sort flattens the groups.
Co-Authored-By: Claude <noreply@anthropic.com>
After sorting by a column, or while a search hid a group, category page group headings stayed hidden, so the header's jump links (and any use-case heading link) pointed at a hidden element and did nothing.
Co-Authored-By: Claude <noreply@anthropic.com>
With tags now pointing at section anchors instead of filtering in place, subcategory pages (74 clicks and 19.7K impressions in the last 3 months) had no other internal links pointing to them.
Co-Authored-By: Claude <noreply@anthropic.com>
Clicking a category or group tag filtered the homepage table in place while rewriting the address bar to the category URL, so one URL rendered two different pages and readers never reached category pages with their intros and guides; category pages also showed a redundant self-referential filter bar.
Co-Authored-By: Claude <noreply@anthropic.com>
Covers Pydantic for API input and config, Pandera for dataframes, and jsonschema for JSON Schema validation.
Co-Authored-By: Claude <noreply@anthropic.com>
Covers pytest as the default, a how-to-choose item per README subcategory, and a guide on Hypothesis, Playwright, tox/Nox, mocks, and coverage.
Co-Authored-By: Claude <noreply@anthropic.com>
Covers librosa for audio analysis, MoviePy for scripted video editing, VidGear for real-time video, Mutagen vs tinytag for tag read/write and licensing, and beets as a CLI tag/organization tool.
Co-Authored-By: Claude <noreply@anthropic.com>
Covers OpenCV as the default, Ultralytics YOLO for detection/segmentation/pose models and its AGPL-3.0/Enterprise License terms, Kornia for GPU-batch vision ops, FiftyOne for dataset curation, and pytesseract vs EasyOCR for OCR.
Co-Authored-By: Claude <noreply@anthropic.com>
Ruff for linting and formatting, a type checker alongside it, and pre-commit to run them, with per-tool guidance sourced from each project's own docs.
Co-Authored-By: Claude <noreply@anthropic.com>
Explains when to pick Wagtail (developer-defined page types) versus
django CMS (editors composing pages live), based on each project's
own documentation.
Co-Authored-By: Claude <noreply@anthropic.com>
Category pages sorted rows by downloads, which buried editorial leads (the Django ORM showed as row 7 of 7, tkinter as 14 of 14) against CONTRIBUTING.md's "position is the marker"; the full intro in the hero pushed the table 2.5 screens down on a phone; and every page repeated the same "Search every project in one place" H2.
Co-Authored-By: Claude <noreply@anthropic.com>
Every entry description row carried aria-hidden="true", hiding descriptions that sighted readers can see from screen readers.
Co-Authored-By: Claude <noreply@anthropic.com>
Category page titles read "ORM Python Libraries", but people search "python orm", so the title word order didn't match the query.
Co-Authored-By: Claude <noreply@anthropic.com>
Personal Claude Code preview config, now holding a local-worktree server with an absolute path, was tracked so local edits showed as repo changes; excluded via .git/info/exclude instead.
Co-Authored-By: Claude <noreply@anthropic.com>
7 of its 13 items carried links while the other intros' lists carry none, and the entry table already links every project.
Co-Authored-By: Claude <noreply@anthropic.com>
The list had 22 items, which in the upcoming layout sits above the entry table and would push it 4-5 phone screens down; it now has one item per README subcategory, in README order, reusing the intro's own wording.
Co-Authored-By: Claude <noreply@anthropic.com>
The old intro told every FastAPI app to use SQLModel, which SQLModel's own docs don't claim beyond simple cases, and leaned on API names from SQLAlchemy's 2.0 rename (Mapped, mapped_column).
Co-Authored-By: Claude <noreply@anthropic.com>
The old intro's lead used the same "For a Python X library, use …" template as other category pages, and it carried version-bound usage tips (Qt Widgets vs Qt Quick, pyside6-uic, per-toolkit threading helpers) instead of each project's recommended setup.
Co-Authored-By: Claude <noreply@anthropic.com>
The AI and Agents category page had no intro, so readers got a 35-project list with no guidance on which library to pick for building an agent, serving a model, or fine-tuning.
Co-Authored-By: Claude <noreply@anthropic.com>
Adds a "website" launch config that runs `make preview` (build, rebuild on file changes, serve website/output), so the desktop app's preview pane can start the site in one step.
Co-Authored-By: Claude <noreply@anthropic.com>
Audits that rename or dissolve a category never added a redirect, which is how the 404s appearing in Search Console traced back to renamed category pages.
Co-Authored-By: Claude <noreply@anthropic.com>
Category pages carried no text of their own beyond the README one-line description, and most meta descriptions fell back to a generic "Explore N curated Python projects" line, which correlated with weak search rankings for category queries. This adds optional per-category intro markdown files rendered under the H1, with the first paragraph used as the meta description and links opening in a new tab, starting with the ORM category.
Co-Authored-By: Claude <noreply@anthropic.com>
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>
PR numbers named in chat during a review-prs run should be clickable so the maintainer can open each PR in the browser while deciding on it. Defines the PR link convention once in the intro and uses it at both chat sites: the closing-comment draft label and the final summary table.
Co-Authored-By: Claude <noreply@anthropic.com>
In the last review-prs run, closing comments were posted to GitHub without the maintainer ever seeing their full text - the AskUserQuestion options carried one-line paraphrases and the actual comment bytes first appeared inside the gh pr close --comment command. The old wording ("AskUserQuestion presenting the draft closing comment") was satisfiable by a summary. The Close arm now prints each draft verbatim in chat (fenced block, PR number as label) before the AskUserQuestion, and the step's completion criterion binds every posted comment to be byte-identical to a printed draft or to the maintainer's custom text.
Co-Authored-By: Claude <noreply@anthropic.com>
The uv-audit bug has no automatic guard outside the bundled-entry
case, so the hazard has to live in the audit process instead. The
sweep only queries names matching PYPI_NAME_RE, so a display name
with a space is skipped outright; renaming it into a PyPI-shaped name
starts it being queried, which is how 'uv audit' became 'uv-audit'
and picked up an unrelated third-party package by rocshers.
The gotcha tells the auditor to fetch PyPI metadata for any renamed
entry and add a null override when the package isn't the linked
project. It also records two broader checks that were measured
against the full list and rejected, so a future audit doesn't spend
time re-proposing them: the repo-backlink check misses this case
entirely since uv-audit declares no repo URL, as do 26 legitimate
entries, and it flags 10 benign org moves; the name-versus-repo-name
check returns 46 hits that are all legitimate, including uv-build
with the shape identical to uv-audit.
Co-Authored-By: Claude <noreply@anthropic.com>
The uv-audit bug had no automated guard: pypi_name_overrides.json is
a manual registry, so a wrong-package mapping is only caught if
someone already suspects it.
Two broader checks were measured against the real list and rejected.
Checking that PyPI metadata links back to the entry's GitHub repo
would not have caught uv-audit, since that package declares no
home_page or project_urls, landing it in a 26-entry bucket of
packages that simply don't declare a repo (numba, selenium, pyglet,
etc.), plus 10 benign cases of orgs moving or splitting bindings.
Flagging display-name/repo-name mismatches yields 46 hits, all
legitimate python-X-repo-to-X-package pairs, with uv-build sitting
among them despite being a real Astral package with the identical
shape to uv-audit.
What discriminates is the bundled marker itself: a "(part of X)"
entry ships inside something else and has no package of its own, so
the sweep must never query it. This test walks the real README and
requires a null override for every bundled entry whose normalized
name is PyPI-shaped. Verified it fails with exactly the uv-audit
message when that override is removed, and passes with it restored,
across the three current bundled entries with no false positives. It
runs offline, fitting the existing network-less CI.
Co-Authored-By: Claude <noreply@anthropic.com>
Renaming the entry from "uv audit" to "uv-audit" made the name PyPI-shaped: normalize() leaves spaces alone, so "uv audit" failed PYPI_NAME_RE and collect_names skipped it, but "uv-audit" passes, so the next sweep would have queried PyPI for it.
A uv-audit package does exist on PyPI, but it is version 0.1.9 by Alekse Marusich of rocshers, an unrelated third-party tool whose summary ("uv Tool for checking dependencies for vulnerabilities") is close enough to be mistaken for Astral's built-in uv audit subcommand. Without the override the entry would have shown that stranger's download count and lost its Bundled badge.
The sweep now writes uv-audit as NOT_FOUND, which load_downloads skips, so the badge is unaffected.
Co-Authored-By: Claude <noreply@anthropic.com>
Keep the docs link inline in the description instead of as the primary entry link, matching the format used by other entries.
Co-Authored-By: Claude <noreply@anthropic.com>
azure-sdk-for-python and google-cloud-python were rendering "Not on
PyPI", which is misleading. Both do ship on PyPI, just as many
per-service packages (azure-identity, azure-storage-blob,
google-cloud-storage, etc.) rather than under the repo name.
pypi_name_overrides.json already recorded that distinction in its
reason field; those two entries now carry an optional "badge" value
that build.py reads into the PyPI Downloads column. The other sixteen
no-count entries (cpython, renpy, agent skill repos, etc.) keep
"Not on PyPI" since that remains accurate for them.
Co-Authored-By: Claude <noreply@anthropic.com>
django.db.models, geodjango, httpx.URL and uv audit were rendering
"Not on PyPI" alongside eighteen genuinely standalone projects that
simply are not packaged on PyPI, conflating two different reasons for
a missing download count.
These four entries now carry a "(part of X)" description prefix in
README.md, mirroring the existing "(Python standard library)"
convention. build.py reads that prefix into a bundled flag that both
templates render as a "Bundled" badge.
The prefix approach was chosen over a separate data file so README.md
stays the single source of content truth, and over inferring from the
entry name because geodjango is neither dotted nor spaced and would
have been missed. Redundant tail wording was trimmed from the
httpx.URL, geodjango and uv audit descriptions now that the prefix
names the parent.
The new entry format is documented in CONTRIBUTING.md and the
vocabulary in CONTEXT.md.
Co-Authored-By: Claude <noreply@anthropic.com>
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>
Add a Stability Exceptions section to docs/audit-logs.md documenting
the zensical override (admitted pre-1.0 to displace a dying mkdocs
upstream). Add a matching evidence bullet to CLAUDE.md and AGENTS.md
warning that a download count is not automatically independent demand
when one listed entry depends on another, using mkdocs / mkdocs-material
as the worked example. Both facts were discovered during this sitting
and need to persist so the next audit doesn't rediscover them.
Co-Authored-By: Claude <noreply@anthropic.com>
Material for MkDocs went into maintenance mode on 2025-11-05 and its
own team called upstream mkdocs unmaintained since 2024-08 and a
supply chain risk; the mkdocs repo was last pushed 2025-10-20. Since
mkdocs-material hard-depends on mkdocs, mkdocs' download count is
almost entirely mkdocs-material pulling it in (18,360,677/month vs
mkdocs-material's 18,167,775/month, ~1% delta), so dropping the
redundant direct entry costs little. zensical is a clean replacement
with no mkdocs dependency, built by the same Material for MkDocs
team, at 1,585,786 downloads/month and 5,540 stars, pushed
2026-08-21. Added as a challenger, placed last below pdoc, keeping
the section at 5 of 5. Approved via verdict preview.
Co-Authored-By: Claude <noreply@anthropic.com>
Obvious choice for physical units and dimensional analysis in Python: 8,647,557 downloads/month (pepy.tech, 2026-08-23), more than twice astropy (3,713,879) and thirty-six times obspy (237,352), 2,781 stars, created 2012, pushed 2026-08-05. PyPI classifier still reads Beta at v0.25.3, treated as stale given fourteen years of history and download scale (judgment call). Listed under Physics and Engineering per maintainer choice over minting a Units and Quantities subcategory.
Co-Authored-By: Claude <noreply@anthropic.com>
Audit of proposed additions from a YouTube video roundup. Admitted as
a challenger: 761,507 downloads/month (pepy.tech, 2026-08-23) already
outranks the incumbent prospector (497,880), the repo is active
(pushed 2026-08-21, version 7.0.1, Production/Stable) and it has
reached 794 stars since being created in January 2024. It fills a
real gap: ruff's PLR0912 measures cyclomatic complexity while
complexipy measures cognitive complexity, and the two are
complementary. Adoption-trajectory evidence is thin, so the
challenger tier is a judgment call. Placed last in the subcategory
since position marks tier and challengers follow obvious choices.
Co-Authored-By: Claude <noreply@anthropic.com>
Audit of proposed additions from a YouTube video roundup. transitions
has not been pushed since 2025-09-11 and crosses the 12-month activity
line on 2026-09-11, while python-statemachine is actively developed
(pushed 2026-08-17, version 3.2.1 released 2026-08-01) and covers
strictly more (SCXML-compliant statecharts, compound and parallel
states, history, sync and async). Downloads 1,422,332/month vs
transitions 3,137,416/month (pepy.tech, 2026-08-23).
Co-Authored-By: Claude <noreply@anthropic.com>
v7 blocks checking out fork PR heads under pull_request_target and
workflow_run. Neither workflow here uses those triggers, so the only
effect is staying current with the other repos.
Pydantic Services took over stewardship of the stalling httpx under the httpx2 name, Starlette already switched its TestClient, and it hit 144M downloads/month (pepy) within 3 months of first release at Production/Stable v2.12.0. Placed last in the challenger tier behind urllib3 per the downloads-descending ordering rule. Fork format and a rewritten description distinguish it from httpx, whose PyPI summary is identical.
Co-Authored-By: Claude <noreply@anthropic.com>
Project now requires Python >=3.14; the pin fails live (verified during
PR review), and plain uv run / make resolves correctly without it.
Co-Authored-By: Claude <noreply@anthropic.com>
The "most diffs sit on stale bases" claim was a one-time artifact of
the shortlist reform, not a durable fact about future PRs. Reworded to
state that diff context lines show the base the PR was written on,
which may have changed since, while keeping conflict-to-Merge-arm
routing.
Co-Authored-By: Claude <noreply@anthropic.com>
Two live review-prs runs surfaced fixes:
- Merge conflicts move from the screen-out rules to a local-merge path
in the Merge arm; 7/10 PRs conflicted only from the reform's stale
bases, and one such PR had already been merged this way.
- Target use case is now resolved from the current README instead of
stale diff context, since most PR bases no longer match live
sections.
- Judge gets a third verdict: no fitting use case is a structure
question, carried to Act for the maintainer to decide.
- Act's Close step gets a batching and checklist note, and the whole
step gets an explicit completion criterion after a verdict nearly
fell out of the question batches.
- Drop the "claude reviewed" label mechanism entirely: the fetch
filter, the Park arm, and the labeled-open terminal state.
Co-Authored-By: Claude <noreply@anthropic.com>
Add seleniumbase to Testing — Browser Automation as a challenger (2.86M downloads/month via pepy vs selenium 56.9M; admitted by maintainer decision). Entry placed per Entry Ordering, display name set to the canonical PyPI package name.
Superseded by the rewritten .claude/skills/review-prs/SKILL.md
committed just before (9ce2a21); leaving it in place would keep a
stale second copy of the PR-review instructions.
Co-Authored-By: Claude <noreply@anthropic.com>
The old review-pending-prs command carried a stale copy of CONTRIBUTING.md's rejection rules (100-star bar, "too niche") that contradicted the current shortlist model. This skill screens from the diff only, delegates admission judgment to the audit-the-list skill, then acts on GitHub: merge with section reconcile, AskUserQuestion-gated closes.
Co-Authored-By: Claude <noreply@anthropic.com>
Kicker now mirrors the hero kicker ("The definitive list that
answers..."), replacing the old "field guide" line changed in
8b14b4f. Subtitle now matches the current tagline ("An opinionated
guide to the best Python frameworks, libraries, and tools.") on one
line. PNG regenerated from the SVG.
Co-Authored-By: Claude <noreply@anthropic.com>