mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
42359a9605
The pip-specific Path implementation has been removed, and all its usages replaced by pathlib.Path. The tmpdir and tmpdir_factory fixtures are also removed, and all usages are replaced by tmp_path and tmp_path_factory, which use pathlib.Path. The pip() function now also accepts pathlib.Path so we don't need to put str() everywhere. Path arguments are coerced with os.fspath() into str.
424 lines
13 KiB
Python
424 lines
13 KiB
Python
import os
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List, Optional, Tuple
|
|
|
|
import pytest
|
|
import tomli_w
|
|
|
|
from pip._internal.build_env import BuildEnvironment
|
|
from pip._internal.req import InstallRequirement
|
|
from tests.lib import (
|
|
PipTestEnvironment,
|
|
TestData,
|
|
create_basic_wheel_for_package,
|
|
make_test_finder,
|
|
)
|
|
|
|
|
|
def make_project(
|
|
tmpdir: Path,
|
|
requires: Optional[List[str]] = None,
|
|
backend: Optional[str] = None,
|
|
backend_path: Optional[List[str]] = None,
|
|
) -> Path:
|
|
requires = requires or []
|
|
project_dir = tmpdir / "project"
|
|
project_dir.mkdir()
|
|
buildsys: Dict[str, Any] = {"requires": requires}
|
|
if backend:
|
|
buildsys["build-backend"] = backend
|
|
if backend_path:
|
|
buildsys["backend-path"] = backend_path
|
|
data = tomli_w.dumps({"build-system": buildsys})
|
|
project_dir.joinpath("pyproject.toml").write_text(data)
|
|
return project_dir
|
|
|
|
|
|
def test_backend(tmpdir: Path, data: TestData) -> None:
|
|
"""Check we can call a requirement's backend successfully"""
|
|
project_dir = make_project(tmpdir, backend="dummy_backend")
|
|
req = InstallRequirement(None, None)
|
|
req.source_dir = os.fspath(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", kind="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 is not None
|
|
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: Path, data: TestData) -> None:
|
|
"""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 = os.fspath(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 is not None
|
|
assert req.pep517_backend.build_wheel("dir") == "Backend called"
|
|
|
|
|
|
def test_backend_path_and_dep(tmpdir: Path, data: TestData) -> None:
|
|
"""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 = os.fspath(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", kind="Installing")
|
|
|
|
assert hasattr(req.pep517_backend, "build_wheel")
|
|
with env:
|
|
assert req.pep517_backend is not None
|
|
assert req.pep517_backend.build_wheel("dir") == "Backend called"
|
|
|
|
|
|
def test_pep517_install(
|
|
script: PipTestEnvironment, tmpdir: Path, data: TestData
|
|
) -> None:
|
|
"""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: PipTestEnvironment, tmpdir: Path, data: TestData
|
|
) -> None:
|
|
"""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: PipTestEnvironment, tmpdir: Path, data: TestData
|
|
) -> None:
|
|
"""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: PipTestEnvironment, tmpdir: Path, data: TestData
|
|
) -> None:
|
|
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=project_dir.as_uri())
|
|
)
|
|
assert result.returncode != 0 and msg in result.stderr, str(result)
|
|
|
|
|
|
def test_no_check_build_deps(
|
|
script: PipTestEnvironment, tmpdir: Path, data: TestData
|
|
) -> None:
|
|
project_dir = make_project(
|
|
tmpdir, requires=["simplewheel==2.0"], backend="test_backend"
|
|
)
|
|
script.pip(
|
|
"install",
|
|
"simplewheel==1.0",
|
|
"test_backend",
|
|
"--no-index",
|
|
"-f",
|
|
data.packages,
|
|
"-f",
|
|
data.backends,
|
|
)
|
|
result = script.pip("install", "--no-build-isolation", project_dir)
|
|
result.assert_installed("project", editable=False)
|
|
|
|
|
|
def test_validate_missing_pep517_backend_requirements(
|
|
script: PipTestEnvironment, tmpdir: Path, data: TestData
|
|
) -> None:
|
|
project_dir = make_project(
|
|
tmpdir, requires=["test_backend", "simplewheel==1.0"], backend="test_backend"
|
|
)
|
|
result = script.pip(
|
|
"install",
|
|
"--no-index",
|
|
"-f",
|
|
data.backends,
|
|
"-f",
|
|
data.packages,
|
|
"--no-build-isolation",
|
|
"--check-build-dependencies",
|
|
project_dir,
|
|
expect_error=True,
|
|
)
|
|
msg = (
|
|
"Some build dependencies for {url} are missing: "
|
|
"'simplewheel==1.0', 'test_backend'.".format(url=project_dir.as_uri())
|
|
)
|
|
assert result.returncode != 0 and msg in result.stderr, str(result)
|
|
|
|
|
|
def test_validate_conflicting_pep517_backend_requirements(
|
|
script: PipTestEnvironment, tmpdir: Path, data: TestData
|
|
) -> None:
|
|
project_dir = make_project(
|
|
tmpdir, requires=["simplewheel==1.0"], backend="test_backend"
|
|
)
|
|
script.pip("install", "simplewheel==2.0", "--no-index", "-f", data.packages)
|
|
result = script.pip(
|
|
"install",
|
|
"--no-index",
|
|
"-f",
|
|
data.backends,
|
|
"-f",
|
|
data.packages,
|
|
"--no-build-isolation",
|
|
"--check-build-dependencies",
|
|
project_dir,
|
|
expect_error=True,
|
|
)
|
|
msg = (
|
|
"Some build dependencies for {url} conflict with the backend "
|
|
"dependencies: simplewheel==2.0 is incompatible with "
|
|
"simplewheel==1.0.".format(url=project_dir.as_uri())
|
|
)
|
|
assert result.returncode != 0 and msg in result.stderr, str(result)
|
|
|
|
|
|
def test_pep517_backend_requirements_satisfied_by_prerelease(
|
|
script: PipTestEnvironment,
|
|
data: TestData,
|
|
) -> None:
|
|
create_basic_wheel_for_package(script, "myreq", "1.0a1")
|
|
script.pip("install", "myreq==1.0a1", "--no-index", "-f", script.scratch_path)
|
|
script.pip("install", "test_backend", "--no-index", "-f", data.backends)
|
|
|
|
project_dir = make_project(
|
|
script.temp_path,
|
|
requires=["test_backend", "myreq"],
|
|
backend="test_backend",
|
|
)
|
|
project_dir.joinpath("backend_reqs.txt").write_text("myreq")
|
|
|
|
result = script.pip("install", "--no-index", "--no-build-isolation", project_dir)
|
|
assert "Installing backend dependencies:" not in result.stdout
|
|
|
|
|
|
def test_pep517_backend_requirements_already_satisfied(
|
|
script: PipTestEnvironment, tmpdir: Path, data: TestData
|
|
) -> None:
|
|
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: PipTestEnvironment, tmpdir: Path, data: TestData
|
|
) -> None:
|
|
"""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: Path, build_system: bool = True, set_backend: bool = True
|
|
) -> Tuple[Path, str]:
|
|
project_dir = tmpdir / "project"
|
|
project_dir.mkdir()
|
|
setup_script = "from setuptools import setup\n"
|
|
expect_script_dir_on_path = True
|
|
if build_system:
|
|
buildsys: Dict[str, Any] = {
|
|
"requires": ["setuptools", "wheel"],
|
|
}
|
|
if set_backend:
|
|
buildsys["build-backend"] = "setuptools.build_meta"
|
|
expect_script_dir_on_path = False
|
|
project_data = tomli_w.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: PipTestEnvironment, tmpdir: Path, data: TestData, common_wheels: Path
|
|
) -> None:
|
|
"""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: PipTestEnvironment, tmpdir: Path, data: TestData, common_wheels: Path
|
|
) -> None:
|
|
"""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: PipTestEnvironment, tmpdir: Path, data: TestData, common_wheels: Path
|
|
) -> None:
|
|
"""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
|
|
def test_pep517_and_build_options(
|
|
script: PipTestEnvironment, tmpdir: Path, data: TestData, common_wheels: Path
|
|
) -> None:
|
|
"""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,
|
|
allow_stderr_warning=True,
|
|
)
|
|
assert "Ignoring --build-option when building" in result.stderr
|
|
assert "using PEP 517" in result.stderr
|
|
|
|
|
|
@pytest.mark.network
|
|
def test_pep517_and_global_options(
|
|
script: PipTestEnvironment, tmpdir: Path, data: TestData, common_wheels: Path
|
|
) -> None:
|
|
"""Backend generated requirements are installed in the build env"""
|
|
project_dir, name = make_pyproject_with_setup(tmpdir)
|
|
result = script.pip(
|
|
"wheel",
|
|
"--wheel-dir",
|
|
tmpdir,
|
|
"--global-option",
|
|
"foo",
|
|
"-f",
|
|
common_wheels,
|
|
project_dir,
|
|
allow_stderr_warning=True,
|
|
)
|
|
assert "Ignoring --global-option when building" in result.stderr
|
|
assert "using PEP 517" in result.stderr
|