Pyroma itself is now built by the native uv_build backend. Its flat
pyroma module and source-distribution extras are declared directly in
pyproject.toml; the Setuptools-only root configuration and MANIFEST.in
are gone. Setuptools-based test fixtures remain for compatibility coverage.
Hypothesis now fuzzes bounded metadata dictionaries and verifies that rating
is deterministic, non-mutating and structurally valid. This found and fixed
a crash on malformed single-component Python classifiers.
The package now passes Ruff's full stable rule set. Ruff is pinned for
reproducible CI, formatter-incompatible rules are explicitly disabled, and
narrow per-file exceptions preserve the unittest suite, public CLI behavior,
and compatibility APIs.
CI now runs Pyroma on itself at a required 10/10, enforces Lizard's strict
complexity threshold, and checks dependency declarations with deptry.
GitHub releases build distributions in an unprivileged job and publish the
resulting artifact to the pyroma621 PyPI project through Trusted
Publishing.
Rating tests are now stateless: each test returns a TestResult
instead of mutating shared test instances, and the new
ratings.rate_project() returns a structured RatedProject
with per-test problems. ratings.rate() keeps its old
(rating, messages) return value.
Added a --format json option for machine-readable output.
Program output no longer goes through the root logger, and
--quiet simply prints the rating.
pyroma no longer depends on setuptools or distutils at runtime.
pyproject.toml validation now uses validate-pyproject, and
pyroma's own metadata lives in pyproject.toml.
setup.cfg-only projects (no setup.py, no pyproject.toml) are now
rated 1 on their build-system problems alone, since no standard tool
can build them and no metadata can be extracted.
New and stricter checks based on the packaging specifications:
version numbers are validated with packaging.version (warning
about non-canonical forms, epochs and local version segments),
Metadata-Version must be a legal value, project names must match
the packaging name format (fatal), License-Expression must be
valid SPDX (fatal, as is combining it with License),
Description-Content-Type and every Requires-Dist entry are
validated, the pyproject.toml [project] table MUST-rules are
enforced (fatal), and Project-URL labels are checked against the
well-known labels (with the 32 character limit fatal).
Official support for the uv_build backend, and backend-neutral
advice when pyproject.toml is missing.
PyPI requests now use timeouts and clear error messages, and
downloaded sdists are always cleaned up.
All code is now type-annotated and checked with ty and pyrefly; a
py.typed marker ships with the package.
Linting moved from black + flake8 to Ruff; the test suite runs with
a coverage gate.
Exit codes are now documented in the README.
Bug fixes found by review: descriptions with a parameterized
Description-Content-Type (for example text/markdown; charset=UTF-8) are no longer validated as ReStructuredText; the
BusFactor test no longer passes for packages with zero owners;
--skip-tests names are matched exactly instead of by substring,
and trailing separators no longer reject the whole list; .tb2
archives are recognized; tar archives are extracted with the "data"
filter, which also protects against symlink attacks.
More robust index handling: XML-RPC faults and network errors while
fetching package roles are now warnings instead of crashes, a failed
sdist download raises a clear error, and index data without a
releases listing no longer crashes. Keys the PyPI JSON API
synthesizes itself (project_url, package_url, release_url
and friends) are no longer folded into the metadata, so they cannot
clobber the real Home-page or hide a missing project URL.
Rating a distribution file or PyPI package now keeps the unpacked
sdist around for the duration of the run, so the pyproject.toml
validation tests run in those modes too. The check-manifest test
still requires a VCS checkout and is skipped for sdists.
A configuration that skips every rated test now raises a
ConfigurationError. The command line reports errors (bad
configuration, package not found, network problems) cleanly on
stderr — or as a JSON error document with --format json — and
exits with the new exit code 3.
Python 3.11 or later is now required; the tomli fallback
dependency is gone.
Ruff is pinned in one place (the lint dependency group) and run
through uv both in CI and in pre-commit. mypy has been replaced by
the ty and pyrefly type checkers, run the same way through the
typecheck dependency group.
Changed the internal metadata names to match Core Metadata. This brings
less confusion, especially since setuptools and PyPI aren't exactly the
same either, so let's pick the offical standard and run with it.
Added a rating for if the wheel fails to build. Unfortunately it's
impossible to get a proper error out of it, so the message tells
you to run python -m build.
Deprecation warnings? For the packaging configuration? What was I thinking?
I'm instead adding a rating telling people that they should add a
pyproject.toml. Much nicer.
Added a --skip-tests parameter to allow skipping of certain tests.
You shouldn't skip tests, of course, but now you can. This is actually
only implemented so that I can add a test using check-manifest and
skip it when run from the zest.releaser hook.
If you also have check-manifest installed, pyroma will run that as a test.
No longer do you need to call it separately! However, if Pyroma is invoked
from zest.releaser, it will not be run, because check-manifest has a separate
hook for zest.releaser, so that would run it twice.
Added support for PEP517 by using build to build the metadata as a
preferred build method over the old patched setuptools method.
The old way is depracated and will be removed in 5.0, which also is
planned to support PEP621.
Thanks to Christopher A.M. Gerlach for valuable insight into the
modern packaging systems.
Rewrote parts of the PyPI support, to avoid using the xmlrpc API,
since it's being heavily rate-limited at the moment. Only one call
is using it now, which seems to work fine.
Backwards incompatible change: As a result of the above change, when
looking for packages on PyPI, the package name must now be spelled
exactly correct, including capitalization.
Some more code cleanup/modernization [CAM Gerlach]
Added --quiet option to output only the rating [Hugo van Kemenade]
Pyroma is now an executable module, and can be called with python -m pyroma [RuRo]
Fixed issue #17: Integration with zest.releaser stopped working.
Fixed issue #18: Pyroma returns the rating as an exit code, this was a
mistake. It now returns 0 on success, 1 on incorrect options and 2 if
the rating is below 8.
Fixed issue #19: Implementing a custom test class counts as having tests.
8: Philadelphia is now considered a "success" based on practical experience.
Issue #11: pyroma would fail if long_description was a non-string.
zest.releaser now only runs Pyroma on Python packages.
Because packages that use external test-runners can not get more
that 9/10, this value is now also seen as acceptable when running
Pyroma through zest.releaser.
Removed the running of tests. I always found it a bit iffy, and getting
rid of it solved a lot of issues. Pyroma is now focused solely on
packaging quality.
A package on PyPI with several versions will no longer return an error.
Now looks for documentation on pythonhosted.org or readthedocs.org.
Adds a hook for zest.releaser, so it can be run after doing the checkout,
before uploading to PyPI.