Build System Support#
What is it?#
Python packaging has come a long way.
setuptools way of packaging Python modules
setup() function within the
setup.py script. Commands such as
python setup.py bdist or
python setup.py bdist_wheel generate a
distribution bundle and
python setup.py install installs the distribution.
This interface makes it difficult to choose other packaging tools without an
setup.py scripts allow for arbitrary execution, it
is difficult to provide a reliable user experience across environments
PEP 517 came to the rescue and specified a new standard for packaging and distributing Python modules. Under PEP 517:
pyproject.tomlfile is used to specify which program to use to generate the distribution.
Two functions provided by the program,
build_sdist(directory: str), create the distribution bundle in the specified
The program may use its own configuration file or extend the
The actual installation is done with
pip install *.whlor
pip install *.tar.gz. If
pipwill go ahead and copy its files into the
site-packagesdirectory. If not,
pipwill look at
pyproject.tomland decide which program to use to ‘build from source’. (Note that if there is no
pyproject.tomlfile or the
build-backendparameter is not defined, then the fall-back behaviour is to use
With this standard, switching between packaging tools is a lot easier.
How to use it?#
Start with a package that you want to distribute. You will need your source
pyproject.toml file and a
~/meowpkg/ pyproject.toml setup.cfg meowpkg/ __init__.py module.py
pyproject.toml file specifies the build system (i.e. what is
being used to package your scripts and install from source). To use it with
setuptools the content would be:
[build-system] requires = ["setuptools"] build-backend = "setuptools.build_meta"
setuptools’ build system support.
setuptools package implements the
command and the
wheel package implements the
command; the latter is a dependency of the former
exposed via PEP 517 hooks.
setuptools’ declarative config to
specify the package information in
[metadata] name = meowpkg version = 0.0.1 description = a package that meows [options] packages = find:
Now generate the distribution. To build the package, use PyPA build:
$ pip install -q build $ python -m build
And now it’s done! The
.whl file and
.tar.gz can then be distributed
dist/ meowpkg-0.0.1.whl meowpkg-0.0.1.tar.gz $ pip install dist/meowpkg-0.0.1.whl
$ pip install dist/meowpkg-0.0.1.tar.gz
Dynamic build dependencies and other
With the changes introduced by PEP 517 and PEP 518, the
setup_requires configuration field was deprecated in
setup.py, in favour of directly listing build dependencies in the
requires field of the
build-system table of
This approach has a series of advantages and gives package managers and
installers the ability to inspect the build requirements in advance and
perform a series of optimisations.
However, some package authors might still need to dynamically inspect the final
user’s machine before deciding these requirements. One way of doing that, as
specified by PEP 517, is to “tweak”
setuptools.build_meta by using an
Before implementing an in-tree backend, have a look at PEP 508. Most of the time, dependencies with environment markers are enough to differentiate operating systems and platforms.
If you put the following configuration in your
[build-system] requires = ["setuptools"] build-backend = "backend" backend-path = ["_custom_build"]
then you can implement a thin wrapper around
_custom_build/backend.py file, as shown in the following example:
from setuptools import build_meta as _orig prepare_metadata_for_build_wheel = _orig.prepare_metadata_for_build_wheel build_wheel = _orig.build_wheel build_sdist = _orig.build_sdist def get_requires_for_build_wheel(self, config_settings=None): return _orig.get_requires_for_build_wheel(config_settings) + [...] def get_requires_for_build_sdist(self, config_settings=None): return _orig.get_requires_for_build_sdist(config_settings) + [...]
Note that you can override any of the functions specified in PEP 517, not only the ones responsible for gathering requirements.
Make sure your backend script is included in the source distribution, otherwise the build will fail. This can be done by using a SCM/VCS plugin (like setuptools-scm and setuptools-svn), or by correctly setting up MANIFEST.in.
.whl files are compressed archives that
can be inspected as follows:
On POSIX systems, this can be done with
tar -tf dist/*.tar.gz
unzip -l dist/*.whl.
On Windows systems, you can rename the
.zip to be able to
inspect it from File Explorer. You can also use the above
tar command in a
command prompt to inspect the
.tar.gz file. Alternatively, there are GUI programs
like 7-zip that handle
In general, the backend script should be present in the
.tar.gz (so the
project can be built from the source) but not in the
.whl (otherwise the
backend script would end up being distributed alongside your package).
See “Package Discovery and Namespace Packages” for more details about package