setup.py (legacy)#

Prior to the introduction of pyproject.toml-based builds (in PEP 517 and PEP 518), pip had only supported installing packages using setup.py files that were built using setuptools.

The interface documented here is retained currently solely for legacy purposes, until the migration to pyproject.toml-based builds can be completed.

Caution

The arguments and syntax of the various invocations of setup.py made by pip, are considered an implementation detail that is strongly coupled with setuptools. This build system interface is not meant to be used by any other build backend, which should be based on the pyproject.toml build system interface instead.

Further, projects should not expect to rely on there being any form of backward compatibility guarantees around the setup.py interface.

Build process#

The overall process for building a package is:

  • Generate the package’s metadata.

  • Generate a wheel for the package.

The wheel can then be used to perform an installation, if necessary.

Metadata Generation#

As a first step, pip needs to get metadata about a package (name, version, dependencies, and more). It collects this by calling setup.py egg_info.

The egg_info command generates the metadata for the package, which pip can then consume and proceed to gather all the dependencies of the package. Once the dependency resolution process is complete, pip will proceed to the next stage of the build process for these packages.

Wheel Generation#

When provided with a source distribution (or “sdist”) for a package, pip will attempt to build a wheel. Since wheel distributions can be cached, this can greatly speed up future installations for the package.

This is done by calling setup.py bdist_wheel which requires the wheel package to be installed.

If this wheel generation is successful (this can include compiling C/C++ code, depending on the package), the generated wheel is added to pip’s wheel cache and will be used for this installation. The built wheel is cached locally by pip to avoid repeated identical builds.

If this wheel generation fails, pip runs setup.py clean to clean up any build artifacts that may have been generated. After that, pip will attempt a direct installation.

Editable Installation#

For installing packages in “editable” mode (pip install --editable), pip will invoke setup.py develop, which will use setuptools’ mechanisms to perform an editable/development installation.

Setuptools Injection#

To support projects that directly use distutils, pip injects setuptools into sys.modules before invoking setup.py. This injection should be transparent to distutils-based projects.

Customising the build#

The --global-option and --build-option arguments to the pip install and pip wheel inject additional arguments into the setup.py command (--build-option is only available in pip wheel).

Attention

The use of --global-option and --build-option is highly setuptools specific, and is considered more an accident of the current implementation than a supported interface. It is documented here for completeness. These flags will not be supported, once this build system interface is dropped.

These arguments are included in the command as follows:

python setup.py <global_options> BUILD COMMAND <build_options>

The options are passed unmodified, and presently offer direct access to the distutils command line. For example:

$ python -m pip wheel --global-option bdist_ext --global-option -DFOO wheel
$ python -m pip wheel --global-option bdist_ext --global-option -DFOO wheel
C:> py -m pip wheel --global-option bdist_ext --global-option -DFOO wheel

will result in pip invoking:

setup.py bdist_ext -DFOO bdist_wheel -d TARGET

This passes a preprocessor symbol to the extension build.

Build Output#

Any output produced by the build system will be read by pip (for display to the user if requested). In order to correctly read the build system output, pip requires that the output is written in a well-defined encoding, specifically the encoding the user has configured for text output (which can be obtained in Python using locale.getpreferredencoding). If the configured encoding is ASCII, pip assumes UTF-8 (to account for the behaviour of some Unix systems).

Build systems should ensure that any tools they invoke (compilers, etc) produce output in the correct encoding. In practice - and in particular on Windows, where tools are inconsistent in their use of the “OEM” and “ANSI” codepages - this may not always be possible. pip will therefore attempt to recover cleanly if presented with incorrectly encoded build tool output, by translating unexpected byte sequences to Python-style hexadecimal escape sequences ("\x80\xff", etc). However, it is still possible for output to be displayed using an incorrect encoding (mojibake).