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.cfg
are 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.cfg
directives (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
project
table. Instead,ini2toml
will rely ondynamic
, as explained below. Also note that for some fields (e.g.readme
),ini2toml
might try to automatically convert the directive into values accepted by PEP 621 (for complex scenariosdynamic
might 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: thewhere
option takes a list of strings (instead of a single directory) and the booleannamespaces
option is added (namespaces = true
is equivalent tofind_namespace:
andnamespaces = false
is 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.attribute
orclassifiers = file: classifiers.txt
), are listed asdynamic
under 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
,scripts
andgui-scripts
: while these 3 fields should be listed underproject.dynamic
, onlytool.setuptools.dynamic.entry-point
is allowed.scripts
andgui-scripts
should be directly derived from entry-points file.The
options.scripts
field is renamed toscript-files
and resides inside thetool.setuptools
table. This is done to avoid confusion with theproject.scripts
field defined by PEP 621.When not present in the original config file,
include_package_data
is explicitly added with theFalse
value to the translated TOML. This happens because insetup.cfg
the default value forinclue_package_data
isFalse
, but inpyproject.toml
the default value isTrue
. This change was mentioned by some members of the community as a nice quality of life improvement.The
metadata.license_files
field insetup.cfg
is not translated toproject.license.file
inpyproject.toml
, even when a single file is given. The reason behind this choice is thatproject.license.file
is meant to be used in a different way thanmetadata.license_files
when generating core metadata (the first is read and expanded into theLicense
core metadata field, the second is added as a path - relative to the project root - as theLicense-file
core metadata field). This might change in the future if PEP 639 is accepted. Meanwhile,metadata.license_files
is translated totool.setuptools.license-files
.