Setuptools and pyproject.tomlΒΆ
In the Python software ecosystem packaging and distributing software have historically been a difficult topic. Nevertheless, the community has been served well by setuptools as the de facto standard for creating distributable Python software packages.
In recent years, however, other needs and opportunities fostered the emergence
of some alternatives.
During this packaging renaissance, PEP 621 was proposed (and accepted)
to standardise package metadata/configuration done by developers, in a single
file format shared by all the packaging alternatives.
It makes use of a TOML file, pyproject.toml, which is, unfortunately,
significantly different from setuptools own original declarative configuration file,
setup.cfg, and therefore its adoption by setuptools requires mapping
concepts between these two files.
PEP 621 covers most of the information expected from a setup.cfg file,
but there are parameters specific to setuptools without an obvious equivalent.
The setuptools docs covers
the remaining options using the [tool.setuptools] TOML table.
Based on this description, the automatic translation proposed by ini2toml
works like the following:
Any field without an obvious equivalent in PEP 621 is stored in the
[tool.setuptools]TOML table, regardless if it comes from the[metadata]or[options]sections insetup.cfg.[options.*]sections insetup.cfgare translated to sub-tables of[tool.setuptools]inpyproject.toml. For example:[options.package_data] => [tool.setuptools.package-data]
Field and subtables in
[tool.setuptools]have the_character replaced by-in their keys, to follow the conventions set in PEP 517 and PEP 621.setup.cfgdirectives (e.g. fields starting withattr:orfile:) can be transformed into a (potentially inline) TOML table, for example:'file: description.rst' => {file = "description.rst"}
Note, however, that these directives are not allowed to be used directly under the
projecttable. Instead,ini2tomlwill rely ondynamic, as explained below. Also note that for some fields (e.g.readme),ini2tomlmight try to automatically convert the directive into values accepted by PEP 621 (for complex scenariosdynamicmight still be used).Instead of requiring a separated/dedicated section to specify parameters, the directives
find:andfind_namespace:just use a nested table:tool.setuptools.packages.find. Moreover, two quality of life improvements are added: thewhereoption takes a list of strings (instead of a single directory) and the booleannamespacesoption is added (namespaces = trueis equivalent tofind_namespace:andnamespaces = falseis equivalent tofind:). For example:# setup.cfg [options] package = find_namespace: [options.packages.find] where = src exclude = tests
# pyproject.toml [tool.setuptools.packages.find] where = ["src"] exclude = ["tests"] namespaces = true
Fields set up to be dynamically resolved by setuptools via directives, that cannot be directly represented by following PEP 621 (or other complementary standards) (e.g.
version = attr: module.attributeorclassifiers = file: classifiers.txt), are listed asdynamicunder the[project]table. The configurations for how setuptools fill those fields are stored under the[tool.setuptools.dynamic]table. For example:# setup.cfg [metadata] version = attr: module.attribute classifiers = file: classifiers.txt [options] entry_points = file: entry-points.txt
# pyproject.toml [project] dynamic = ["version", "classifiers", "entry-points", "scripts", "gui-scripts"] [tool.setuptools.dynamic] version = {attr = "module.attribute"} classifiers = {file = "classifiers.txt"} entry-points = {file = "entry-points.txt"}
There is a special case for dynamic
entry-points,scriptsandgui-scripts: while these 3 fields should be listed underproject.dynamic, onlytool.setuptools.dynamic.entry-pointis allowed.scriptsandgui-scriptsshould be directly derived from entry-points file.The
options.scriptsfield is renamed toscript-filesand resides inside thetool.setuptoolstable. This is done to avoid confusion with theproject.scriptsfield defined by PEP 621.When not present in the original config file,
include_package_datais explicitly added with theFalsevalue to the translated TOML. This happens because insetup.cfgthe default value forinclue_package_dataisFalse, but inpyproject.tomlthe default value isTrue. This change was mentioned by some members of the community as a nice quality of life improvement.The
metadata.license_filesfield insetup.cfgis not translated toproject.license.fileinpyproject.toml, even when a single file is given. The reason behind this choice is thatproject.license.fileis meant to be used in a different way thanmetadata.license_fileswhen generating core metadata (the first is read and expanded into theLicensecore metadata field, the second is added as a path - relative to the project root - as theLicense-filecore metadata field). This might change in the future if PEP 639 is accepted. Meanwhile,metadata.license_filesis translated totool.setuptools.license-files.