Dataclass for pyproject.toml
[project]metadata with support for [core > metadata] generation
This project does not implement the parsing of pyproject.toml containing
pyproject.toml [project] metadata.
Instead, given a Python data structure representing pyproject.toml [project]
metadata (already parsed), it will validate this input and generate a PEP
643-compliant metadata file (e.g. PKG-INFO).
After
installing pyproject-metadata,
you can use it as a library in your scripts and programs:
from pyproject_metadata import StandardMetadata
parsed_pyproject = {...} # you can use parsers like `tomli` to obtain this dict
metadata = StandardMetadata.from_pyproject(parsed_pyproject, allow_extra_keys=False)
# Same fields as defined in pyproject.toml [project] metadata
print(metadata.entrypoints)
pkg_info = metadata.as_rfc822()
print(str(pkg_info)) # core metadataIf project.license is a string or project.license-files is present, then
METADATA 2.4+ will be used. A user is expected to validate and normalize
metadata.license with an SPDX validation tool, such as the one being added to
packaging. Add something like this (requires packaging 24.2+):
if isinstance(metadata.license, str):
metadata.license = packaging.licenses.canonicalize_license_expression(
metadata.license
)A backend is also expected to copy entries from project.license_files, which
are paths relative to the project directory, into the dist-info/licenses
folder, preserving the original source structure.
Pyproject-metadata supports dynamic metadata. To use it, specify your METADATA
fields in dynamic_metadata. If you want to convert pyproject.toml field
names to METADATA field(s), use
pyproject_metadata.pyproject_to_metadata("field-name"), which will return a
frozenset of metadata names that are touched by that field.
You can add extra fields to the Message returned by to_rfc822(), as long as
they are valid metadata entries.
You can use the all_errors argument to from_pyproject to show all errors in
the metadata parse at once, instead of raising an exception on the first one.
The exception type will be pyproject_metadata.errors.ExceptionGroup (which is
just ExceptionGroup on Python 3.11+).
By default, a warning (pyproject_metadata.errors.ExtraKeyWarning) will be
issued for extra fields at the project table. You can pass allow_extra_keys=
to either avoid the check (True) or hard error (False). If you want to
detect extra keys, you can get them with pyproject_metadata.extra_top_level
and pyproject_metadata.extra_build_system. It is recommended that build
systems only warn on failures with these extra keys.
If you want to validate classifiers, then install the trove_classifiers
library (the canonical source for classifiers), and run:
import trove_classifiers
metadata_classifieres = {
c for c in metadata.classifiers if not c.startswith("Private ::")
}
invalid_classifiers = set(metadata.classifiers) - trove_classifiers.classifiers
# Also the deprecated dict if you want it
dep_names = set(metadata.classifiers) & set(trove_classifiers.deprecated_classifiers)
deprecated_classifiers = {
k: trove_classifiers.deprecated_classifiers[k] for k in dep_names
}If you are writing a build backend, you should not validate classifiers with a
Private :: prefix; these are only restricted for upload to PyPI (such as
Private :: Do Not Upload).
Since classifiers are a moving target, it is probably best for build backends (which may be shipped by third party distributors like Debian or Fedora) to either ignore or have optional classifier validation.