poetry: peotry version doesn't bump the value in __version__

$ poetry version 0.1.1
Bumping version from 0.1.0 to 0.1.1
$ grep version pyproject.toml 
version = "0.1.1"
$ grep version */__init__.py
src/__init__.py:__version__ = '0.1.0'
$ poetry --version
Poetry 0.9.0

I don’t know if it’s intended or not. A way to do that safely is to parse the root __init__.py, detect __version__, back it up, extract the ast, bump the version string and replace it, then extract the new ast and compare the result. If both ast are the same, except for the version, the file semantics have been preserved. Otherwise, rollback the change and display an error message stating we can’t bump the version safely.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 50
  • Comments: 75 (18 by maintainers)

Commits related to this issue

Most upvoted comments

I really wish that the version field in the .toml ended up superseding the __version__ string in __init__.py. This would reduce the burden of having to keep multiple places in the code in sync.

you may use that

import toml
from pathlib import Path

def get_version():
   path = Path(__file__).resolve().parents[1] / 'pyproject.toml'
   pyproject = toml.loads(open(str(path)).read())
   return pyproject['tool']['poetry']['version']

__version__ = get_version()

Closing this as importlib.metadata is the way to go. A possible pattern to support python ❤️.8 and >=3.8 I described here.

If this is the recommended solution, then poetry new should be updated to use it. Currently, the poetry new command adds __version__ to __init__.py, but then poetry version doesn’t update it. This incompatibility between the two commands is confusing for me, and I imagine other newcomers.

Currently workaround can be to use bump2version package (maintained fork of bumpversion).

For my package poetry_demo with default poetry_demo/__init__.py and tests/test_poetry_demo.py content I have following .bumpversion.cfg file (use of setup.cfg file name is also possible):

[bumpversion]
current_version = 0.1.0
commit = True
tag = True

[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"

[bumpversion:file:poetry_demo/__init__.py]
search = __version__ = '{current_version}'
replace = __version__ = '{new_version}'

[bumpversion:file:tests/test_poetry_demo.py]
search = __version__ == '{current_version}'
replace = __version__ == '{new_version}'

To bump the version: $ bump2version patch and all version instances will become 0.1.1.

Provided configuration will also commit the modification and create tag. This (and many more features) are configurable.

A little problem is, that currently it requires extra configuration file .bumpversion.cfg, but there is an issue https://github.com/c4urself/bump2version/issues/42 planning to use pyproject.toml

The tool seems rather mature, has many features and is used for quite some time.

To implement bumping versions in poetry, it may serve at least as nice inspiration.

Personally I think storing a __version__ in __init__.py seems duplicative. You can use pkg_resources to retrieve the version for the installed package using:

pkg_resources.get_distribution("packagename").version

If used with:

poetry develop

This will work even when developing locally (it requires the egg-info).

At the moment this is not possible no.

poetry version will only bump the version in pyproject.toml. This is not intended and that’s definitely something I plan on adding.

Setuptoos has solved this problem for 2 years:

https://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files

You can put all metadata in setup.cfg instead of setup.py. For the version, you can specify:

version = attr: src.__version__

Which will load the version from the package directly, this way you have it in one place only. It works as long as you has setuptools >= 30.3 (current version is 38.5.1) so basically any package with a setup.py can already benefit from that without using any additional tool.

Le 27/05/2018 à 07:28, Victor-Nicolae Savu a écrit :

I really wish that the |version| field in the |.toml| ended up superseding the |version| string in |init.py|. This would reduce the burden of having to keep multiple places in the code in sync.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/sdispater/poetry/issues/144#issuecomment-392306362, or mute the thread https://github.com/notifications/unsubscribe-auth/AANivj7pFmuXBG79h1VOEG9_SM0V4Tj1ks5t2jl7gaJpZM4UO8xi.

Reading this thread, it seems to me that for now it’s best to keep both the pyproject.toml version and the package.__version__.

So my process is just to stop me accidentally forgetting to update one.

I’ve got a simple test that checks that those two are in alignment and fails if not.

import toml
from pathlib import Path
import my_package

def test_versions_are_in_sync():
    """Checks if the pyproject.toml and package.__init__.py __version__ are in sync."""

    path = Path(__file__).resolve().parents[2] / "pyproject.toml"
    pyproject = toml.loads(open(str(path)).read())
    pyproject_version = pyproject["tool"]["poetry"]["version"]

    package_init_version = my_package.__version__
    
    assert package_init_version == pyproject_version

Seems to do the job without having to resort to some of the convoluted solutions above which may be hard to maintain

Closing this as importlib.metadata is the way to go. A possible pattern to support python ❤️.8 and >=3.8 I described here.

I have 2 cents to spend here as well:

I stopped a long time ago to write any metadata in __init__.py. Others explained it or explain it better in other comments or blog articles: metadata has no place in the source code. I don’t want any __author__, __version__ or else in __init__.py. This information:

  • can be found in the SCM (git commits authors, git tags, etc.)
  • can be found in the configuration file of your packaging tool
  • and finally is, for Python, available at runtime with pkg_resources or importlib.metadata.

Maybe writing __version__ in __init__.py was/is very common, but it’s not a standard thing. In the end, I don’t think poetry should bother too much to support bumping __version__ in __init__.py.

Solution to get version at runtime:

# utils.py
import pkg_resources

def get_version():
    try:
        distribution = pkg_resources.get_distribution("my_package")
    except pkg_resources.DistributionNotFound:
        return "dev"  # or "", or None
        # or try with importib.metadata
        # or try reading pyproject.toml
    else:
        return distribution.version

Solution to get version when building Sphinx documentation:

# docs/conf.py
from pathlib import Path

import toml  # yes, you'll need to add toml to your docs/requirements.txt

metadata = toml.load(Path(__file__).parent.parent / "pyproject.toml")["tool"]["poetry"]
version = release = metadata["version"]
# project = metadata["name"]
# etc.

anybody: provide feedback

I’ll give you my (very opinionated) two cents.

My background is Java, but I recently find myself in a department of Python developers. For me, this is a massive culture shock - I assumed that a popular language like Python would have a mature ecosystem of tools, standard patterns for CI/CD, but that’s not what I have found.

Nevermind; when life gives you lemons, make lemonade 🍋😄

The first problem I must solve is packaging, so I need:

  • a repository to host packages,
  • a tool which can create, deploy, and retrieve those packages.

Obviously those two systems must agree on how to canonically identify a package, i.e. a coordinate scheme for artifacts [ namespace, name, version ].

Repository options in Python seem limited to Artifactory and pypiserver. I infer that pypiserver’s coordinate system must be some de facto standard. So, I must now find a package manager which can deploy to pypiserver.

If you do some Googling, or read Hacker News, everyone is saying, “use Poetry, that’s the project to watch”.

So, I tried Poetry, and those people are correct - it’s already far superior to the alternatives, so good job 👍

However, I immediately ran into this issue #144 , and this prevents me enforcing Poetry as the standard Python packaging tool across my company.

When poetry fails to meet expectations (about version management)… we are likely to lose such a user.

I am that user!

You have presented some options @vlcinsky very clearly, which I appreciate.

I understand that you are constrained by things outside of Poetry, you have compatibility concerns, and so on. However, your suggestions don’t address the real problem in my opinion, which is this:

  • there can only be a single place where a project’s metadata is defined

Only the package manager can define a package’s version, and it must be canonical.

Whatever this __init__.py nonsense is, get rid of it.

Yes, I know, this file has some semantics for the Python interpreter, and is necessary in certain circumstances… I really don’t care, that file has no business trying to define the version of my package, versioning is the responsibility of the package manager and no-one else.

Most of the suggestions in this thread are about incrementing a version, which is interesting, but futile if there isn’t a single canonical version we can think about incrementing.

Now that doesn’t work when the file system is not available, or when the project file is not shiped.

Le 27/05/2018 à 20:21, jgirardet a écrit :

you may use that

|import toml from pathlib import Path def get_version(): path = Path(file).resolve().parents[1] / ‘pyproject.toml’ pyproject = toml.loads(open(str(path)).read()) return pyproject[‘tool’][‘poetry’][‘version’] version = get_version() |

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sdispater/poetry/issues/144#issuecomment-392352521, or mute the thread https://github.com/notifications/unsubscribe-auth/AANivtdMQKQ53fmYDl0RaC2Wh-ZviBhlks5t2u6TgaJpZM4UO8xi.

@pmav99 you can’t do that unless you also ship pyproject.toml with your WHEEL for instance.

My 2 cents: I was quite used to add a __version__ in all my __init__.py and use it as the “truth”. As I wanted to give poetry a go, I started to read quite a few discussions/issues/PR here and a bit elsewhere and stumbled upon #1036, there the solution given by @chrahunt seems indeed to be the way to go. I tested quickly this with a single module and a package projects and it’s working as intended (for >3.8.3 or backports of course):

from importlib import metadata
import toml

try:
    __version__ = metadata.version(__package__)
except metadata.PackageNotFoundError:
    __version__ = toml.load("pyproject.toml")["tool"]["poetry"]["version"] + "dev"

After testing this, I have to admit I see the sense in not having any more any metadata in the code and use this standard library import if needed (cli version or whatever).

Just wanted to comment as this read session helped me quite a bit 😃

edit: added toml based info when in dev

Another potential solution: imitate setuptools_scm (see last part of https://github.com/pypa/setuptools_scm#pyprojecttoml-usage) and let the developer specify something like

# pyproject.toml

[tool.poetry]
version = {version = "0.1.0", write_to = "pkg/_version.py"}

(Add _version.py to your SCM’s ignore file so you don’t check it in)

Then you can import it like

# pkg/__init__.py

try:
    from ._version import version as __version__
except ImportError:
    __version__ = "0.0.0+unknown"

No external deps necessary.

Apart from poetry itself, __version__ bumping should be implemented in the scaffold project that is being created by poetry new too. In that particular case, I think it would make sense to stick to the standard library instead of adding a toml depenedency, so something like this could be used:

import configparser

parser = configparser.ConfigParser()
parser.read("pyproject.toml")
__version__ = parser["tool.poetry"]["version"]

__all__ = ["__version__"]

Depending on your build process, one solution is to use a shell script for bumping the version instead of running poetry version explicitly. I prefer using makefiles, so I added these two rules to mine to solve this problem.

poetryversion:
	poetry version $(version) 
	
version: poetryversion
	$(eval NEW_VERS := $(shell cat pyproject.toml | grep "^version = \"*\"" | cut -d'"' -f2))
	sed -i "" "s/__version__ = .*/__version__ = \"$(NEW_VERS)\"/g" x12_utils/__init__.py
	

This command can be run as follows: make version=1.2.3 version

If you dislike specifying the version(type) that way, this could just as easily be implemented as a shell script such as:

poetry version $1
new_vers=$(cat pyproject.toml | grep "^version = \"*\"" | cut -d'"' -f2)
sed -i "" "s/__version__ = .*/__version__ = \"${new_vers}\"/g" x12_utils/__init__.py

Thoughts on poetry tooling for versioning

Table of Contents

  1. Requirements to version management
  2. Existing options
    1. Manually edited source code
    2. Version bumping tool
    3. SCM commit/tag based versioning
  3. Poetry options for package version management
    1. No version bumping support
    2. Bump version on predefined locations
    3. Adopt commit/tag based versioning
    4. Support poetry versioning plugins
  4. Summary

poetry is becoming the tool simplifying python package development.

However, to succeed in wider range, it must fit into automated build processes and this includes managing package versions.

Risk: When poetry fails to meet expectations (about version management) of a developer, we are likely to loose such user.

Requirements to version management

  • support at least SemVer versioning schema (support for other schemes would be great)
  • allow to bump major, minor and patch versions (semi)manually
  • built python package shall be aware of it’s version, e.g. to allow CLI to report it’s package version using --version flag
  • try to prevent releasing modified packages with the same version.
  • allow to release versioned package even when outside of SCM environment (do we really need this?)

Meeting all these requirements is in fact impossible. There will be always space for well defined and respected procedures in package version management. The solution shall try to make these procedures as simple as possible anyway.

Existing options

Manually edited source code

Most packages use this approach.

  • Pros:
    • simple to setup (no effort)
  • Cons:
    • Does not meet most of the requirements
    • Makes procedure complex.

Version bumping tool

Tools like bump2version provide commands to bump version using simple call updating content of arbitrary number of files. Sometime the tool may even attempt to commit and tag the changes.

  • Pros:
    • bumping version is easy
    • might create relevant commit/tag in repository
  • Cons:
    • require installation of extra tool
    • initial setup effort
    • following commits build into packages declaring the same version regardless having different codebase

Note: currently poetry supports bumping version in pyproject.toml, so it partially falls into this category. Anyway, other features (edit content of other files) is not provided (yet).

SCM commit/tag based versioning

Tools like pbr or setuptools_scm read version related information from commits and tags. When commit is tagged as v1.2.3, such package version is built. Following commits get incremented versions (using suffixes such as dev{distance} etc).

  • Pros:
    • meets most of criteria defined above
    • great fit into automated build procedures
    • relatively easy to setup
    • simple version assignment procedure
    • compatible with tool like reno, building release notes based on SCM tags
  • Cons:
    • requires presence of SCM repository
    • version is not always explicitly present in source code (as it lives in SCM tags)

Note: While there are multiple SCM systems (git, hg, …), some tools work only with git and this seems to be generally more and more acceptable decision.

Poetry options for package version management

Here are some options, poetry could take in regards to package version management.

No version bumping support

Skip version command, leave user with their own choice of external tools such as bump2version.

Bump version on predefined locations

Bumping would update version in pyproject.toml and in another file (like module/__init__.py).

Adopt commit/tag based versioning

Adopt pbr or setuptools_scm approach and make it simpler.

Support poetry versioning plugins

To be flexible enough, use plugins (similarly as pytest is using them) for versioning.

It would require poetry to:

  • define expected plugin interface (such as bump major/minor/patch command and a hook to report version at the build time)
  • extend pyproject.toml and add information about (single) version bumping plugin to use, probably allowing also extra section for plugin specific configuration parameters.
  • provide it’s internal plugin for version bumping. This would serve also as an example for other plugins.

There are packages which can make plugin integration rather easy, such as pluggy.

Summary

First two options (“No version bumping support” and “Bump version on predefined locations”) are not ideal, as most builds result in packages declaring the same version regardless of having different codebase. This could be partially solved by rule to bump to dev version after each release, but this is quite inflexible and not easy to follow.

I would be very happy with “Adopt commit/tag based versioning” option. Anyway, I am not sure, if such solutions seems enough acceptable to wider audience.

Last option (“Support poetry versioning plugins”) seem to be the most flexible one. It adds some complexity to poetry source code, but on the other hand it could outsource version bumping effort to other parties and it also has the potential to “please everybody”.

Proposed next actions:

  • anybody: provide feedback
  • @sdispater: express your opinion, preferences or decision

In my case reading from pyproject.toml does not make sense as I ship a CLI and myapp version has to be able to answer to this question without pyproject.toml.

FWIW I’m using this:

import importlib.metadata
__version__= importlib.metadata.version('<package name>')

Works fine 👍

One thing to keep in mind with the automatisms like that is that they will access the disk on importing the library. Code execution on import is not that uncommon in Python, but not desireable to keep down import times and minimize side effects.

Does the pyproject.toml file even ship with wheels?

Using pkg_resources also introduces an install-time dependency on setuptools, no?

I have published poetry-bumpversion plugin to update version in other files when poetry upgrades the package version. To update __version__ just install the plugin and add the following to your pyproject.toml file.

[tool.poetry_bumpversion.file."your_package/__init__.py"]

I’m coming in here in Jan 2022, and I’m actually just looking for the canonical answer. Where is the single source of truth spot to put the version string (or where in the docs as poetry version doesn’t seem to be the place).

I ran across this: https://pythonrepo.com/repo/tiangolo-poetry-version-plugin-python-package-dependency-management

which seems to show that others have this issue and are solving it with plugins but if I missed this a pointer in the right direction is appreciated.

It doesn’t work with vendoring, or anything using sys.path.append. And it’s way harder to type in an interpretter. Who remember that ?

Le 30/05/2018 à 19:54, Bert JW Regeer a écrit :

Personally I think storing a |version| in |init.py| seems duplicative. You can use |pkg_resources| to retrieve the version for the installed package using:

|pkg_resources.get_distribution(“packagename”).version |

If used with:

|poetry develop |

This will work even when developing locally (it requires the egg-info).

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sdispater/poetry/issues/144#issuecomment-393257700, or mute the thread https://github.com/notifications/unsubscribe-auth/AANivn9NPZz_2OgTFG4OPozzcHa3zB_xks5t3tzvgaJpZM4UO8xi.

Cross-posting my comment from: https://github.com/python-poetry/poetry/pull/2366#issuecomment-849615115 as it seems it’s relevant here too.

Sorry if it feels like spam, but I think people subscribed to this issue might not be subscribed to that one and could miss this.


I just made a plugin (available with Poetry 1.2.0a1 and above) to read the version from an __init__.py file:

# __init__.py

__version__ = "0.1.0"

or from a git tag, set with a GitHub release or with:

$ git tag 0.1.0

I thought this could be useful for others here too.

https://github.com/tiangolo/poetry-version-plugin

I’m coming in here in Jan 2022, and I’m actually just looking for the canonical answer. Where is the single source of truth spot to put the version string (or where in the docs as poetry version doesn’t seem to be the place).

I ran across this: https://pythonrepo.com/repo/tiangolo-poetry-version-plugin-python-package-dependency-management

which seems to show that others have this issue and are solving it with plugins but if I missed this a pointer in the right direction is appreciated.

I’d also like some clarification. I’m not sure what to follow. There are still several different plugins floating around:

  1. https://github.com/bulv1ne/poetry-bumpversion
  2. https://github.com/tiangolo/poetry-version-plugin
  3. https://github.com/mtkennerly/poetry-dynamic-versioning

I’m fine to use importlib.metadata, so long as I can also use it for local packages I am developing (develop = true). Has anyone tested this?

Let me summarize (not expressing my opinion, only my understanding of opinions above – including mine; please correct any omissions or misunderstandings and I will edit accordingly). Specs:

  1. Poetry version should bump the version
  2. There should be no duplicate information about the version or, failing this, the duplication should be automatically maintained with a single source of truth. The possibility of inconsistent version info should be minimized.
  3. The version should be available at runtime with a minimum of dependencies
  4. The version mechanism should not rely on source code management (because core says so) but it would be nice if it worked well with (because we all use SCM)

Solutions (scm issue dealt with separately):

  1. Pyproject.toml: it is the file poetry uses for meta info, but it’s not available at runtime
  2. Pyproject.toml plus init.__version__ maintained by poetry: it’s a duplication but it is managed. In case of conflict pyproject.toml wins, __version__ is overwrittern
  3. Pyproject.toml plus a Python file that poetry install generates with a version variable that can be imported elsewhere: no duplication to manage in most cases as you don’t need to touch the generated file except when jumping around versions
  4. Pyproject.toml plus init.__version__ initialized at load time using importlib.metadata or alternate mechanism: checks the first three boxes

As to the alternate mechanism to initialize __version__, some people are wary of an additional import so here are three options

  1. as said, importlib.metadata or backport thereof
  2. a micro-package with faster load that just grabs the version
  3. a well documented snippet of code one can put in __init__

As to SCM:

  1. Use tags to tag version after source bump, whichever the mechanism selected above
  2. Ignore, it is out of scope
  3. Optionally, make SCM version authoritative. Read latest parent tag with version format (say v1.2.3), set the version based on that, than follow one of the solutions 1-4 above. Could look like version = {use_scm = true, scm=git} or some such

@vanyakosmos But based on @finswimmer’s comment, couldn’t you just use this logic to achieve that?

import importlib  # probably want a conditional for pre-3.8

__version__ = importlib.metadata("mypackage").version

Again, extracting from pyproject.toml doesn’t help at run-time, because the pyproject file is not installed.

Another take on extracting the version from the “pyproject.toml” file:

from tomlkit import toml_file

def _get_project_metadata():
    project = toml_file.TOMLFile("pyproject.toml").read()
    return project.get("tool", {}).get("poetry", {})

project_metadata = _get_project_metadata()
version = project_metadata.get("version")

The version variable will beset to None in case the file is broken.

Bonus: basic tests for it!

from unittest.mock import patch
import pytest
import tomlkit
from XYZ import _get_project_metadata

@patch("XYZ.toml_file")
def test_get_metadata(tomlfile_mock):
    config = [
        "[tool.poetry]",
        "version = 'v-test'",
        "",
        "[tool.other]",
        "ignored = true",
    ]
    doc = tomlkit.parse("\r\n".join(config))
    tomlfile_mock.TOMLFile.return_value.read.return_value = doc

    metadata = _get_project_metadata()

    assert metadata["version"] == "v-test"
    tomlfile_mock.TOMLFile.assert_called_with("pyproject.toml")
    tomlfile_mock.TOMLFile.return_value.read.assert_called_with()


@pytest.mark.parametrize(
    "config", [[""], ["[tool]", "version = 'incomplete-table-name'"], ["[tool.poetry]", "version_missing = true"],]
)
@patch("XYZ.toml_file")
def test_get_metadata_with_invalid_config(tomlfile_mock, config):
    doc = tomlkit.parse("\r\n".join(config))
    tomlfile_mock.TOMLFile.return_value.read.return_value = doc

    metadata = _get_project_metadata()

    assert metadata.get("version") is None

This plugin also has support for updating __version__: https://github.com/mtkennerly/poetry-dynamic-versioning

I also think this is something very important.

Another example is when you integrate it with a tool like Sentry, you are willing to know for which version it applies. Currently, I couldn’t find a proper way to include it except writing a __version__ string in the __init__.py

@williamcanin Add it to the include section of your pyproject.toml

[tool.poetry]
...
include = [
    "pyproject.toml"
]

@adithyabsk This is not guaranteed and safe. This would cause “pyproject.toml” to be sent to the “site-packages” folder. If another project has the same import configuration, it will replace the “pyproject.toml” of the other project.

Again, extracting from pyproject.toml doesn’t help at run-time, because the pyproject file is not installed.

Any solution that requires adding a python dependency to my project to read a simple version string is a non-solution. This includes setuptools and especially pkg_resources, which is very slow.

IMHO, version semantics and bumping and SCMs could all be considered secondary features. The most important thing is to ensure that the version in pyproject.toml drives/generates the version found in mypackage.__version__.

I have the impression, the discussion here goes beyond the aim of poetry:

Poetry is a tool for dependency management and packaging in Python.

“Packaging” is the important word. Not CI/CD nor Deployment.

If you package your project, the version number is integrated into it. Then use importlib_metadata (since python 3.8 as build-in importlib.metadata) to access the version number during run time if needed. There is no need to have the version number in any additional file.

With poetry version one have a cli to manipulate the version number in pyproject.toml (which is used when building the package through poetry). So integrating this into a pipeline shouldn’t be that hard, should it?

Just my two cents 😃

I’m with @vlcinsky on this one. The best I’ve found so far to support git tag driven versions is https://pypi.org/project/setuptools-scm/.

I also switched to importlib.metadata But I had to change my release workflow, before it was fully automated as I was able to choose in advance the version number during development.

My Take Away for importlib.metadata

Pros: No more version number in files No more required to bump/commit/push a new version during development or release workflow
Version is only available in git tags and inside package relatively easy to automate for patches releases even with branch protection

Cons: hard to automated for minor and major release

Bonus

Here is the github action snippet code to trigger a package publish for version 1.0.0 My Workflow

  • develop
  • create pr
  • merge
  • release

I use manually gh cli gh release create 1.0.0 --generate-notes or directly from github.com (it generates the same event)

name: publish package

on:
  push:
    tags:
    - '**'

jobs:
  publish:
    runs-on: ubuntu-20.04
    steps:
    - uses: actions/checkout@v3
      name: Checkout code
    - uses: actions/setup-python@v4
      name: Set up Python
      with:
        python-version: '3.10'
    - name: Install tools
      run: |
        pip install --upgrade pip
        pip install poetry
    - name: Publish distribution 📦 to PyPI
      run: |
        poetry config repositories.pypi ${{ secrets.PYPI_REPOSITORY }}
        poetry version ${{ github.ref_name }}
        poetry publish --build -r pypi -u ${{ secrets.PYPI_PUBLISH_USERNAME }} -p ${{ secrets.PYPI_PUBLISH_TOKEN }}

@williamcanin ~you can distribute the pyproject.toml if you would like, as in the option is there to do it.~

EDIT: this is actually incorrect as per: #144 (comment)

Fascinating reply from @sdispater in January to a qustion about getting poetry to use setuptools_scm “as a plugin or as direct integration” on twitter:

A plugin system is in the works and should be available in the 1.1 release. You can take a look at the WIP Pull Request here: #1237 No ETA yet. I would like to release the 1.1 version by the end of the quarter but it will be a big release so there is no guarantee that we will make it in time.

Any news on this matter?

I am right now checking out importlib_metadata in 3.7 (backport of importlib.metadata suggested by @dmontagu ) It seems to pick the contents of pyproject, not __init__.py after a couple of tests. The peps that describe metadata are 214, 314, 345 and 566. Pep 8 is about formatting, as we all know, and its mention of __version__ is cursory, in an example. 214 mentions two files, PKG_INFO and setup.py. I wonder where this use of putting some metadata in init.py comes from (I follow it too, without much thought; not questioning it’s widespread). The optional pep396 (status: deferred) is the closest I could find. It states

The version attribute in a classic distutils setup.py file, or the PEP 345 [7] Version metadata field SHOULD be derived from the __version__ field, or vice versa.

In fact, proper version management was one of two reasons we postponed using poetry and moved back to using pbr. The other reason was usability with private repositories which we need in our continuous integration process.

I really like the workflow, where the version is assigned by adding tag to git repo, I even plan to adopt usage of reno for generating release notes.

I wonder, if similar workflow could be possible with poetry. Taking into account, that pyproject.toml includes explicit package version, this does not seem trivial. One idea is to allow version having alternative value git-tag and in such a case to generate the value from git tag (the style pbr does that seems working well).

I utilized the latter strategy (looking for a list of defined version files in pyproject.toml) when I created AutoPub, a tool that enables project maintainers to release new package versions to PyPI by merging pull requests. AutoPub needs to increment all relevant version numbers before publishing new packages to PyPI, and while I was hoping Poetry could handle this, I eventually solved it within AutoPub itself.

First, AutoPub looks for a tool.autopub.version-strings key and creates a VERSION_STRINGS list of the version file paths contained within, if any.

AutoPub defines an update_version_strings function that increments version numbers. This function is used to update the relevant version string(s) if any version files are defined.

Perhaps these code samples could be useful when a solution is devised for Poetry. I would love to remove that code and rely on Poetry to comprehensively handle version string updates in all the relevant files.

The reasons I think this latter approach is preferred relative to others include the following:

  • explicit is better than implicit
  • upholds the principle of least astonishment
  • not feasible to guess (or appropriate to dictate) which files might contain version number definitions that the user wants to be updated

@JnyJny If I understand correctly, your current approach for text files is just replacing the version string wherever it occurs in any file with a matching extension. I feel like that has a relatively high potential for false positives, e.g. if you list required versions for dependencies anywhere in your documentation.

I haven’t dug into your implementation for python files at all but in principle I think it would be less likely to cause problems there (given you can analyze code more deeply than documentation, and code is less likely to include unrelated version strings).

Also, I think this would be a nice quality-of-life improvement even if it was only applied to __init__.pys (and given the potential for false positives, that may be preferable anyway).

I would also like to see poetry version bump the version in:

  • pyproject.toml
  • <package_name>/__init__.py
  • tests/*.py # any tests which have a version literal in them

Since pyproject.toml is already handled, it seems something like using astor might be a good choice for safely modifying python source.