Package: poetry-version-plugin
Poetry Version PluginAPoetryplugin for dynamically extracting the packageversion.It can read the version from a file__init__.pywith:# __init__.py__version__="0.1.0"Alternatively, it can read it from agit tag, set with a GitHub release or with:$gittag0.1.0🚨 Consider this in the alpha stage. Read the warning below.When to useThis is useful mainly if you are building a package library for others to use and you want to set the version in a place different thanpyproject.toml, but you still want to keep asingle source of truth.It won't be helpful in other use cases like managing local app environments with Poetry.How to useMake sure you have Poetry version1.2.0or above. Read below for instructions to install it if you haven't.Install Poetry Version PluginInstall this plugin to your Poetry:$poetrypluginaddpoetry-version-plugin--> 100%Set version in init fileSet your package version in your file__init__.py, for example:from.mainimportdo_awesome_stuff,AwesomeClass__version__="0.2.3"And then edit yourpyproject.tomlwith a section containing:[tool.poetry-version-plugin]source="init"Next, build your project. It will show an output like:$poetrybuildUsing __init__.py file at my_awesome_package/__init__.py for dynamic versionSetting package dynamic version to __version__ variable from __init__.py: 0.1.9Building my-awesome-package (0.1.9)- Building sdist- Built my-awesome-package-0.1.9.tar.gz- Building wheel- Built my-awesome-package-0.1.9-py3-none-any.whlSet the version in a Git tagAlternatively, to extract the version to use from a Git tag, add a section:[tool.poetry-version-plugin]source="git-tag"Then create a git tag, for example:$gittag0.1.3In this case, when building your project, it will show an output like:$poetrybuildGit tag found, setting dynamic version to: 0.1.3Building my-awesome-package (0.1.3)- Building sdist- Built my-awesome-package-0.1.3.tar.gz- Building wheel- Built my-awesome-package-0.1.3-py3-none-any.whlVersion inpyproject.tomlCurrently (2021-05-24) Poetry requires aversionconfiguration in thepyproject.toml, even if you use this plugin.When using this plugin, thatversionconfig won't be used, but Poetry still requires it to be present in thepyproject.toml.To make it more evident that you are not using thatversionyou can set it to0.[tool.poetry]name="my-awesome-package"version="0"That way, you will more easily notice if the plugin is not installed, as it will show that you are building a package with version0instead of the dynamic version set.An examplepyproject.tomlA short, minimal examplepyproject.tomlcould look like:[tool.poetry]name="my-awesome-package"version="0"description=""authors=["Rick Sanchez <rick@rick-citadel.com>"]readme="README.md"[tool.poetry.dependencies]python="^3.7"[build-system]requires=["poetry-core"]build-backend="poetry.core.masonry.api"[tool.poetry-version-plugin]source="init"WhyBy default, Poetry expects you to set your package version inpyproject.toml. And that would work in most cases.But imagine you want to expose the version of your package in a__version__variable so that your users can do things like:importmy_awesome_packageprint(my_awesome_package.__version__)You could manually write the__version__variable and handle the synchronization between it and thepyproject.tomlyourself, which is veryerror-prone.The currentofficial way of doing it without duplicating the valueis withimportlib.metadata.But that module is only available in Python 3.8 and above. So, for Python 3.7 and 3.6 you have to install a backport as a dependency of your package:[tool.poetry.dependencies]importlib-metadata={version="^1.0",python="<3.8"}But then, when they release each new version of the backport (currently4.0.1), you have to update it (or not). And your users would have to manually handle conflicts with any other packages that also depend onimportlib-metadatawhich could be multiple, as many packages could be doing the same trick (I've dealt with that).The other option is not to pin any version range of yourimportlib-metadatain yourpyproject.tomland hope for the best.And then your__init__.pywould have to include code using it, like:# I don't want this extra complexity 😔# And it doesn't work in Docker 🐋try:importimportlib.metadataasimportlib_metadataexceptModuleNotFoundError:importimportlib_metadata__version__=importlib_metadata.version(__name__)But that code is extra complexity and logic needed in your code, in each of your packages.🚨 Additionally, this only works when your package is installed in a Python environment. It won't work if, for example, you simply put your code in acontainer, which is common for web apps and distributed systems.How this plugin solves itWith this plugin, your package doesn't depend onimportlib-metadata, so your users won't need to handle conflicts or extra dependencies.Instead, your build system (Poetry) is what needs to have this plugin installed.That avoids the extra code complexity on your side, dependency conflicts for your users, and support for other use cases like code copied directly inside a container.Version from Git tagAlternatively, this plugin can also extract the version from a Git tag.So, you could only create each version in a Git tag (for example, a GitHub release) instead of writing it in code.And then build the package on Continuous Integration (e.g. GitHub Actions). And this plugin would get the version of the package from that Git tag.Install Poetry1.2.0For this plugin to work, you need Poetry version1.2.0or above.Poetry1.2.0was released recently.There's a high chance you already have installed Poetry1.1.x.The first step is to uninstall it:$curl-sSLhttps://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py-O--> 100%$pythonget-poetry.py--uninstall--> 100%And then install the new Poetry with the new installer:$curl-sSLhttps://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py-O--> 100%$pythoninstall-poetry.py--preview--> 100%🔍 Notice that the new installer file is namedinstall-poetry.pyinstead ofget-poetry.py. Also, notice that, currently, you need to set--previewfor it to install the alpha version1.2.0.You can check that it worked with:$poetry--versionPoetry (version 1.2.0)Support for version in init fileWhen using a__version__variable in your__init__.pyyou can have more logic in that file, import modules, and do more things above and below the declaration of that variable.But the value has to be a literal string, like:___version___="0.2.0"...instead of calling a function or something similar.And the variable has to be at the top-level, so it can't be inside anifstatement or similar.This is all fine and supported in your__init__.py:# __init__.py# This is all valid 👍✅from.mainimportdo_awesome_stuff,AwesomeClassawesome=AwesomeClass()# Some comment explaining why this is commented out# __version__ = "1.0.0"__version__="0.2.3"if__name__=="__main__":awesome.run()This example is all valid and supported, and it includes:ImportsOther objects and variablesCommentsThe same string__version__inside a commentIf blocks aroundBut this is not supported:# 🚨 Not supportedif2==2:__version__="0.1.0And this is not supported:# 🚨 Not supporteddefget_version():return"0.2.0"__version__=get_version()How the plugin worksPoetry runs the plugin when building a package, and it sets the version right before creating the "package distributable" (e.g., the wheel).How the version variable worksIf you have a package (a single package) declared in thepackagesconfig in yourpyproject.toml, the plugin will use that package's__init__.pyto find the__version__variable.If you don't have anypackagesconfig, the plugin will assume that you have a single package named as your project, but in the module version (changing-for_). So, if your package ismy-awesome-project, the plugin will use the file atmy_awesome_project/__init__.pyto find the__version__variable.This file structure is the default if you create a new project with the commandpoetry new, so it should work as expected. ✨The way the plugin works internally is by parsing the__init__.pyfile. Reading the Python's "Abstract Syntax Tree" using theaststandard module and extracting the literal value of the string. So, it doesn't execute the code in__init__.py, it only reads it as Python code.The plugin doesn't try to import and execute that__init__.pyfile because that could require extra computation, external dependencies, etc. And it doesn't try to extract the__version__with regular expressions, as that would be prone to errors if, for example, there was some other__version__somewhere in the code, in a comment or inside a string.Warning🚨 Consider this in the alpha stage. Poetry1.2.0a1with support for plugins was released on 2021-05-21. I started writing this plugin 3 days later, on 2021-05-24.Things might break in Poetry or in this plugin. So, please try it and test it very carefully before fully adopting it for delicate systems.The way it works might change, and the specific configuration might change.Also, if you don't find the following sections intuitive:[tool.poetry-version-plugin]source="init"and[tool.poetry-version-plugin]source="git-tag"let me know what alternative configuration would make more sense and be more intuitive to you.👍 The good news is, assuming you are building packages to then upload them to PyPI for your users to download and use them, theworst that could happenif something broke is that you wouldn't be able to build a new version until something is fixed or changed. But your users shouldn't be affected in any way.Release NotesLatest Changes0.2.0✨ Add support for Poetry 1.2.0 and above (including 1.5.1), deprecate support for Python 3.6. PR#28by@mbeacom.✏️ Fix typos and rewording in README.md. PR#8by@Gl0deanR.0.1.3✨ Improve logs, prefix with plugin name. PR#6by@tiangolo.🔧 Update pyproject metadata. PR#5by@tiangolo.✅ Fix coverage. PR#4by@tiangolo.📝 Improve docs. PR#3by@tiangolo.🐛 Fix tests for CI. PR#1by@tiangolo.👷 Fix Latest Changes action, set branch to main. PR#2by@tiangolo.LicenseThis project is licensed under the terms of the MIT license.