Dataclass for PEP 621 metadata with support for core metadata generation
This project does not implement the parsing of pyproject.toml
containing PEP
621 metadata.
Instead, given a Python data structure representing PEP 621 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)
print(metadata.entrypoints) # same fields as defined in PEP 621
pkg_info = metadata.as_rfc822()
print(str(pkg_info)) # core metadata
If 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.licence_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.