Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get the version from pyproject.toml [tool.poetry] section #432

Closed
qequ opened this issue Sep 23, 2022 · 9 comments · Fixed by #502
Closed

Get the version from pyproject.toml [tool.poetry] section #432

qequ opened this issue Sep 23, 2022 · 9 comments · Fixed by #502

Comments

@qequ
Copy link

qequ commented Sep 23, 2022

Currently using towncrier with a project that uses poetry as package manager. The docs says that you can add towncrier's config to pyproject.toml, and poetry has metadata version like this

[tool.poetry]
...
version = "0.1.0"

if adding towncrier metadata to the toml like this

[tool.towncrier]
directory = "changes"
package_dir = "."
filename = "NEWS.rst"

it complains about lacking the version. Can't towncrier parse the toml looking for version instead of repeating version variables?

@adiroiban adiroiban changed the title Get the version from pyproject.toml Get the version from pyproject.toml [tool.poetry] section Sep 24, 2022
@adiroiban
Copy link
Member

Thanks for the feedback.

I have updated the title of this ticket.

It read to me like this is about reading the version from [tool.poetry] the pyproject.toml is secondary.

And maybe, pyproject.toml and [tool.poetry] section don't really care,
all is missing is for towncrier to call some poetry API to get the version, from whatever source poetry wants.

So towncrier should only care about talking with poetry, and poetry can get the info from pyproject.toml, or any other source (reading the starts :)


As a workaround, you can still use pyproject.toml and call towcrier build --version 1.2.3rc1 or something like that.

I don't know poetry but maybe there is a CLI tool for poetry that can print the verision, and you can pipe it into towncrier.


Anyway, having towncrier automatically detect the version, as advertised by poetry would be nice.

I would be happy to review and approve such a PR :)


@dgutson
Copy link

dgutson commented Sep 26, 2022

What about https://python-poetry.org/docs/cli/#version ?

@agriyakhetarpal
Copy link
Contributor

Hi @adiroiban, I shall be happy to write a PR for this:

Since the aim is to get the version from poetry directly, would a possible implementation be to write an additional method get_poetry_project_name() in src/towncrier/_project.py? That way, one can get the version of a package by running either poetry show <projectname> or poetry version like @dgutson suggests, inside the directory of the package. Please let me know if there are any other ideas for doing this as I continue to familiarise myself with the source code

@adiroiban
Copy link
Member

Anything that does the job, is well documented and tested and doesn't introduce regression should be ok.

I know there is importlib.metadata that AFAIK is supposted to provide a standard way to extract information for a package.

and supporting this feature only on py3.8 and above should be good enough.

@agriyakhetarpal
Copy link
Contributor

agriyakhetarpal commented Apr 21, 2023

Okay, so I was able to find a solution (only my initial thoughts though, but if this is right I can proceed with opening a PR) – if one can assume that poetry is installed for any Python project that uses poetry as a dependency manager (of course), and __version__ is not defined in __init__.py, then using API from poetry we can do

def get_poetry_version(package_dir: str, package: str) -> str:
    # assume poetry is installed
    from subprocess import check_output
    version = check_output(['poetry', 'version', '--short']).decode('utf-8')
    return version

and then running towncrier build attaches the correct package version from the [tool.poetry] configuration in pyproject.toml (I tested this by making a boilerplate poetry package). Is this something that can work? We can also run get_poetry_version internally within get_version and raise another exception if this fails

That being said, I am not sure if this is the best way though; I did try to use importlib.metadata.version() like you suggested to get the version, but I could not get that to work with poetry packages (edit: it works but the package needs to be either installed or at least editable-installed, relevant discussion on python-poetry/poetry#273).

@adiroiban
Copy link
Member

Regarding importlib.metadata.version() I am expecting that towncrier is used as a development tool and you will have your project installed.

The nice part about using importlib.metadata.version() is that it will work not only with poetry

As mentioned earlier. I am not happy with calling poetry version using check_output, but as long as the code is well documented and tested, it should be fine.

Is there any non-CLI API for poetry ?
Maybe we can have something like

from poetry import Project
our_project = Project('.')
# We can query the version.
our_project.get_version()

@agriyakhetarpal
Copy link
Contributor

@adiroiban that makes sense, in most situations a project will be installed or editable-installed. I do not think there is any non-CLI poetry API and I could not find one either (but I guess that it might be using importlib.metadata or its backport importlib_metadata under the hood too).

I am happy to use importlib.metadata.version(), which seems to be the best way plus I tested it across some sample poetry and non poetry projects. It works perfectly, and this will be supported on 3.8 and above like you mentioned. I shall open up a draft PR with an implementation

agriyakhetarpal added a commit to agriyakhetarpal/towncrier that referenced this issue Apr 29, 2023
@jaraco
Copy link
Contributor

jaraco commented Apr 29, 2024

I should point out that reading from pyproject.toml or from an installed package's metadata (#502) might seem like workable approaches, they will fail in general because they don't honor PEP 517/518 designs. The consistent way to get metadata for an unbuilt package is to use PEP 517 to have whatever backend (poetry, setuptools, flit, etc) resolve the metadata (maybe from pyproject.toml, maybe dynamically generated, etc).

The project_wheel_metadata function was designed to generally resolve the metadata for any PEP 517 project. It uses 'importlib.metadata' under the hood, so build.util.project_wheel_metadata('.')['Version'] should be viable.

The downside is that it's potentially very slow (because it creates a virtualenv to do an isolated build, builds the metadata (and possibly the whole package if the build backend doesn't support just building the metadata), and then tears down that isolated build). It has another downside in that it by default depends on network access to install dependencies unless the isolated build is disabled and the (build) dependencies have been previously resolved by something else.

If you can rely on the package having been built and installed (as suggested in the comment above), then yes, using importlib.metadata directly will be faster and appropriate. The challenge then becomes - how to know what is the distribution package name (as discussed in #502) from a package_dir. Once again, you're stuck either having to infer it from pyproject.toml or the Python import package, or using build.util.project_wheel_metadata(package_dir)['Name'] (which is most correct but has the same downsides as resolving the version).

My advice - adopt build.util.project_wheel_metadata, even if it's slow and creates environmental constraints, and raise issues with /pypa/packaging-problems when it is slow (and shouldn't be). That is, let's get the Python packaging ecosystem to make package metadata available and performant generally.

SmileyChris added a commit that referenced this issue May 22, 2024
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Adi Roiban <[email protected]>
Co-authored-by: Adi Roiban <[email protected]>
Co-authored-by: Chris Beaven <[email protected]>
@adiroiban
Copy link
Member

I should point out that reading from pyproject.toml or from an installed package's metadata (#502) might seem like workable approaches, they will fail in general because they don't honor PEP 517/518 designs.

Thanks @jaraco for the feedback and info about build.util.project_wheel_metadata

#502 was merged as I think that while not ideal, it's a bit better than what we had in the past.

Basically, what we currently have only works if you have the project installed with pip install -e . ... but I hope that everyone will run towncrier in a simialr environment and will not use just pip install .


I guess that we will see how people will use this functionality.


I agree that doing a build is the method that always get a correct result.

At the same time, if you call towncrier build on your project, I expect that you already have an active virtual environment and the package installed in dev mode ... do recreating the virtual environment , while correct, might not be what people want.


The solution from #502 , while not 100% looks simple, and I hope that it will alwasy work in practice ... time will tell.

I am happy to have a PR that implements getting the version via build.util.project_wheel_metadata

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants