import pytest from pip._vendor import toml from pip._internal.build_env import BuildEnvironment from pip._internal.req import InstallRequirement from tests.lib import make_test_finder, path_to_url, windows_workaround_7667 def make_project(tmpdir, requires=None, backend=None, backend_path=None): requires = requires or [] project_dir = tmpdir / 'project' project_dir.mkdir() buildsys = {'requires': requires} if backend: buildsys['build-backend'] = backend if backend_path: buildsys['backend-path'] = backend_path data = toml.dumps({'build-system': buildsys}) project_dir.joinpath('pyproject.toml').write_text(data) return project_dir def test_backend(tmpdir, data): """Check we can call a requirement's backend successfully""" project_dir = make_project(tmpdir, backend="dummy_backend") req = InstallRequirement(None, None) req.source_dir = project_dir # make req believe it has been unpacked req.load_pyproject_toml() env = BuildEnvironment() finder = make_test_finder(find_links=[data.backends]) env.install_requirements(finder, ["dummy_backend"], 'normal', "Installing") conflicting, missing = env.check_requirements(["dummy_backend"]) assert not conflicting and not missing assert hasattr(req.pep517_backend, 'build_wheel') with env: assert req.pep517_backend.build_wheel("dir") == "Backend called" dummy_backend_code = """\ def build_wheel( wheel_directory, config_settings=None, metadata_directory=None ): return "Backend called" """ def test_backend_path(tmpdir, data): """Check we can call a backend inside the project""" project_dir = make_project( tmpdir, backend="dummy_backend", backend_path=['.'] ) (project_dir / 'dummy_backend.py').write_text(dummy_backend_code) req = InstallRequirement(None, None) req.source_dir = project_dir # make req believe it has been unpacked req.load_pyproject_toml() env = BuildEnvironment() assert hasattr(req.pep517_backend, 'build_wheel') with env: assert req.pep517_backend.build_wheel("dir") == "Backend called" def test_backend_path_and_dep(tmpdir, data): """Check we can call a requirement's backend successfully""" project_dir = make_project( tmpdir, backend="dummy_internal_backend", backend_path=['.'] ) (project_dir / 'dummy_internal_backend.py').write_text( "from dummy_backend import build_wheel" ) req = InstallRequirement(None, None) req.source_dir = project_dir # make req believe it has been unpacked req.load_pyproject_toml() env = BuildEnvironment() finder = make_test_finder(find_links=[data.backends]) env.install_requirements(finder, ["dummy_backend"], 'normal', "Installing") assert hasattr(req.pep517_backend, 'build_wheel') with env: assert req.pep517_backend.build_wheel("dir") == "Backend called" def test_pep517_install(script, tmpdir, data): """Check we can build with a custom backend""" project_dir = make_project( tmpdir, requires=['test_backend'], backend="test_backend" ) result = script.pip( 'install', '--no-index', '-f', data.backends, project_dir ) result.assert_installed('project', editable=False) def test_pep517_install_with_reqs(script, tmpdir, data): """Backend generated requirements are installed in the build env""" project_dir = make_project( tmpdir, requires=['test_backend'], backend="test_backend" ) project_dir.joinpath("backend_reqs.txt").write_text("simplewheel") result = script.pip( 'install', '--no-index', '-f', data.backends, '-f', data.packages, project_dir ) result.assert_installed('project', editable=False) def test_no_use_pep517_without_setup_py(script, tmpdir, data): """Using --no-use-pep517 requires setup.py""" project_dir = make_project( tmpdir, requires=['test_backend'], backend="test_backend" ) result = script.pip( 'install', '--no-index', '--no-use-pep517', '-f', data.backends, project_dir, expect_error=True ) assert 'project does not have a setup.py' in result.stderr def test_conflicting_pep517_backend_requirements(script, tmpdir, data): project_dir = make_project( tmpdir, requires=['test_backend', 'simplewheel==1.0'], backend="test_backend" ) project_dir.joinpath("backend_reqs.txt").write_text("simplewheel==2.0") result = script.pip( 'install', '--no-index', '-f', data.backends, '-f', data.packages, project_dir, expect_error=True ) msg = ( 'Some build dependencies for {url} conflict with the backend ' 'dependencies: simplewheel==1.0 is incompatible with ' 'simplewheel==2.0.'.format(url=path_to_url(project_dir))) assert ( result.returncode != 0 and msg in result.stderr ), str(result) def test_pep517_backend_requirements_already_satisfied(script, tmpdir, data): project_dir = make_project( tmpdir, requires=['test_backend', 'simplewheel==1.0'], backend="test_backend" ) project_dir.joinpath("backend_reqs.txt").write_text("simplewheel") result = script.pip( 'install', '--no-index', '-f', data.backends, '-f', data.packages, project_dir, ) assert 'Installing backend dependencies:' not in result.stdout def test_pep517_install_with_no_cache_dir(script, tmpdir, data): """Check builds with a custom backends work, even with no cache. """ project_dir = make_project( tmpdir, requires=['test_backend'], backend="test_backend" ) result = script.pip( 'install', '--no-cache-dir', '--no-index', '-f', data.backends, project_dir, ) result.assert_installed('project', editable=False) def make_pyproject_with_setup(tmpdir, build_system=True, set_backend=True): project_dir = tmpdir / 'project' project_dir.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 = toml.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.joinpath('pyproject.toml').write_text(project_data) project_dir.joinpath('setup.py').write_text(setup_script) package_dir = project_dir / "pep517_test" package_dir.mkdir() package_dir.joinpath('__init__.py').write_text('__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) @pytest.mark.network @windows_workaround_7667 def test_pep517_and_build_options(script, tmpdir, data, common_wheels): """Backend generated requirements are installed in the build env""" project_dir, name = make_pyproject_with_setup(tmpdir) result = script.pip( 'wheel', '--wheel-dir', tmpdir, '--build-option', 'foo', '-f', common_wheels, project_dir, expect_error=True ) assert 'Cannot build wheel' in result.stderr assert 'when --build-option is present' in result.stderr