Bundled plugins¶
This package ships five plugins. regex, template, and substitute are
generic — they read their target from a field setting. static writes values
straight from its settings, and readme_fragment is single-purpose and always
writes readme. Because they live inside dynamic-metadata, you must add
dynamic-metadata to your [build-system].requires to use them.
Each registers a provider name of dynamic_metadata. plus the heading below, so
the regex plugin is provider = "dynamic_metadata.regex". The examples use
these names.
Entries run in order and each sees the project resolved so far, so several
entries can cooperate on one field: readme_fragment and substitute build a
readme the way hatch-fancy-pypi-readme assembles one — one entry per
fragment or substitution rather than a nested list.
regex¶
dynamic_metadata.plugins.regex extracts a value from a file with a regular
expression. By default it pulls a version out of a __version__/VERSION
assignment.
[project]
dynamic = ["version"]
[[tool.dynamic-metadata]]
provider = "dynamic_metadata.regex"
field = "version"
input = "src/my_package/__init__.py"
Settings (all values must be strings):
Setting |
Required |
Description |
|---|---|---|
|
yes |
The metadata field to set. |
|
yes |
The file to read. |
|
unless version |
The pattern to search for. Must capture a |
|
no ( |
A |
|
no |
A regex stripped from the result. |
The search runs in re.MULTILINE mode. When the target field is not a string
field, result is applied across the container shape the field requires (each
string in a list, each value in a table, and so on).
template¶
dynamic_metadata.plugins.template fills a str.format template from fields
resolved by earlier entries, demonstrating cross-field references.
[[tool.dynamic-metadata]]
provider = "dynamic_metadata.template"
field = "readme"
result = "{project[name]} {project[version]}"
Settings:
Setting |
Required |
Description |
|---|---|---|
|
yes |
The metadata field to set. |
|
yes |
A |
Only fields produced by earlier entries (or static values already in
[project]) are available — a forward reference raises a KeyError.
static¶
dynamic_metadata.plugins.static sets fields directly from its own settings —
an alternative to writing them in [project]. Each setting is a metadata field
mapped to its value, returned verbatim.
[project]
dynamic = ["version", "description"]
[[tool.dynamic-metadata]]
provider = "dynamic_metadata.static"
version = "1.2.3"
description = "My package"
Settings: any settable metadata field maps to the value to give it. The fields
must be listed in project.dynamic like every dynamic field, and values use the
same shape they would in [project] — a string for version, a list for
keywords, a table for readme, and so on.
This is mainly useful as the first half of a pipeline: it gives a later entry
like substitute a dynamic value to transform, which a field set in
[project] cannot be (a scalar field may not be both static and dynamic).
[project]
dynamic = ["version"]
[[tool.dynamic-metadata]]
provider = "dynamic_metadata.static"
version = "1.2.3-beta"
[[tool.dynamic-metadata]]
provider = "dynamic_metadata.substitute"
field = "version"
pattern = "-beta$"
replacement = "b0"
It can also keep metadata out of [project], hiding it from tools that read
[project] directly.
readme_fragment¶
dynamic_metadata.plugins.readme_fragment builds a readme from an ordered
series of fragments, each its own entry. Every entry appends to the readme
produced by the entries before it, so a heading, a slice of a file, and a
changelog excerpt can be stitched together. An entry with text is a literal
fragment; an entry with path reads a file and may slice it.
[project]
dynamic = ["readme"]
[[tool.dynamic-metadata]]
provider = "dynamic_metadata.readme_fragment"
content-type = "text/markdown"
text = "# My Project\n\n"
[[tool.dynamic-metadata]]
provider = "dynamic_metadata.readme_fragment"
path = "README.md"
start-after = "<!-- start -->\n"
end-before = "\n<!-- end -->"
[[tool.dynamic-metadata]]
provider = "dynamic_metadata.readme_fragment"
path = "CHANGELOG.md"
pattern = "(## .*?)(?=\n## )"
Settings (all values are strings):
Setting |
Required |
Description |
|---|---|---|
|
one of text/path |
A literal fragment, used verbatim. |
|
one of text/path |
A file to read (UTF-8) as the fragment, optionally sliced by the keys below. |
|
no ( |
The readme content type. Consulted when the first fragment creates the readme. |
|
no |
Drop everything up to and including this marker (file fragments). Excludes |
|
no |
Drop everything before this marker, keeping it (file fragments). Excludes |
|
no |
Keep everything before this marker (file fragments). Excludes |
|
no |
Keep everything through this marker (file fragments). Excludes |
|
no |
A regex searched with |
Slicing is applied in order: start, then end, then pattern. A missing marker
or a non-matching pattern raises a RuntimeError.
substitute¶
dynamic_metadata.plugins.substitute applies a single regex substitution to a
field already produced by an earlier entry, the way fancy-pypi-readme touches up
an assembled readme (for example, turning #123 into a link).
[[tool.dynamic-metadata]]
provider = "dynamic_metadata.substitute"
field = "readme"
pattern = "#(\\d+)"
replacement = "[#\\1](https://github.com/org/repo/issues/\\1)"
Settings:
Setting |
Required |
Description |
|---|---|---|
|
yes |
The field to transform. Must be a scalar field (see below). |
|
yes |
The regex to replace, applied with |
|
yes |
The replacement; backreferences such as |
|
no ( |
Match case-insensitively. |
|
no ( |
Resolve |
With format = true, replacement is run through str.format(project=...)
before substitution, so it can pull in fields produced by earlier entries — the
same {project[...]} syntax as template. Backreferences keep
working alongside it (braces and backslashes don’t collide):
[[tool.dynamic-metadata]]
provider = "dynamic_metadata.substitute"
field = "readme"
pattern = "#(\\d+)"
replacement = "[#\\1](https://github.com/org/repo/v{project[version]}/issues/\\1)"
format = true
It is opt-in because formatting makes { and } special: with format = true
a literal brace in the replacement must be doubled ({{ / }}), as with any
str.format string. Leave it off (the default) to use the replacement verbatim.
field must be a single-value field — a string field (version, description,
requires-python, license) or readme — and must already hold a value from
an earlier entry. List and table fields are rejected: the backend appends a
provider’s contribution to those, so re-emitting a whole transformed value would
duplicate it. For readme the substitution is applied across the table, so
anchor patterns to the body text rather than the content type.
Warning
substitute only works on a dynamic field produced by an earlier entry. A
field set statically in [project] cannot be modified — a scalar field may not
be both static and dynamic (PEP 808), so substituting one is an error.