How to get `setup.cfg` metadata at the command line (Python)

Question:

When you have a setup.py file, you can get the name of the package via the command:

C:somedir>python setup.py --name

And this would print the name of the package to the command line.

In an attempt to adhere to best practice, I’m trying to migrate away from setup.py by putting everything in setup.cfg since everything that was previously in setup.py was static content.

But our build pipeline depends on being able to call python setup.py --name. I’m looking to rewrite the pipeline in such a way that I don’t need to create a setup.py file.

Is there way to get the name of the package when you have a setup.cfg but not a setup.py file?

Asked By: myoungberg

||

Answers:

Maybe using the ConfigParser Python module ?

python -c "from configparser import ConfigParser; cf = ConfigParser(); cf.read('setup.cfg'); print(cf['metadata']['name'])"
Answered By: Cubix48

For an "over-kill" solution that allows getting any meta-data field from any PEP 517-compatible project (i.e. pyproject.toml), with the help of the build project:

#!/usr/bin/env python3

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") and it was added in "0.7.0 (16-09-2021)", in this change.


But yes, realistically, as you have already said it I would recommend just keeping a minimal setup.py:

#!/usr/bin/env python3

import setuptools
setuptools.setup()

so that python setup.py --name keeps working

And of course, as others have already said it, parsing is a viable and simple solution. setuptools uses ConfigParser from the standard library to read the setup.cfg file, so you can do it as well.


Another possible solution (credit to wim):

python -c 'import setuptools; setuptools.setup()' --name
Answered By: sinoroc

TL;DR, use the setuptools configuration API https://setuptools.pypa.io/en/latest/setuptools.html#configuration-api.

In your case, this line will give the name of the package:

python -c 'from setuptools.config import read_configuration as c; print(c("setup.cfg")["metadata"]["name"])'

Edit:

In setuptools v61.0.0 (24 Mar 2022) setuptools.config.read_configuration was deprecated . Using the new API, the command becomes:

python -c 'from setuptools.config.setupcfg import read_configuration as c; print(c("setup.cfg")["metadata"]["name"])'

Explanation:

Setuptools exposes a read_configuration() function for parsing metadata and options sections of the configuration. Internally, setuptools uses the configparser module to parse the configuration file setup.cfg. For simple str-type data such as the "name" key, configparser can be used to read the data. However, setuptools also allows dynamic configuration using directives that cannot be directly parsed with configparser.

Here is an example that shows the difference between the two approaches for replacing python setup.py --version:

$ tree .
.
├── my_package
│   └── __init__.py
├── pyproject.toml
└── setup.cfg

1 directory, 3 files

$ cat setup.cfg
[metadata]
name = my_package
version = attr:my_package.__version__

[options]
packages = find:

$ cat my_package/__init__.py 
__version__ = "1.0.0"

$ cat pyproject.toml

$ python -c 'from setuptools.config import read_configuration as c; print(c("setup.cfg")["metadata"]["version"])'
1.0.0

$ python -c 'from configparser import ConfigParser; c = ConfigParser(); c.read("setup.cfg"); print(c["metadata"]["version"])'
attr:my_package.__version__

Answered By: alih