diff --git a/.gitignore b/.gitignore index dfa41c022..b1fd6887e 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ nosetests.xml coverage.xml *.cover tests/data/common_wheels/ +pip-wheel-metadata # Misc *~ diff --git a/docs/html/reference/pip.rst b/docs/html/reference/pip.rst index e3c0f44a4..17b94d728 100644 --- a/docs/html/reference/pip.rst +++ b/docs/html/reference/pip.rst @@ -145,26 +145,28 @@ explicitly manage the build environment. For such workflows, build isolation can be problematic. If this is the case, pip provides a ``--no-build-isolation`` flag to disable build isolation. Users supplying this flag are responsible for ensuring the build environment is managed -appropriately. +appropriately (including ensuring that all required build dependencies are +installed). -By default, pip will continue to use the legacy (``setuptools`` based) build -processing for projects that do not have a ``pyproject.toml`` file. Projects -with a ``pyproject.toml`` file will use a :pep:`517` backend. Projects with -a ``pyproject.toml`` file, but which don't have a ``build-system`` section, +By default, pip will continue to use the legacy (direct ``setup.py`` execution +based) build processing for projects that do not have a ``pyproject.toml`` file. +Projects with a ``pyproject.toml`` file will use a :pep:`517` backend. Projects +with a ``pyproject.toml`` file, but which don't have a ``build-system`` section, will be assumed to have the following backend settings:: [build-system] - requires = ["setuptools>=40.2.0", "wheel"] - build-backend = "setuptools.build_meta" + requires = ["setuptools>=40.8.0", "wheel"] + build-backend = "setuptools.build_meta:__legacy__" .. note:: - ``setuptools`` 40.2.0 is the first version of setuptools with full - :pep:`517` support. - -If a project has ``[build-system]``, but no ``build-backend``, pip will use -``setuptools.build_meta``, but will assume the project requirements include -``setuptools>=40.2.0`` and ``wheel`` (and will report an error if not). + ``setuptools`` 40.8.0 is the first version of setuptools that offers a + :pep:`517` backend that closely mimics directly executing ``setup.py``. + +If a project has ``[build-system]``, but no ``build-backend``, pip will also use +``setuptools.build_meta:__legacy__``, but will expect the project requirements +to include ``setuptools`` and ``wheel`` (and will report an error if the +installed version of ``setuptools`` is not recent enough). If a user wants to explicitly request :pep:`517` handling even though a project doesn't have a ``pyproject.toml`` file, this can be done using the diff --git a/news/6163.bugfix b/news/6163.bugfix new file mode 100644 index 000000000..cd171a637 --- /dev/null +++ b/news/6163.bugfix @@ -0,0 +1,5 @@ +The implicit default backend used for projects that provide a ``pyproject.toml`` +file without explicitly specifying ``build-backend`` now behaves more like direct +execution of ``setup.py``, and hence should restore compatibility with projects +that were unable to be installed with ``pip`` 19.0. This raised the minimum +required version of ``setuptools`` for such builds to 40.8.0. diff --git a/src/pip/_internal/pyproject.py b/src/pip/_internal/pyproject.py index 1de4b62a7..8d739a6c1 100644 --- a/src/pip/_internal/pyproject.py +++ b/src/pip/_internal/pyproject.py @@ -111,11 +111,13 @@ def load_pyproject_toml( # section, or the user has no pyproject.toml, but has opted in # explicitly via --use-pep517. # In the absence of any explicit backend specification, we - # assume the setuptools backend, and require wheel and a version - # of setuptools that supports that backend. + # assume the setuptools backend that most closely emulates the + # traditional direct setup.py execution, and require wheel and + # a version of setuptools that supports that backend. + build_system = { - "requires": ["setuptools>=40.2.0", "wheel"], - "build-backend": "setuptools.build_meta", + "requires": ["setuptools>=40.8.0", "wheel"], + "build-backend": "setuptools.build_meta:__legacy__", } # If we're using PEP 517, we have build system information (either @@ -154,7 +156,7 @@ def load_pyproject_toml( # If the user didn't specify a backend, we assume they want to use # the setuptools backend. But we can't be sure they have included # a version of setuptools which supplies the backend, or wheel - # (which is neede by the backend) in their requirements. So we + # (which is needed by the backend) in their requirements. So we # make a note to check that those requirements are present once # we have set up the environment. # This is quite a lot of work to check for a very specific case. But @@ -163,7 +165,7 @@ def load_pyproject_toml( # execute setup.py, but never considered needing to mention the build # tools themselves. The original PEP 518 code had a similar check (but # implemented in a different way). - backend = "setuptools.build_meta" - check = ["setuptools>=40.2.0", "wheel"] + backend = "setuptools.build_meta:__legacy__" + check = ["setuptools>=40.8.0", "wheel"] return (requires, backend, check) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 38a771d99..a42320bbb 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -62,7 +62,7 @@ def test_pep518_refuses_conflicting_requires(script, data): result.returncode != 0 and ('Some build dependencies for %s conflict with PEP 517/518 supported ' 'requirements: setuptools==1.0 is incompatible with ' - 'setuptools>=40.2.0.' % path_to_url(project_dir)) in result.stderr + 'setuptools>=40.8.0.' % path_to_url(project_dir)) in result.stderr ), str(result) diff --git a/tests/functional/test_pep517.py b/tests/functional/test_pep517.py index a1a45d27b..6235a16e4 100644 --- a/tests/functional/test_pep517.py +++ b/tests/functional/test_pep517.py @@ -123,3 +123,78 @@ def test_pep517_install_with_no_cache_dir(script, tmpdir, data): project_dir, ) result.assert_installed('project', editable=False) + + +def make_pyproject_with_setup(tmpdir, build_system=True, set_backend=True): + project_dir = (tmpdir / 'project').mkdir() + setup_script = ( + 'from setuptools import setup\n' + ) + expect_script_dir_on_path = True + if build_system: + buildsys = { + 'requires': ['setuptools', 'wheel'], + } + if set_backend: + buildsys['build-backend'] = 'setuptools.build_meta' + expect_script_dir_on_path = False + project_data = pytoml.dumps({'build-system': buildsys}) + else: + project_data = '' + + if expect_script_dir_on_path: + setup_script += ( + 'from pep517_test import __version__\n' + ) + else: + setup_script += ( + 'try:\n' + ' import pep517_test\n' + 'except ImportError:\n' + ' pass\n' + 'else:\n' + ' raise RuntimeError("Source dir incorrectly on sys.path")\n' + ) + + setup_script += ( + 'setup(name="pep517_test", version="0.1", packages=["pep517_test"])' + ) + + project_dir.join('pyproject.toml').write(project_data) + project_dir.join('setup.py').write(setup_script) + package_dir = (project_dir / "pep517_test").mkdir() + package_dir.join('__init__.py').write('__version__ = "0.1"') + return project_dir, "pep517_test" + + +def test_no_build_system_section(script, tmpdir, data, common_wheels): + """Check builds with setup.py, pyproject.toml, but no build-system section. + """ + project_dir, name = make_pyproject_with_setup(tmpdir, build_system=False) + result = script.pip( + 'install', '--no-cache-dir', '--no-index', '-f', common_wheels, + project_dir, + ) + result.assert_installed(name, editable=False) + + +def test_no_build_backend_entry(script, tmpdir, data, common_wheels): + """Check builds with setup.py, pyproject.toml, but no build-backend entry. + """ + project_dir, name = make_pyproject_with_setup(tmpdir, set_backend=False) + result = script.pip( + 'install', '--no-cache-dir', '--no-index', '-f', common_wheels, + project_dir, + ) + result.assert_installed(name, editable=False) + + +def test_explicit_setuptools_backend(script, tmpdir, data, common_wheels): + """Check builds with setup.py, pyproject.toml, and a build-backend entry. + """ + project_dir, name = make_pyproject_with_setup(tmpdir) + result = script.pip( + 'install', '--no-cache-dir', '--no-index', '-f', common_wheels, + project_dir, + ) + result.assert_installed(name, editable=False) diff --git a/tools/tests-common_wheels-requirements.txt b/tools/tests-common_wheels-requirements.txt index 0a8547bcc..6703d606c 100644 --- a/tools/tests-common_wheels-requirements.txt +++ b/tools/tests-common_wheels-requirements.txt @@ -1,2 +1,9 @@ -setuptools +# Create local setuptools wheel files for testing by: +# 1. Cloning setuptools and checking out the branch of interest +# 2. Running `python3 bootstrap.py` in that directory +# 3. Running `python3 -m pip wheel --no-cache -w /tmp/setuptools_build_meta_legacy/ .` +# 4. Replacing the `setuptools` entry below with a `file:///...` URL +# (Adjust artifact directory used based on preference and operating system) + +setuptools >= 40.8.0 wheel