# Replacement for ./setup.py --version?

**URL:** <https://discuss.python.org/t/replacement-for-setup-py-version/39103>\
**Category:** Python Help\
**Tags:** help, packaging-help\
**Created:** [November 19, 2023, 7:51am UTC](https://discuss.python.org/t/replacement-for-setup-py-version/39103 "2023-11-19T07:51:09Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![ericzolf](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ericzolf/32/15985_2.png) [@ericzolf](https://discuss.python.org/u/ericzolf)\
**Post date:** [November 19, 2023, 7:51am UTC](https://discuss.python.org/t/replacement-for-setup-py-version/39103/1 "2023-11-19T07:51:09Z")

</div>

Hi,

I’ve been reading [Why you shouldn't invoke setup.py directly](https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html) and I’m willing to replace all calls of setup.py (thanks to the very helpful table at the end of the article, it shouldn’t be too difficult) but I’m using setuptools-scm to define the version of my tool (rdiff-backup) and using hence `./setup.py --version` to get the version of my tool (not of setup.py itself, obviously). I had a look at `pyproject-build` but didn’t see anything useful and my search skills failed on me. Or is this one of the few cases where calling setup.py is still correct?

---

<div class="post-metadata">

**Author:** ![ericzolf](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ericzolf/32/15985_2.png) [@ericzolf](https://discuss.python.org/u/ericzolf)\
**Post date:** [November 19, 2023, 7:54am UTC](https://discuss.python.org/t/replacement-for-setup-py-version/39103/2 "2023-11-19T07:54:29Z")

</div>

I hate when this happens: writing down the question brought a new idea how to find a solution, and I found it… `python -m setuptools_scm` is the simple answer… Sorry for the noise, perhaps it’ll help someone else at least.

---

<div class="post-metadata">

**Author:** ![jeanas](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jeanas/32/8115_2.png) [@jeanas](https://discuss.python.org/u/jeanas)\
**Post date:** [November 19, 2023, 9:41am UTC](https://discuss.python.org/t/replacement-for-setup-py-version/39103/3 "2023-11-19T09:41:59Z")

</div>

> [@ericzolf](#):
>
> Or is this one of the few cases where calling setup.py is still correct?

There are no cases where invoking `setup.py` directly is still correct.

---

<div class="post-metadata">

**Author:** ![sinoroc](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/sinoroc/32/1607_2.png) [@sinoroc](https://discuss.python.org/u/sinoroc)\
**Post date:** [November 20, 2023, 7:04pm UTC](https://discuss.python.org/t/replacement-for-setup-py-version/39103/4 "2023-11-20T19:04:42Z")

</div>

That is a good question. One that I have seen asked more than once. And the “correct” solution is not that simple I am afraid (see below):

- [setuptools - How to get `setup.cfg` metadata at the command line (Python) - Stack Overflow](https://stackoverflow.com/a/71276197)

In the case of _setuptools-scm_, there is indeed an easy way, `python -m setuptools_scm`:

- [Usage - setuptools scm](https://setuptools-scm.readthedocs.io/en/latest/usage/#as-cli-tool)

Personally I think it is fine to use `python setup.py --version`, as long as you are fully aware that it is not supported, that it might break, and that you should really avoid it. But if there is no other solution, then maybe it is okay-ish:

- [How to modernize a setup.py based project? - Python Packaging User Guide](https://packaging.python.org/en/latest/guides/modernize-setup-py-project/#what-if-something-that-can-not-be-changed-expects-a-setup-py-file)

Then there is of course the cases where the metadata (here the version string) is clearly specified in some easy to parse file such as `pyproject.toml` or `setup.cfg`. In such cases it should be straightfoward to extract the value with [_`tomllib`_](https://docs.python.org/3/library/tomllib.html) or [_`configparser`_](https://docs.python.org/3/library/configparser.html)

* * *

For an “over-kill” solution that allows getting any meta-data field from any project, with the help of the [_`build`_ project](https://pypi.org/project/build/):

```python
#!/usr/bin/env python

import argparse
import pathlib

import build.util

def _main():
    args_parser = argparse.ArgumentParser()
    args_parser.add_argument('path')
    args = args_parser.parse_args()
    path_name = getattr(args, 'path')
    path = pathlib.Path(path_name)
    #
    metadata = build.util.project_wheel_metadata(path)
    print(metadata)

if __name__ == ' __main__':
    _main()

```

This will actually call the build back-end (_setuptools_, _poetry_, _flit_, _pdm_, or watever), so this might take some seconds.

The `build.util` API is documented [here (on “_latest_”)](https://pypa-build.readthedocs.io/en/latest/api.html#module-build.util) and it was added in [“0.7.0 (16-09-2021)”](https://pypa-build.readthedocs.io/en/latest/changelog.html#id3), in this [change](https://github.com/pypa/build/pull/340).

---

<div class="post-metadata">

**Author:** ![kknechtel](https://avatars.discourse-cdn.com/v4/letter/k/e47c2d/32.png) [@kknechtel](https://discuss.python.org/u/kknechtel)\
**Post date:** [November 21, 2023, 6:47am UTC](https://discuss.python.org/t/replacement-for-setup-py-version/39103/5 "2023-11-21T06:47:45Z")

</div>

Are you only trying to find out what setuptools\_scm thinks is the current version number? Or is this intended to be part of a larger automated task or something? The default versioning scheme that setuptools\_scm uses is explained in the documentation. If you just want to see the repository tags (which setuptools\_scm will use to compute a version number; otherwise the only thing it can go off of is the number of commits), and you aren’t trying to do it programmatically, it might make more sense to use the CLI for your version control directly.

> [@sinoroc](#):
>
> And the “correct” solution is not that simple I am afraid (see below):
> 
> - [setuptools - How to get `setup.cfg` metadata at the command line (Python) - Stack Overflow](https://stackoverflow.com/a/71276197)

If you need to take into account anywhere that the metadata could be or how it could be specified (in particularly if it’s something that a tool like `setuptools_scm` is computing on the fly, and the tool doesn’t offer its own interface), then yes, the natural approach is to invoke the build system and see what it generates for the corresponding core metadata.

However, for something that’s known to be in `pyproject.toml`, or `setup.cfg` etc. (and it seems like OP in that case was specifically concerned with `setup.cfg`), it would make more sense to just parse that file directly. In the case of `pyproject.toml` in particular, one can simply install `toml` or `tomli` (or use `tomllib` since 3.11) and interpret the file just as one would with a JSON file and `json`.

---

<div class="post-metadata">

**Author:** ![ericzolf](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ericzolf/32/15985_2.png) [@ericzolf](https://discuss.python.org/u/ericzolf)\
**Post date:** [November 23, 2023, 6:17am UTC](https://discuss.python.org/t/replacement-for-setup-py-version/39103/6 "2023-11-23T06:17:24Z")

</div>

Hi, @sinoroc thanks for the extensive answer, I think, I’ll stick to the setuptools\_scm approach, the “overkill” approach would also work, but it’s definitely… overkill (and it takes 17s where setuptools\_scm takes 1s).

---

<div class="post-metadata">

**Author:** ![sinoroc](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/sinoroc/32/1607_2.png) [@sinoroc](https://discuss.python.org/u/sinoroc)\
**Post date:** [November 23, 2023, 6:25pm UTC](https://discuss.python.org/t/replacement-for-setup-py-version/39103/7 "2023-11-23T18:25:08Z")

</div>

It is over-kill for that specific situation for sure : D

It is because we know what the build backend is and what its plugins are. But this solution should work for any project, any build backend, any plugin. It might be helpful to whoever read this thread in search for a generic solution.

---

<div class="post-metadata">

**Author:** ![CAM-Gerlach](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cam-gerlach/32/3688_2.png) [@CAM-Gerlach](https://discuss.python.org/u/CAM-Gerlach)\
**Post date:** [November 24, 2023, 3:10am UTC](https://discuss.python.org/t/replacement-for-setup-py-version/39103/8 "2023-11-24T03:10:28Z")

</div>

> [@sinoroc](#):
>
> ```python
> #!/usr/bin/env python
> 
> import sys
> import pathlib
> 
> import build.util
> 
> def _main():
> args_parser = argparse.ArgumentParser()
> args_parser.add_argument('path')
> args = args_parser.parse_args()
> path_name = getattr(args, 'path')
> path = pathlib.Path(path_name)
> #
> metadata = build.util.project_wheel_metadata(path)
> print(metadata)
> 
> if __name__ == ' __main__':
> _main()
> 
> ```

We can simplify that a bit to just…

```python
import sys
import build.util

path = sys.argv[1] if len(sys.argv) > 1 else "."
print(build.util.project_wheel_metadata(path))

```

This also gets the first in the current dir by default, without having to pass an arg.

> [@ericzolf](#):
>
> the “overkill” approach would also work, but it’s definitely… overkill (and it takes 17s where setuptools\_scm takes 1s).

The great majority of this time is due to needing to create an isolated environment and install the build dependencies, which is what gaurentees everything will work for any arbitrary project. To skip this when the build dependencies are already available in the local environment (which running `python -m setuptools_scm` also requires them to be) and perform similarly to the same, but automatically fall back to isolated mode otherwise, you can do the following:

```python
import sys

import build
import build.util

path = sys.argv[1] if len(sys.argv) > 1 else "."
try:
    metadata = build.util.project_wheel_metadata(path, isolated=False)
except build.BuildBackendException:
    metadata = build.util.project_wheel_metadata(path, isolated=True)
print(metadata)

```

This is essentially [what we do in Pyroma](https://github.com/regebro/pyroma/pull/92) to retrieve the project metadata, taking the fast path if it works and otherwise falling back to the slower but more reliable path otherwise.

---

<div class="post-metadata">

**Author:** ![ericzolf](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ericzolf/32/15985_2.png) [@ericzolf](https://discuss.python.org/u/ericzolf)\
**Post date:** [November 24, 2023, 6:49am UTC](https://discuss.python.org/t/replacement-for-setup-py-version/39103/9 "2023-11-24T06:49:42Z")

</div>

OK, I ended up mixing both of your suggestions into the following script. That allows me to output only a specific metadata parameter, e.g. the “Version”:

```python
#!/usr/bin/env python
# Alternative using the build front-end to `python -m setuptools_scm`

import argparse
import pathlib

import build.util

def get_args():
    """Parse the command line arguments"""
    args_parser = argparse.ArgumentParser(
        description="Output some or all metadata of a given project")
    args_parser.add_argument('--path', '-p', default='.',
                             help="Path where to find the project (default: current path)")
    args_parser.add_argument('keys', nargs='*',
                             help="Metadata keys to output (else output all metadata)")
    args = args_parser.parse_args()
    return args

def get_metadata(path='.'):
    """Get project metadata from the given path"""
    path = pathlib.Path(path)
    try:
        metadata = build.util.project_wheel_metadata(path, isolated=False)
    except build.BuildBackendException:
        metadata = build.util.project_wheel_metadata(path, isolated=True)
    return metadata

def main():
    args = get_args()

    metadata = get_metadata(args.path)

    if args.keys:
        for key in args.keys:
            print(metadata[key])
    else:
        print(metadata)

if __name__ == ' __main__':
    main()

```

Thanks for the help, that was great!

---

<div class="post-metadata">

**Author:** ![jeanas](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jeanas/32/8115_2.png) [@jeanas](https://discuss.python.org/u/jeanas)\
**Post date:** [November 24, 2023, 7:08pm UTC](https://discuss.python.org/t/replacement-for-setup-py-version/39103/10 "2023-11-24T19:08:21Z")

</div>

> <https://github.com/pypa/packaging.python.org/pull/1415>
>
> https://discuss.python.org/t/replacement-for-setup-py-version
> 
> 
> \----
> :books: …Documentation preview :books:: https://python-packaging-user-guide--1415.org.readthedocs.build/en/1415/
