April 23, 2019·5 min read·engineeringpythonpackagingtooling

pip 19.1 makes build backend boundaries explicit

pip 19.1 clarifies when pyproject.toml selects PEP 517 processing and rejects operations that cannot preserve their requested meaning.

Adding a configuration file can change how a Python source tree builds. With pip 19.1, the presence of pyproject.toml, the presence of setup.py and an explicit backend declaration determine which build route pip can take. Editable installation exposes the consequences because the PEP 517 route in this release does not support that operation.

The pip changelog dates 19.1 to April 23, 2019. Its release-tagged notes list errors for unsupported editable installations and for passing build options to PEP 517 builds. Those details matter more than a general claim that packaging support has improved. They describe which requests now stop instead of proceeding with an interpretation the caller might not expect.

I find Python's packaging complexity frustrating precisely because a small change to project metadata can have consequences beyond the file itself. A build tool needs to explain those consequences at the point of failure. This release makes several boundaries explicit, although the underlying choice of build path still requires understanding more than the install command alone.

A file can select the build path

The 19.1 implementation of pyproject handling separates the cases. A project with pyproject.toml and no setup.py must use PEP 517. A project that declares build-backend in its build-system table must also use PEP 517. An explicit request to disable that processing is invalid in either case.

These requirements follow from what the source tree supplies. Without setup.py, pip has no legacy setup script to execute. With a declared backend, the project has supplied an explicit build interface. Falling back to direct execution would disregard that declaration. The errors preserve the project's stated build arrangement.

There is a less obvious default when a tree has both files but no backend declaration. If the user has not expressed a preference, pip selects PEP 517 when pyproject.toml exists. The implementation therefore distinguishes a forced route from a default route. That distinction determines whether opting out is meaningful.

For editable installation, this default becomes visible as an error with a specific explanation. If setup.py exists and no backend is declared, the message says the user may pass --no-use-pep517 to opt out. The escape applies to that particular case. It cannot supply a missing setup script or cancel an explicit backend declaration.

Editable mode does not imply a backend capability

The release notes say that pip now reports an informative error for an editable installation of a PEP 517 source tree. The implementation checks editable mode while resolving the build system. It explains why that tree is being processed through PEP 517, including whether the cause is a missing setup script, a backend declaration or an explicit user request.

That explanation is operationally useful. Two directories can contain the same pair of filenames but differ in whether the backend is mandatory. A generic message that editable installation failed would conceal the decision that led there. Naming the reason gives the maintainer something concrete to examine.

For a project that has a setup script and no declared backend, the documented opt-out can be expressed as follows.

python -m pip install --no-use-pep517 --editable .

This command depends on the source tree meeting those conditions. It does not add editable support to a backend. It asks pip to take the available legacy route for a project where that route remains valid. Treating the flag as a universal workaround would discard the distinction the resolver makes.

The tradeoff is that a source tree's normal installation and editable installation can require different reasoning. A successful ordinary build does not establish that the same build interface can represent an editable install. In 19.1, pip chooses an explicit failure when the requested operation cannot be performed through the selected interface.

Build requirements and backend choice are separate

The resolver also validates the build-system table. If that table exists, it must contain requires, and the value must be a list of strings. A missing table differs from a malformed one. The implementation can provide defaults for the former while rejecting the latter.

When no build-system table supplies the needed information, pip's PEP 517 route assumes the setuptools legacy backend and requirements including setuptools>=40.8.0 and wheel. The backend is named setuptools.build_meta:__legacy__. Those exact defaults appear in the release source, so they are part of the version-specific behavior rather than a recommendation about whatever setuptools version happens to be current.

If the table exists but omits a backend, pip again assumes that legacy backend. It separately records requirements to check after setting up the build environment. The source comments explain the compatibility problem. Some projects adopted build requirements without naming the build tools themselves, and pip needs to check that the assumed backend is available.

This illustrates why merely reading dependency declarations does not fully explain the build. Declared build requirements, the selected backend and the install mode each contribute to the eventual operation. pip 19.1 contains explicit handling for their combinations instead of treating every pyproject.toml as an identical instruction.

Options should preserve their meaning

The release also changes --build-options handling for PEP 517 builds. It now fails instead of silently ignoring the option. That is a useful correction because a successful command with an ignored build option can produce an artifact under different assumptions from those the caller requested.

Another change separates wheel construction from the persistent cache. Passing --no-cache-dir no longer changes whether wheels will be built. pip uses a temporary directory when necessary. Cache policy consequently stops controlling an unrelated build decision, which reduces the number of behaviors hidden behind that flag.

I prefer these failures and separations because they make an installation command more interpretable. An unsupported option should produce an error, and disabling a cache should have consequences related to storage. Neither change removes the complexity of the packaging model, but both reduce uncertainty about which parts of the request pip honored.

The practical consequence of 19.1 is a clearer contract at several points where source metadata meets a requested operation. Projects with mandatory PEP 517 processing receive explicit errors for unsupported editable mode. Projects with an available legacy route get a reasoned opt-out message, while build options that cannot be honored no longer disappear silently.