poetry-dynamic-versioning, version numbers that come from your tags
Plugin for Poetry to enable dynamic versioning based on VCS tags
At a glance
- What is it?
- poetry-dynamic-versioning is a Poetry plugin that reads your version control tags and injects that version into pyproject.toml and your source files while a build runs, then reverts the working tree. It removes the edit-and-commit step from every release, and it does it by writing to your files behind your back for a moment.
- Who is it for?
- poetry-dynamic-versioning suits a Python team that releases from tags and has grown tired of editing a version string in two places before every build, and it suits anyone who wants PEP 517 frontends other than Poetry to produce correctly versioned artifacts.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The tag is the source of truth, the file is a placeholder
The problem this plugin removes is small but recurring. A Python project that releases from tags has a version number in at least two places: the `pyproject.toml` metadata and, often, a `__version__` string in the package source. Both have to be edited and committed before the release, and forgetting either one produces a build that lies about its own version.
poetry-dynamic-versioning inverts that. It is a Python 3.7 and later plugin for Poetry 1.2.0 and later and Poetry Core 1.0.0 and later, and it enables dynamic versioning based on tags in your version control system, powered by Dunamai, the same author's version-detection library. Many different version control systems are supported, including Git and Mercurial, with the full list and the minimum supported version for each on the Dunamai page.
The mechanism has a distinctive property that is worth understanding before you install it. You create a tag in your version control system, for example `git tag v0.1.0 -m "New release"`, and the plugin automatically inserts that version into relevant files, such as pyproject.toml or Python files with a `__version__` line, for the duration of Poetry commands such as `poetry build`, and then reverts the changes afterwards to keep your repository clean.
So the version is genuinely ephemeral. It exists in your working tree while a build runs and is gone afterwards, which is why the plugin can be installed globally while the per-project opt-in stays in your own pyproject.toml.
The stated goal is narrow and honest: to inject a static copy of the version into build artifacts, the sdists and wheels, without the need to manually edit and commit changed files in your repository.
The audience is a Python developer or team that already uses Poetry, already tags releases, and finds the version bookkeeping tiresome.
Two integration paths, one build backend
The plugin does two separate things, and understanding which one you need saves an afternoon of confusion.
The first is a build backend. `poetry-dynamic-versioning` provides a build backend that patches Poetry Core to enable the versioning system in PEP 517 build frontends. In pyproject.toml that looks like this.
[build-system]
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
build-backend = "poetry_dynamic_versioning.backend"The documentation notes that this is a thin wrapper around `poetry.core.masonry.api`. The effect is that any PEP 517 frontend, not only Poetry itself, will produce artifacts with the right version, because the version is resolved during the build rather than read from a static file.
The second is the Poetry CLI plugin, which you get by installing the package with the `plugin` extra, that is `poetry-dynamic-versioning[plugin]`. That integration triggers the versioning in commands like `poetry build`, using the same machinery.
So the backend path is about interoperability with other build tools, and the plugin extra is about convenience inside Poetry. You can use either, or both.
The configuration required in your own project is minimal. Poetry still requires the `tool.poetry.version` field to be present in pyproject.toml, but you are encouraged to use `version = "0.0.0"` as a standard placeholder, and the `project` table declares the version as dynamic.
[project]
dynamic = ["version"]
[tool.poetry]
version = "0.0.0"
[tool.poetry-dynamic-versioning]
enable = trueWith that minimal configuration in place, the plugin automatically takes effect when you run commands such as `poetry build`, updating the version in pyproject.toml and reverting the change when the plugin deactivates.
Installing the plugin, and the deprecated package it replaces
Installation is a two-part affair, and the first part is a warning about a package with a confusingly similar name.
If you have previously installed the deprecated `poetry-dynamic-versioning-plugin` package, be sure to uninstall it before proceeding. That older package relied on a `.pth` import hack, and while it did work with Poetry 1.1.x, the documentation is explicit that this is no longer supported, so you should migrate to the standardized plugin and Poetry 1.2.0 or later. Using an old version, 0.17.1 or earlier, is described as possible for Poetry 1.1.x users, and as a dead end.
For Poetry 2.0.0 and later, the plugin goes into pyproject.toml as a required plugin, and then you run `poetry install`.
[tool.poetry.requires-plugins]
poetry-dynamic-versioning = { version = ">=1.0.0,<2.0.0", extras = ["plugin"] }For older Poetry versions the method depends on how you installed Poetry. In most cases the documentation points you at the self add command, `poetry self add "poetry-dynamic-versioning[plugin]"`, and if you installed Poetry with Pipx, the injection command `pipx inject poetry "poetry-dynamic-versioning[plugin]"` is the one to use.
Then, in your project directory, you run `poetry dynamic-versioning enable` to turn it on, or you add the configuration fields by hand as shown above.
The `enable` setting defaults to false, and the reason given is that the plugin has to be installed globally while this setting is an opt-in per project. That is the right shape for a tool that mutates your files: the global half decides whether the capability exists, the per-project half decides whether it fires.
The plugin exposes a console script named `poetry-dynamic-versioning`, wired to the package's main module, which is what the `enable` command calls.
Tag patterns, metadata and the v prefix problem
The `vcs` option selects the version control system, and it defaults to `any`, which means the plugin checks for whichever supported system it finds. The explicit choices are git, mercurial, darcs, bazaar, subversion, fossil and pijul.
The `pattern` option is where most configuration time goes, and the reason is that tagging conventions differ. It is a regular expression used to find a tag representing a version, and when it is unset, Dunamai's default pattern is used.
The rules for that regex are specific. There must be a capture group named `base` with the main part of the version. Optionally it may contain two more groups named `stage` and `revision` for prereleases, and it may contain a group named `tagged_metadata` used with the `tagged-metadata` option. There may also be a group named `epoch` for the PEP 440 concept. If the `base` group is not included, the value is interpreted as a named preset from the Dunamai `Pattern` class, and the presets listed are `default` and `default-unprefixed`, the latter making the `v` prefix optional.
The `pattern-prefix` option handles the case where your tags are not bare versions at all. It is inserted after the pattern's start anchor, and the documentation gives the example of matching tags like `some-package-v1.2.3` by keeping the default pattern and setting the prefix to `some-package-`.
# Regular expression:
pattern = '(?P<base>\d+\.\d+\.\d+)'
# Named preset:
pattern = "default-unprefixed"The metadata options control what appears alongside the version. `metadata` includes the commit hash in the version, and also a dirty flag if `dirty` is true, and when it is unset, metadata is only included if you are on a commit without a version tag. `tagged-metadata` includes any tagged metadata discovered as the first part of the metadata segment, and `dirty` adds a flag indicating uncommitted changes. All three are ignored when `format` or `format-jinja` is used, which is the escape hatch for teams that need a specific string shape.
The `format` option defines a custom output format with substitutions including `{base}`, `{stage}`, `{revision}`, `{distance}`, `{commit}`, `{dirty}`, `{tagged_metadata}`, `{branch}`, `{branch_escaped}` which omits any non-letter or number characters, `{timestamp}` of the current commit expanding to YYYYmmddHHMMSS as UTC, and `{major}`, `{minor}` and `{patch}` which are the parts of `base` split on the dot. The example given in the documentation is `v{base}+{distance}.{commit}`, which is a PEP 440 local version segment.
What gets rewritten, and what it costs you
The default configuration does more than touch pyproject.toml. The documentation states that it will also update any pre-existing `__version__ = "0.0.0"` and `__version_tuple__ = (0, 0, 0)` placeholders in some files, and you can configure additional substitution patterns and files as needed.
That last part is the operational risk. The plugin edits files in your working tree during the build and reverts them afterwards. On a clean tree this is safe. On a dirty tree, where you have uncommitted work in the same files it wants to rewrite, the behaviour depends on how the revert is implemented, and the documentation does not describe that case. A developer with local edits to a `__version__` line who runs `poetry build` is in exactly that situation.
The second cost is that the version stops being queryable from the repository. If a release-notes script reads the version out of pyproject.toml in a checkout, it now reads `0.0.0`, because the real value only exists during the build and only in the built artifact. The documentation does not mention a command to print the resolved version, which is the kind of small feature that a build pipeline usually ends up wanting.
The third cost is the interaction with the wider packaging toolchain. The plugin declares `dynamic = ["version"]` in the `project` table, and it patches Poetry Core to do it. If you also use tools that inspect the metadata statically, such as a linter that checks version fields, or a build frontend that does not honour the patched backend, you can get a package that installs and reports the wrong version.
None of this makes the plugin a bad idea. It makes it a plugin for a specific way of working, and that working assumption is worth checking before you adopt it across a team.
A small, well-trodden project with a stable release line
The project metadata is modest and gives a clear picture of the scale.
The package is `poetry-dynamic-versioning` at version 1.10.1, authored by Matthew T. Kennerly, licensed MIT, and classified as Development Status :: 5 - Production/Stable with the Environment :: Plugins classifier. The runtime dependencies are small: dunamai at ^1.26.0, tomlkit at 0.4 or later, and jinja2 between 2.11.1 and 4. Poetry itself is an optional dependency pulled in by the `plugin` extra, which matches the two-integration-paths design.
The development dependencies are equally conventional: pytest, pre-commit, black, mypy and ruff, with a black line length of 120 and ruff configured with the same 120 plus the W605 and N rule sets. There is a `tasks.py` at the root, which is the invoke-style task runner, and a `poetry.lock`.
The tree also contains a separate `pyproject.plugin.toml` and a `README.plugin.md`, which is the pattern for shipping a second, smaller package for the plugin-only distribution so that a user who does not need the backend does not pull in extra weight. The `poetry_dynamic_versioning/` directory holds the package, `tests/` the test suite, `docs/` the documentation, and `CHANGELOG.md`, `CONTRIBUTING.md` and `LICENSE` round it out.
The release history is steady rather than fast. v1.10.1 was released on 2026-10-14, v1.10.0 on 2026-02-14 and v1.9.1 on 2025-07-09, and the last push to the repository was on 2026-10-14, the same day as the newest release. So the project is actively moving, with a version line that has been on 1.x for a while and no sign of a breaking release.
The licence is MIT, which for a build-time plugin is the low-friction choice. There is no copyleft obligation that reaches the packages you build, and the plugin's own source can be forked and vendored without obligations beyond keeping the notice.
Editorial conclusion
poetry-dynamic-versioning suits a Python team that releases from tags and has grown tired of editing a version string in two places before every build, and it suits anyone who wants PEP 517 frontends other than Poetry to produce correctly versioned artifacts. It does not suit a project that has no tags at all, since the version comes from the tag rather than from the file, and it does not suit a team that needs the version committed in the repository for release notes tooling to read. Verify first that your tagging convention matches the pattern option, that your tag prefix is handled by pattern-prefix if you use one, and that the version string Poetry produces is valid PEP 440 before you publish it to an index.
Frequently asked questions
How do I install poetry-dynamic-versioning?
For Poetry 2.0.0 and later, add it to pyproject.toml under [tool.poetry.requires-plugins] with the plugin extra and run poetry install. For older Poetry, use poetry self add "poetry-dynamic-versioning[plugin]", or pipx inject poetry "poetry-dynamic-versioning[plugin]" if Poetry was installed with Pipx. Then run poetry dynamic-versioning enable in your project.
Does poetry-dynamic-versioning work with build tools other than Poetry?
Yes, through the build backend path. The plugin provides a build backend that patches Poetry Core, so any PEP 517 frontend can build your project, configured with build-backend = "poetry_dynamic_versioning.backend" in the build-system section.
How do I make it work with tags that do not start with v?
You can set the pattern option to a regular expression with a capture group named base, or use the named preset default-unprefixed, which makes the v prefix optional. For tags like some-package-v1.2.3, the pattern-prefix option inserts a prefix after the pattern's start anchor.
Does the plugin leave my repository modified after a build?
No. The plugin injects the version into relevant files for the duration of commands such as poetry build and then reverts the changes afterwards to keep the repository clean. The version it writes into pyproject.toml is a placeholder that Poetry requires, and 0.0.0 is the recommended value.
Which version control systems can poetry-dynamic-versioning read tags from?
The vcs option accepts any, git, mercurial, darcs, bazaar, subversion, fossil and pijul, and it defaults to any, which checks for whichever supported system is present. The full list with minimum supported versions is on the Dunamai page.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mtkennerly-poetry-dynamic-versioning)Was this useful?