I’ve been reading Why you shouldn't invoke setup.py directly 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?
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.
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:
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 or configparser
For an “over-kill” solution that allows getting any meta-data field from any project, with the help of the build project:
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.
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.
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).
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.
This also gets the first in the current dir by default, without having to pass an arg.
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:
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”:
# Alternative using the build front-end to `python -m setuptools_scm`
"""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)")
help="Metadata keys to output (else output all metadata)")
args = args_parser.parse_args()
"""Get project metadata from the given path"""
path = pathlib.Path(path)
metadata = build.util.project_wheel_metadata(path, isolated=False)
metadata = build.util.project_wheel_metadata(path, isolated=True)
args = get_args()
metadata = get_metadata(args.path)
for key in args.keys:
if __name__ == '__main__':