Commit 735235a0 by Randy Döring Committed by GitHub

installer: remove old installer and config setting `experimental.new-installer` (#7356)

parent 6b4b5391
......@@ -555,7 +555,7 @@ $ poetry cache clear pypi --all
- Added in info output to `poetry lock --check` ([#5081](https://github.com/python-poetry/poetry/pull/5081)).
- Added new argument `--all` for `poetry env remove` to delete all venv of a project at once ([#3212](https://github.com/python-poetry/poetry/pull/3212)).
- Added new argument `--without-urls` for `poetry export` to exclude source repository urls from the exported file ([#4763](https://github.com/python-poetry/poetry/pull/4763)).
- Added a `new installer.max-workers` property to the configuration ([#3516](https://github.com/python-poetry/poetry/pull/3516)).
- Added a new `installer.max-workers` property to the configuration ([#3516](https://github.com/python-poetry/poetry/pull/3516)).
- Added experimental option `virtualenvs.prefer-active-python` to detect current activated python ([#4852](https://github.com/python-poetry/poetry/pull/4852)).
- Added better windows shell support ([#5053](https://github.com/python-poetry/poetry/pull/5053)).
......
......@@ -229,9 +229,6 @@ specific packages.
| `package[,package,..]` | Disallow binary distributions for specified packages only. |
{{% note %}}
This configuration is only respected when using the new installer. If you have disabled it please
consider re-enabling it.
As with all configurations described here, this is a user specific configuration. This means that this
is not taken into consideration when a lockfile is generated or dependencies are resolved. This is
applied only when selecting which distribution for dependency should be installed into a Poetry managed
......
......@@ -124,7 +124,6 @@ class Config:
"prompt": "{project_name}-py{python_version}",
},
"experimental": {
"new-installer": True,
"system-git-client": False,
},
"installer": {
......@@ -276,7 +275,6 @@ class Config:
"virtualenvs.options.always-copy",
"virtualenvs.options.system-site-packages",
"virtualenvs.options.prefer-active-python",
"experimental.new-installer",
"experimental.system-git-client",
"installer.modern-installation",
"installer.parallel",
......
......@@ -335,10 +335,6 @@ class Application(BaseApplication):
poetry.config,
disable_cache=poetry.disable_cache,
)
use_executor = poetry.config.get("experimental.new-installer", False)
if not use_executor:
# only set if false because the method is deprecated
installer.use_executor(False)
command.set_installer(installer)
def _load_plugins(self, io: IO | None = None) -> None:
......
......@@ -68,7 +68,6 @@ To remove a repository (repo is a short alias for repositories):
),
"virtualenvs.path": (str, lambda val: str(Path(val))),
"virtualenvs.prefer-active-python": (boolean_validator, boolean_normalizer),
"experimental.new-installer": (boolean_validator, boolean_normalizer),
"experimental.system-git-client": (boolean_validator, boolean_normalizer),
"installer.modern-installation": (boolean_validator, boolean_normalizer),
"installer.parallel": (boolean_validator, boolean_normalizer),
......
......@@ -105,11 +105,6 @@ dependencies and not including the current project, run the command with the
from poetry.masonry.builders.editable import EditableBuilder
use_executor = self.poetry.config.get("experimental.new-installer", False)
if not use_executor:
# only set if false because the method is deprecated
self.installer.use_executor(False)
if self.option("extras") and self.option("all-extras"):
self.line_error(
"<error>You cannot specify explicit"
......
......@@ -35,11 +35,6 @@ file.
loggers = ["poetry.repositories.pypi_repository"]
def handle(self) -> int:
use_executor = self.poetry.config.get("experimental.new-installer", False)
if not use_executor:
# only set if false because the method is deprecated
self.installer.use_executor(False)
if self.option("check"):
if self.poetry.locker.is_locked() and self.poetry.locker.is_fresh():
self.line("poetry.lock is consistent with pyproject.toml.")
......
......@@ -40,12 +40,6 @@ class UpdateCommand(InstallerCommand):
def handle(self) -> int:
packages = self.argument("packages")
use_executor = self.poetry.config.get("experimental.new-installer", False)
if not use_executor:
# only set if false because the method is deprecated
self.installer.use_executor(False)
if packages:
self.installer.whitelist({name: "*" for name in packages})
......
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from poetry.core.packages.package import Package
class BaseInstaller:
def install(self, package: Package) -> None:
raise NotImplementedError
def update(self, source: Package, target: Package) -> None:
raise NotImplementedError
def remove(self, package: Package) -> None:
raise NotImplementedError
from __future__ import annotations
import warnings
from typing import TYPE_CHECKING
from cleo.io.null_io import NullIO
......@@ -11,13 +9,11 @@ from poetry.installation.executor import Executor
from poetry.installation.operations import Install
from poetry.installation.operations import Uninstall
from poetry.installation.operations import Update
from poetry.installation.pip_installer import PipInstaller
from poetry.repositories import Repository
from poetry.repositories import RepositoryPool
from poetry.repositories.installed_repository import InstalledRepository
from poetry.repositories.lockfile_repository import LockfileRepository
from poetry.utils.extras import get_extra_package_names
from poetry.utils.helpers import pluralize
if TYPE_CHECKING:
......@@ -28,7 +24,6 @@ if TYPE_CHECKING:
from poetry.core.packages.project_package import ProjectPackage
from poetry.config.config import Config
from poetry.installation.base_installer import BaseInstaller
from poetry.installation.operations.operation import Operation
from poetry.packages import Locker
from poetry.utils.env import Env
......@@ -74,9 +69,7 @@ class Installer:
)
self._executor = executor
self._use_executor = True
self._installer = self._get_installer()
if installed is None:
installed = self._get_installed()
......@@ -86,10 +79,6 @@ class Installer:
def executor(self) -> Executor:
return self._executor
@property
def installer(self) -> BaseInstaller:
return self._installer
def set_package(self, package: ProjectPackage) -> Installer:
self._package = package
......@@ -187,19 +176,6 @@ class Installer:
return self
def use_executor(self, use_executor: bool = True) -> Installer:
warnings.warn(
(
"Calling use_executor() is deprecated since it's true by default now"
" and deactivating it will be removed in a future release."
),
DeprecationWarning,
stacklevel=2,
)
self._use_executor = use_executor
return self
def _do_refresh(self) -> int:
from poetry.puzzle.solver import Solver
......@@ -384,127 +360,7 @@ class Installer:
self._io.write_line("<info>Writing lock file</>")
def _execute(self, operations: list[Operation]) -> int:
if self._use_executor:
return self._executor.execute(operations)
self._io.write_error(
"<warning>"
"Setting `experimental.new-installer` to false is deprecated and"
" slated for removal in an upcoming minor release.\n"
"(Despite of the setting's name the new installer is not experimental!)"
"</warning>"
)
if not operations and (self._execute_operations or self._dry_run):
self._io.write_line("No dependencies to install or update")
if operations and (self._execute_operations or self._dry_run):
installs = 0
updates = 0
uninstalls = 0
skipped = 0
for op in operations:
if op.skipped:
skipped += 1
elif op.job_type == "install":
installs += 1
elif op.job_type == "update":
updates += 1
elif op.job_type == "uninstall":
uninstalls += 1
self._io.write_line("")
self._io.write("Package operations: ")
self._io.write(f"<info>{installs}</> install{pluralize(installs)}, ")
self._io.write(f"<info>{updates}</> update{pluralize(updates)}, ")
self._io.write(f"<info>{uninstalls}</> removal{pluralize(uninstalls)}")
if skipped and self.is_verbose():
self._io.write(f", <info>{skipped}</> skipped")
self._io.write_line("")
self._io.write_line("")
for op in operations:
self._execute_operation(op)
return 0
def _execute_operation(self, operation: Operation) -> None:
"""
Execute a given operation.
"""
method = operation.job_type
getattr(self, f"_execute_{method}")(operation)
def _execute_install(self, operation: Install) -> None:
target = operation.package
if operation.skipped:
if self.is_verbose() and (self._execute_operations or self.is_dry_run()):
self._io.write_line(
f" - Skipping <c1>{target.pretty_name}</c1>"
f" (<c2>{target.full_pretty_version}</c2>) {operation.skip_reason}"
)
return
if self._execute_operations or self.is_dry_run():
self._io.write_line(
f" - Installing <c1>{target.pretty_name}</c1>"
f" (<c2>{target.full_pretty_version}</c2>)"
)
if not self._execute_operations:
return
self._installer.install(operation.package)
def _execute_update(self, operation: Update) -> None:
source = operation.initial_package
target = operation.target_package
if operation.skipped:
if self.is_verbose() and (self._execute_operations or self.is_dry_run()):
self._io.write_line(
f" - Skipping <c1>{target.pretty_name}</c1> "
f"(<c2>{target.full_pretty_version}</c2>) {operation.skip_reason}"
)
return
if self._execute_operations or self.is_dry_run():
self._io.write_line(
f" - Updating <c1>{target.pretty_name}</c1>"
f" (<c2>{source.full_pretty_version}</c2> ->"
f" <c2>{target.full_pretty_version}</c2>)"
)
if not self._execute_operations:
return
self._installer.update(source, target)
def _execute_uninstall(self, operation: Uninstall) -> None:
target = operation.package
if operation.skipped:
if self.is_verbose() and (self._execute_operations or self.is_dry_run()):
self._io.write_line(
f" - Not removing <c1>{target.pretty_name}</c1>"
f" (<c2>{target.pretty_version}</c2>) {operation.skip_reason}"
)
return
if self._execute_operations or self.is_dry_run():
self._io.write_line(
f" - Removing <c1>{target.pretty_name}</c1>"
f" (<c2>{target.pretty_version}</c2>)"
)
if not self._execute_operations:
return
self._installer.remove(operation.package)
return self._executor.execute(operations)
def _populate_lockfile_repo(
self, repo: LockfileRepository, ops: Iterable[Operation]
......@@ -588,8 +444,5 @@ class Installer:
return get_extra_package_names(repo.packages, extras, self._extras)
def _get_installer(self) -> BaseInstaller:
return PipInstaller(self._env, self._io, self._pool)
def _get_installed(self) -> InstalledRepository:
return InstalledRepository.load(self._env)
from __future__ import annotations
from typing import TYPE_CHECKING
from poetry.installation.base_installer import BaseInstaller
if TYPE_CHECKING:
from poetry.core.packages.package import Package
class NoopInstaller(BaseInstaller):
def __init__(self) -> None:
self._installs: list[Package] = []
self._updates: list[tuple[Package, Package]] = []
self._removals: list[Package] = []
@property
def installs(self) -> list[Package]:
return self._installs
@property
def updates(self) -> list[tuple[Package, Package]]:
return self._updates
@property
def removals(self) -> list[Package]:
return self._removals
def install(self, package: Package) -> None:
self._installs.append(package)
def update(self, source: Package, target: Package) -> None:
self._updates.append((source, target))
def remove(self, package: Package) -> None:
self._removals.append(package)
from __future__ import annotations
import contextlib
import os
import tempfile
import urllib.parse
from pathlib import Path
from subprocess import CalledProcessError
from typing import TYPE_CHECKING
from typing import Any
from poetry.core.constraints.version import Version
from poetry.installation.base_installer import BaseInstaller
from poetry.pyproject.toml import PyProjectTOML
from poetry.repositories.http_repository import HTTPRepository
from poetry.utils._compat import encode
from poetry.utils.helpers import remove_directory
from poetry.utils.pip import pip_install
if TYPE_CHECKING:
from cleo.io.io import IO
from poetry.core.masonry.builders.builder import Builder
from poetry.core.packages.package import Package
from poetry.repositories.repository_pool import RepositoryPool
from poetry.utils.env import Env
class PipInstaller(BaseInstaller):
def __init__(self, env: Env, io: IO, pool: RepositoryPool) -> None:
self._env = env
self._io = io
self._pool = pool
def install(self, package: Package, update: bool = False) -> None:
if package.source_type == "directory":
self.install_directory(package)
return
if package.source_type == "git":
self.install_git(package)
return
args = ["install", "--no-deps", "--no-input"]
if not package.is_direct_origin() and package.source_url:
assert package.source_reference is not None
repository = self._pool.repository(package.source_reference)
parsed = urllib.parse.urlparse(package.source_url)
if parsed.scheme == "http":
assert parsed.hostname is not None
self._io.write_error(
" <warning>Installing from unsecure host:"
f" {parsed.hostname}</warning>"
)
args += ["--trusted-host", parsed.hostname]
if isinstance(repository, HTTPRepository):
certificates = repository.certificates
if certificates.cert:
args += ["--cert", str(certificates.cert)]
if parsed.scheme == "https" and not certificates.verify:
assert parsed.hostname is not None
args += ["--trusted-host", parsed.hostname]
if certificates.client_cert:
args += ["--client-cert", str(certificates.client_cert)]
index_url = repository.authenticated_url
args += ["--index-url", index_url]
if (
self._pool.has_default()
and repository.name != self._pool.repositories[0].name
):
first_repository = self._pool.repositories[0]
if isinstance(first_repository, HTTPRepository):
args += [
"--extra-index-url",
first_repository.authenticated_url,
]
if update:
args.append("-U")
req: str | list[str]
if package.files and not package.source_url:
# Format as a requirements.txt
# We need to create a requirements.txt file
# for each package in order to check hashes.
# This is far from optimal but we do not have any
# other choice since this is the only way for pip
# to verify hashes.
req = self.create_temporary_requirement(package)
args += ["-r", req]
try:
self.run(*args)
finally:
os.unlink(req)
else:
req = self.requirement(package)
if not isinstance(req, list):
args.append(req)
else:
args += req
self.run(*args)
def update(self, package: Package, target: Package) -> None:
if package.source_type != target.source_type:
# If the source type has changed, we remove the current
# package to avoid perpetual updates in some cases
self.remove(package)
self.install(target, update=True)
def remove(self, package: Package) -> None:
try:
self.run("uninstall", package.name, "-y")
except CalledProcessError as e:
if "not installed" in str(e):
return
raise
# This is a workaround for https://github.com/pypa/pip/issues/4176
for nspkg_pth_file in self._env.site_packages.find_distribution_nspkg_pth_files(
distribution_name=package.name
):
nspkg_pth_file.unlink()
# If we have a VCS package, remove its source directory
if package.source_type == "git":
src_dir = self._env.path / "src" / package.name
if src_dir.exists():
remove_directory(src_dir, force=True)
def run(self, *args: Any, **kwargs: Any) -> int | str:
return self._env.run_pip(*args, **kwargs)
def requirement(self, package: Package, formatted: bool = False) -> str | list[str]:
if formatted and not package.source_type:
req = f"{package.name}=={package.version}"
for f in package.files:
hash_type = "sha256"
h = f["hash"]
if ":" in h:
hash_type, h = h.split(":")
req += f" --hash {hash_type}:{h}"
req += "\n"
return req
if package.source_type in ["file", "directory"]:
assert package.source_url is not None
if package.root_dir:
req = (package.root_dir / package.source_url).as_posix()
else:
req = os.path.realpath(package.source_url)
if package.develop and package.source_type == "directory":
return ["-e", req]
return req
if package.source_type == "git":
req = (
f"git+{package.source_url}@{package.source_reference}"
f"#egg={package.name}"
)
if package.source_subdirectory:
req += f"&subdirectory={package.source_subdirectory}"
if package.develop:
return ["-e", req]
return req
if package.source_type == "url":
return f"{package.source_url}#egg={package.name}"
return f"{package.name}=={package.version}"
def create_temporary_requirement(self, package: Package) -> str:
fd, name = tempfile.mkstemp("reqs.txt", f"{package.name}-{package.version}")
req = self.requirement(package, formatted=True)
if isinstance(req, list):
req = " ".join(req)
try:
os.write(fd, encode(req))
finally:
os.close(fd)
return name
def install_directory(self, package: Package) -> str | int:
from cleo.io.null_io import NullIO
from poetry.factory import Factory
assert package.source_url is not None
if package.root_dir:
req = package.root_dir / package.source_url
else:
req = Path(package.source_url).resolve(strict=False)
if package.source_subdirectory:
req /= package.source_subdirectory
pyproject = PyProjectTOML(req / "pyproject.toml")
package_poetry = None
if pyproject.is_poetry_project():
with contextlib.suppress(RuntimeError):
package_poetry = Factory().create_poetry(pyproject.file.path.parent)
if package_poetry is not None:
# Even if there is a build system specified
# some versions of pip (< 19.0.0) don't understand it
# so we need to check the version of pip to know
# if we can rely on the build system
legacy_pip = self._env.pip_version < Version.from_parts(19, 0, 0)
builder: Builder
if package.develop and not package_poetry.package.build_script:
from poetry.masonry.builders.editable import EditableBuilder
# This is a Poetry package in editable mode
# we can use the EditableBuilder without going through pip
# to install it, unless it has a build script.
builder = EditableBuilder(package_poetry, self._env, NullIO())
builder.build()
return 0
elif legacy_pip or package_poetry.package.build_script:
from poetry.core.masonry.builders.sdist import SdistBuilder
# We need to rely on creating a temporary setup.py
# file since the version of pip does not support
# build-systems
# We also need it for non-PEP-517 packages
builder = SdistBuilder(package_poetry)
with builder.setup_py():
return pip_install(
path=req,
environment=self._env,
upgrade=True,
editable=package.develop,
)
return pip_install(
path=req, environment=self._env, upgrade=True, editable=package.develop
)
def install_git(self, package: Package) -> None:
from poetry.core.packages.package import Package
from poetry.vcs.git import Git
assert package.source_url is not None
source = Git.clone(
url=package.source_url,
source_root=self._env.path / "src",
revision=package.source_resolved_reference or package.source_reference,
)
# Now we just need to install from the source directory
pkg = Package(
name=package.name,
version=package.version,
source_type="directory",
source_url=str(source.path),
source_subdirectory=package.source_subdirectory,
develop=package.develop,
)
self.install_directory(pkg)
......@@ -22,7 +22,6 @@ if TYPE_CHECKING:
from cleo.testers.command_tester import CommandTester
from pytest_mock import MockerFixture
from poetry.installation.noop_installer import NoopInstaller
from poetry.poetry import Poetry
from poetry.utils.env import MockEnv
from poetry.utils.env import VirtualEnv
......@@ -65,13 +64,6 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("add")
@pytest.fixture()
def old_tester(tester: CommandTester) -> CommandTester:
tester.command.installer._use_executor = False
return tester
def test_add_no_constraint(
app: PoetryTestApplication, repo: TestRepository, tester: CommandTester
) -> None:
......@@ -804,7 +796,6 @@ def test_add_constraint_with_platform(
) -> None:
platform = sys.platform
env._platform = platform
env._marker_env = None
cachy2 = get_package("cachy", "0.2.0")
......@@ -1223,16 +1214,15 @@ Writing lock file
assert content_hash != app.poetry.locker.lock_data["metadata"]["content-hash"]
def test_add_no_constraint_old_installer(
def test_add_to_section_that_does_no_exist_yet(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
tester: CommandTester,
) -> None:
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0"))
old_tester.execute("cachy")
tester.execute("cachy --group dev")
expected = """\
Using version ^0.2.0 for cachy
......@@ -1242,938 +1232,43 @@ Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0)
Installing cachy (0.2.0)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert tester.io.fetch_output() == expected
assert len(installer.installs) == 1
assert tester.command.installer.executor.installations_count == 1
content = app.poetry.file.read()["tool"]["poetry"]
assert "cachy" in content["dependencies"]
assert content["dependencies"]["cachy"] == "^0.2.0"
def test_add_equal_constraint_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0"))
old_tester.execute("cachy==0.1.0")
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.1.0)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 1
assert "cachy" in content["group"]["dev"]["dependencies"]
assert content["group"]["dev"]["dependencies"]["cachy"] == "^0.2.0"
def test_add_greater_constraint_old_installer(
def test_add_preferes_stable_releases(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
tester: CommandTester,
) -> None:
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0"))
repo.add_package(get_package("foo", "1.2.3"))
repo.add_package(get_package("foo", "1.2.4b1"))
old_tester.execute("cachy>=0.1.0")
tester.execute("foo")
expected = """\
Using version ^1.2.3 for foo
Updating dependencies
Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 1
@pytest.mark.parametrize("extra_name", ["msgpack", "MsgPack"])
def test_add_constraint_with_extras_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
extra_name: str,
) -> None:
cachy1 = get_package("cachy", "0.1.0")
cachy1.extras = {"msgpack": [get_dependency("msgpack-python")]}
msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True)
cachy1.add_dependency(msgpack_dep)
repo.add_package(get_package("cachy", "0.2.0"))
repo.add_package(cachy1)
repo.add_package(get_package("msgpack-python", "0.5.3"))
old_tester.execute(f"cachy[{extra_name}]>=0.1.0,<0.2.0")
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 2 installs, 0 updates, 0 removals
- Installing msgpack-python (0.5.3)
- Installing cachy (0.1.0)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 2
def test_add_constraint_dependencies_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
cachy2 = get_package("cachy", "0.2.0")
msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6")
cachy2.add_dependency(msgpack_dep)
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2)
repo.add_package(get_package("msgpack-python", "0.5.3"))
old_tester.execute("cachy=0.2.0")
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 2 installs, 0 updates, 0 removals
- Installing msgpack-python (0.5.3)
- Installing cachy (0.2.0)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 2
def test_add_git_constraint_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
old_tester.execute("git+https://github.com/demo/demo.git")
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4)
- Installing demo (0.1.2 9cf87a2)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 2
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"git": "https://github.com/demo/demo.git"
}
def test_add_git_constraint_with_poetry_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("pendulum", "1.4.4"))
old_tester.execute("git+https://github.com/demo/pyproject-demo.git")
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4)
- Installing demo (0.1.2 9cf87a2)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 2
@pytest.mark.parametrize("extra_name", ["foo", "FOO"])
def test_add_git_constraint_with_extras_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
extra_name: str,
) -> None:
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
repo.add_package(get_package("tomlkit", "0.5.5"))
old_tester.execute(f"git+https://github.com/demo/demo.git[{extra_name},bar]")
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 4 installs, 0 updates, 0 removals
- Installing cleo (0.6.5)
- Installing pendulum (1.4.4)
- Installing tomlkit (0.5.5)
- Installing demo (0.1.2 9cf87a2)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 4
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"git": "https://github.com/demo/demo.git",
"extras": [extra_name, "bar"],
}
def test_add_git_ssh_constraint_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
old_tester.execute("git+ssh://git@github.com/demo/demo.git@develop")
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4)
- Installing demo (0.1.2 9cf87a2)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 2
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"git": "ssh://git@github.com/demo/demo.git",
"rev": "develop",
}
@pytest.mark.parametrize(
"required_fixtures",
[["git/github.com/demo/demo"]],
)
def test_add_directory_constraint_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
path = "../git/github.com/demo/demo"
old_tester.execute(f"{path}")
expected = f"""\
Updating dependencies
Resolving dependencies...
Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4)
- Installing demo (0.1.2 {app.poetry.file.parent.joinpath(path).resolve().as_posix()})
Writing lock file
• Installing foo (1.2.3)
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 2
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {"path": path}
@pytest.mark.parametrize(
"required_fixtures",
[["git/github.com/demo/pyproject-demo"]],
)
def test_add_directory_with_poetry_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("pendulum", "1.4.4"))
path = "../git/github.com/demo/pyproject-demo"
old_tester.execute(f"{path}")
expected = f"""\
Updating dependencies
Resolving dependencies...
Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4)
- Installing demo (0.1.2 {app.poetry.file.parent.joinpath(path).resolve().as_posix()})
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 2
@pytest.mark.parametrize(
"required_fixtures",
[["distributions/demo-0.1.0-py2.py3-none-any.whl"]],
)
def test_add_file_constraint_wheel_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("pendulum", "1.4.4"))
path = "../distributions/demo-0.1.0-py2.py3-none-any.whl"
old_tester.execute(f"{path}")
expected = f"""\
Updating dependencies
Resolving dependencies...
Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4)
- Installing demo (0.1.0 {app.poetry.file.parent.joinpath(path).resolve().as_posix()})
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 2
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {"path": path}
@pytest.mark.parametrize(
"required_fixtures",
[["distributions/demo-0.1.0.tar.gz"]],
)
def test_add_file_constraint_sdist_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("pendulum", "1.4.4"))
path = "../distributions/demo-0.1.0.tar.gz"
old_tester.execute(f"{path}")
expected = f"""\
Updating dependencies
Resolving dependencies...
Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4)
- Installing demo (0.1.0 {app.poetry.file.parent.joinpath(path).resolve().as_posix()})
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 2
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {"path": path}
@pytest.mark.parametrize("extra_name", ["msgpack", "MsgPack"])
def test_add_constraint_with_extras_option_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
extra_name: str,
) -> None:
cachy2 = get_package("cachy", "0.2.0")
cachy2.extras = {"msgpack": [get_dependency("msgpack-python")]}
msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True)
cachy2.add_dependency(msgpack_dep)
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2)
repo.add_package(get_package("msgpack-python", "0.5.3"))
old_tester.execute(f"cachy=0.2.0 --extras {extra_name}")
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 2 installs, 0 updates, 0 removals
- Installing msgpack-python (0.5.3)
- Installing cachy (0.2.0)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 2
content = app.poetry.file.read()["tool"]["poetry"]
assert "cachy" in content["dependencies"]
assert content["dependencies"]["cachy"] == {
"version": "0.2.0",
"extras": [extra_name],
}
def test_add_url_constraint_wheel_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
mocker: MockerFixture,
old_tester: CommandTester,
) -> None:
p = mocker.patch("pathlib.Path.cwd")
p.return_value = Path(__file__) / ".."
repo.add_package(get_package("pendulum", "1.4.4"))
old_tester.execute(
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
)
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4)
- Installing demo\
(0.1.0 https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 2
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"url": "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
}
@pytest.mark.parametrize("extra_name", ["foo", "FOO"])
def test_add_url_constraint_wheel_with_extras_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
extra_name: str,
) -> None:
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
repo.add_package(get_package("tomlkit", "0.5.5"))
old_tester.execute(
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
f"[{extra_name},bar]"
)
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 4 installs, 0 updates, 0 removals
- Installing cleo (0.6.5)
- Installing pendulum (1.4.4)
- Installing tomlkit (0.5.5)
- Installing demo\
(0.1.0 https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 4
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"url": (
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
),
"extras": [extra_name, "bar"],
}
def test_add_constraint_with_python_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
cachy2 = get_package("cachy", "0.2.0")
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2)
old_tester.execute("cachy=0.2.0 --python >=2.7")
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 1
content = app.poetry.file.read()["tool"]["poetry"]
assert "cachy" in content["dependencies"]
assert content["dependencies"]["cachy"] == {"version": "0.2.0", "python": ">=2.7"}
def test_add_constraint_with_platform_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
env: MockEnv,
old_tester: CommandTester,
) -> None:
platform = sys.platform
env._platform = platform
env._marker_env = None
cachy2 = get_package("cachy", "0.2.0")
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2)
old_tester.execute(f"cachy=0.2.0 --platform {platform} -vvv")
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 1
content = app.poetry.file.read()["tool"]["poetry"]
assert "cachy" in content["dependencies"]
assert content["dependencies"]["cachy"] == {
"version": "0.2.0",
"platform": platform,
}
def test_add_constraint_with_source_old_installer(
app: PoetryTestApplication,
poetry: Poetry,
installer: NoopInstaller,
old_tester: CommandTester,
mocker: MockerFixture,
) -> None:
repo = LegacyRepository(name="my-index", url="https://my-index.fake")
repo.add_package(get_package("cachy", "0.2.0"))
mocker.patch.object(
repo,
"_find_packages",
wraps=lambda _, name: [
Package(
"cachy",
Version.parse("0.2.0"),
source_type="legacy",
source_reference=repo.name,
source_url=repo._url,
yanked=False,
)
],
)
poetry.pool.add_repository(repo)
old_tester.execute("cachy=0.2.0 --source my-index")
expected = """\
Updating dependencies
Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 1
content = app.poetry.file.read()["tool"]["poetry"]
assert "cachy" in content["dependencies"]
assert content["dependencies"]["cachy"] == {
"version": "0.2.0",
"source": "my-index",
}
def test_add_constraint_with_source_that_does_not_exist_old_installer(
app: PoetryTestApplication, old_tester: CommandTester
) -> None:
with pytest.raises(IndexError) as e:
old_tester.execute("foo --source i-dont-exist")
assert str(e.value) == 'Repository "i-dont-exist" does not exist.'
def test_add_constraint_not_found_with_source_old_installer(
app: PoetryTestApplication,
poetry: Poetry,
mocker: MockerFixture,
old_tester: CommandTester,
) -> None:
repo = LegacyRepository(name="my-index", url="https://my-index.fake")
mocker.patch.object(repo, "find_packages", return_value=[])
poetry.pool.add_repository(repo)
pypi = poetry.pool.repositories[0]
pypi.add_package(get_package("cachy", "0.2.0"))
with pytest.raises(ValueError) as e:
old_tester.execute("cachy --source my-index")
assert str(e.value) == "Could not find a matching version of package cachy"
def test_add_to_section_that_does_no_exist_yet_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0"))
old_tester.execute("cachy --group dev")
expected = """\
Using version ^0.2.0 for cachy
Updating dependencies
Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 1
content = app.poetry.file.read()["tool"]["poetry"]
assert "cachy" in content["group"]["dev"]["dependencies"]
assert content["group"]["dev"]["dependencies"]["cachy"] == "^0.2.0"
def test_add_should_not_select_prereleases_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("pyyaml", "3.13"))
repo.add_package(get_package("pyyaml", "4.2b2"))
old_tester.execute("pyyaml")
expected = """\
Using version ^3.13 for pyyaml
Updating dependencies
Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals
- Installing pyyaml (3.13)
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert len(installer.installs) == 1
content = app.poetry.file.read()["tool"]["poetry"]
assert "pyyaml" in content["dependencies"]
assert content["dependencies"]["pyyaml"] == "^3.13"
def test_add_should_skip_when_adding_existing_package_with_no_constraint_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
content = app.poetry.file.read()
content["tool"]["poetry"]["dependencies"]["foo"] = "^1.0"
app.poetry.file.write(content)
repo.add_package(get_package("foo", "1.1.2"))
old_tester.execute("foo")
expected = """\
The following packages are already present in the pyproject.toml and will be skipped:
• foo
If you want to update it to the latest compatible version,\
you can use `poetry update package`.
If you prefer to upgrade it to the latest available version,\
you can use `poetry add package@latest`.
"""
assert expected in old_tester.io.fetch_output()
def test_add_should_work_when_adding_existing_package_with_latest_constraint_old_installer( # noqa: E501
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
content = app.poetry.file.read()
content["tool"]["poetry"]["dependencies"]["foo"] = "^1.0"
app.poetry.file.write(content)
repo.add_package(get_package("foo", "1.1.2"))
old_tester.execute("foo@latest")
expected = """\
Using version ^1.1.2 for foo
Updating dependencies
Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals
- Installing foo (1.1.2)
Writing lock file
"""
assert expected in old_tester.io.fetch_output()
content = app.poetry.file.read()["tool"]["poetry"]
assert "foo" in content["dependencies"]
assert content["dependencies"]["foo"] == "^1.1.2"
def test_add_chooses_prerelease_if_only_prereleases_are_available_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("foo", "1.2.3b0"))
repo.add_package(get_package("foo", "1.2.3b1"))
old_tester.execute("foo")
expected = """\
Using version ^1.2.3b1 for foo
Updating dependencies
Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals
- Installing foo (1.2.3b1)
Writing lock file
"""
assert expected in old_tester.io.fetch_output()
def test_add_preferes_stable_releases_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("foo", "1.2.3"))
repo.add_package(get_package("foo", "1.2.4b1"))
old_tester.execute("foo")
expected = """\
Using version ^1.2.3 for foo
Updating dependencies
Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals
- Installing foo (1.2.3)
Writing lock file
"""
assert expected in old_tester.io.fetch_output()
def test_add_with_lock_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
) -> None:
repo.add_package(get_package("cachy", "0.2.0"))
old_tester.execute("cachy --lock")
expected = """\
Using version ^0.2.0 for cachy
Updating dependencies
Resolving dependencies...
Writing lock file
"""
assert old_tester.io.fetch_output() == expected
assert expected in tester.io.fetch_output()
def test_add_keyboard_interrupt_restore_content(
......
......@@ -52,7 +52,6 @@ def test_list_displays_default_value_if_not_set(
cache_dir = json.dumps(str(config_cache_dir))
venv_path = json.dumps(os.path.join("{cache-dir}", "virtualenvs"))
expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false
installer.max-workers = null
installer.modern-installation = true
......@@ -82,7 +81,6 @@ def test_list_displays_set_get_setting(
cache_dir = json.dumps(str(config_cache_dir))
venv_path = json.dumps(os.path.join("{cache-dir}", "virtualenvs"))
expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false
installer.max-workers = null
installer.modern-installation = true
......@@ -136,7 +134,6 @@ def test_list_displays_set_get_local_setting(
cache_dir = json.dumps(str(config_cache_dir))
venv_path = json.dumps(os.path.join("{cache-dir}", "virtualenvs"))
expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false
installer.max-workers = null
installer.modern-installation = true
......@@ -174,7 +171,6 @@ def test_list_must_not_display_sources_from_pyproject_toml(
cache_dir = json.dumps(str(config_cache_dir))
venv_path = json.dumps(os.path.join("{cache-dir}", "virtualenvs"))
expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false
installer.max-workers = null
installer.modern-installation = true
......
......@@ -11,7 +11,6 @@ from cleo.testers.application_tester import ApplicationTester
from cleo.testers.command_tester import CommandTester
from poetry.installation import Installer
from poetry.installation.noop_installer import NoopInstaller
from poetry.utils.env import MockEnv
from tests.helpers import MOCK_DEFAULT_GIT_REVISION
from tests.helpers import PoetryTestApplication
......@@ -35,11 +34,6 @@ if TYPE_CHECKING:
from tests.types import ProjectFactory
@pytest.fixture()
def installer() -> NoopInstaller:
return NoopInstaller()
@pytest.fixture
def env(tmp_path: Path) -> MockEnv:
path = tmp_path / ".venv"
......@@ -50,15 +44,10 @@ def env(tmp_path: Path) -> MockEnv:
@pytest.fixture(autouse=True)
def setup(
mocker: MockerFixture,
installer: NoopInstaller,
installed: Repository,
config: Config,
env: MockEnv,
) -> Iterator[None]:
# Set Installer's installer
p = mocker.patch("poetry.installation.installer.Installer._get_installer")
p.return_value = installer
# Do not run pip commands of the executor
mocker.patch("poetry.installation.executor.Executor.run_pip")
......@@ -117,11 +106,6 @@ def app_tester(app: PoetryTestApplication) -> ApplicationTester:
return ApplicationTester(app)
@pytest.fixture
def new_installer_disabled(config: Config) -> None:
config.merge({"experimental": {"new-installer": False}})
@pytest.fixture()
def executor(poetry: Poetry, config: Config, env: MockEnv) -> TestExecutor:
return TestExecutor(env, poetry.pool, config, NullIO())
......
......@@ -20,9 +20,8 @@ from poetry.core.packages.package import Package
from poetry.core.packages.project_package import ProjectPackage
from poetry.factory import Factory
from poetry.installation import Installer as BaseInstaller
from poetry.installation import Installer
from poetry.installation.executor import Executor as BaseExecutor
from poetry.installation.noop_installer import NoopInstaller
from poetry.packages import Locker as BaseLocker
from poetry.repositories import Repository
from poetry.repositories import RepositoryPool
......@@ -51,11 +50,6 @@ if TYPE_CHECKING:
RESERVED_PACKAGES = ("pip", "setuptools", "wheel")
class Installer(BaseInstaller):
def _get_installer(self) -> NoopInstaller:
return NoopInstaller()
class Executor(BaseExecutor):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
......@@ -190,7 +184,7 @@ def installer(
installed: CustomInstalledRepository,
config: Config,
) -> Installer:
installer = Installer(
return Installer(
NullIO(),
env,
package,
......@@ -200,7 +194,6 @@ def installer(
installed=installed,
executor=Executor(env, pool, config, NullIO()),
)
return installer
def fixture(name: str) -> dict[str, Any]:
......@@ -2264,7 +2257,6 @@ def test_installer_uses_prereleases_if_they_are_compatible(
result = installer.run()
assert result == 0
del installer.installer.installs[:]
locker.locked(True)
locker.mock_lock_data(locker.written_data)
......
from __future__ import annotations
import itertools
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
import pytest
from cleo.io.null_io import NullIO
from poetry.core.packages.project_package import ProjectPackage
from poetry.factory import Factory
from poetry.installation import Installer as BaseInstaller
from poetry.installation.noop_installer import NoopInstaller
from poetry.packages import Locker as BaseLocker
from poetry.repositories import Repository
from poetry.repositories import RepositoryPool
from poetry.repositories.installed_repository import InstalledRepository
from poetry.toml.file import TOMLFile
from poetry.utils.env import MockEnv
from poetry.utils.env import NullEnv
from tests.helpers import get_dependency
from tests.helpers import get_package
from tests.repositories.test_legacy_repository import (
MockRepository as MockLegacyRepository,
)
from tests.repositories.test_pypi_repository import MockRepository
if TYPE_CHECKING:
from pytest_mock import MockerFixture
from poetry.utils.env import Env
from tests.conftest import Config
from tests.types import FixtureDirGetter
RESERVED_PACKAGES = ("pip", "setuptools", "wheel")
class Installer(BaseInstaller):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self._use_executor = False
def _get_installer(self) -> NoopInstaller:
return NoopInstaller()
class CustomInstalledRepository(InstalledRepository):
@classmethod
def load(
cls, env: Env, with_dependencies: bool = False
) -> CustomInstalledRepository:
return cls()
class Locker(BaseLocker):
def __init__(self, lock_path: Path) -> None:
self._lock = lock_path / "poetry.lock"
self._written_data = None
self._locked = False
self._lock_data = None
self._content_hash = self._get_content_hash()
@property
def written_data(self) -> dict | None:
return self._written_data
def set_lock_path(self, lock: Path) -> Locker:
self._lock = lock / "poetry.lock"
return self
def locked(self, is_locked: bool = True) -> Locker:
self._locked = is_locked
return self
def mock_lock_data(self, data: dict) -> None:
self._lock_data = data
def is_locked(self) -> bool:
return self._locked
def is_fresh(self) -> bool:
return True
def _get_content_hash(self) -> str:
return "123456789"
def _write_lock_data(self, data: dict) -> None:
for package in data["package"]:
python_versions = str(package["python-versions"])
package["python-versions"] = python_versions
self._written_data = data
self._lock_data = data
@pytest.fixture()
def package() -> ProjectPackage:
p = ProjectPackage("root", "1.0")
p.root_dir = Path.cwd()
return p
@pytest.fixture()
def repo() -> Repository:
return Repository("repo")
@pytest.fixture()
def pool(repo: Repository) -> RepositoryPool:
pool = RepositoryPool()
pool.add_repository(repo)
return pool
@pytest.fixture()
def installed() -> CustomInstalledRepository:
return CustomInstalledRepository()
@pytest.fixture()
def locker(project_root: Path) -> Locker:
return Locker(lock_path=project_root)
@pytest.fixture()
def env(tmp_path: Path) -> NullEnv:
return NullEnv(path=tmp_path)
@pytest.fixture()
def installer(
package: ProjectPackage,
pool: RepositoryPool,
locker: Locker,
env: NullEnv,
installed: CustomInstalledRepository,
config: Config,
):
return Installer(NullIO(), env, package, locker, pool, config, installed=installed)
def fixture(name: str) -> str:
file = TOMLFile(Path(__file__).parent / "fixtures" / f"{name}.test")
return file.read()
def test_run_no_dependencies(installer: Installer, locker: Locker):
result = installer.run()
assert result == 0
expected = fixture("no-dependencies")
assert locker.written_data == expected
def test_run_with_dependencies(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
package.add_dependency(Factory.create_dependency("A", "~1.0"))
package.add_dependency(Factory.create_dependency("B", "^1.0"))
result = installer.run()
assert result == 0
expected = fixture("with-dependencies")
assert locker.written_data == expected
def test_run_update_after_removing_dependencies(
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
installed: CustomInstalledRepository,
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "B",
"version": "1.1",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "C",
"version": "1.2",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"files": {"A": [], "B": [], "C": []},
},
}
)
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c = get_package("C", "1.2")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
installed.add_package(package_a)
installed.add_package(package_b)
installed.add_package(package_c)
package.add_dependency(Factory.create_dependency("A", "~1.0"))
package.add_dependency(Factory.create_dependency("B", "~1.1"))
installer.update(True)
result = installer.run()
assert result == 0
expected = fixture("with-dependencies")
assert locker.written_data == expected
installs = installer.installer.installs
assert len(installs) == 0
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 1
def test_run_install_no_group(
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
installed: CustomInstalledRepository,
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "B",
"version": "1.1",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "C",
"version": "1.2",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"files": {"A": [], "B": [], "C": []},
},
}
)
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c = get_package("C", "1.2")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
installed.add_package(package_a)
installed.add_package(package_b)
installed.add_package(package_c)
package.add_dependency(Factory.create_dependency("A", "~1.0"))
package.add_dependency(Factory.create_dependency("B", "~1.1"))
package.add_dependency(Factory.create_dependency("C", "~1.2", groups=["dev"]))
installer.only_groups([])
result = installer.run()
assert result == 0
installs = installer.installer.installs
assert len(installs) == 0
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 0
@pytest.mark.parametrize(
"managed_reserved_package_names",
itertools.chain(
[()],
itertools.permutations(RESERVED_PACKAGES, 1),
itertools.permutations(RESERVED_PACKAGES, 2),
[RESERVED_PACKAGES],
),
)
def test_run_install_with_synchronization(
managed_reserved_package_names: tuple[str, ...],
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
installed: CustomInstalledRepository,
):
package_a = get_package("a", "1.0")
package_b = get_package("b", "1.1")
package_c = get_package("c", "1.2")
package_pip = get_package("pip", "20.0.0")
package_setuptools = get_package("setuptools", "20.0.0")
package_wheel = get_package("wheel", "20.0.0")
all_packages = [
package_a,
package_b,
package_c,
package_pip,
package_setuptools,
package_wheel,
]
managed_reserved_packages = [
pkg for pkg in all_packages if pkg.name in managed_reserved_package_names
]
locked_packages = [package_a, *managed_reserved_packages]
for pkg in all_packages:
repo.add_package(pkg)
installed.add_package(pkg)
installed.add_package(package) # Root package never removed.
package.add_dependency(Factory.create_dependency(package_a.name, package_a.version))
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": pkg.name,
"version": pkg.version,
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
}
for pkg in locked_packages
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"files": {pkg.name: [] for pkg in locked_packages},
},
}
)
installer.requires_synchronization(True)
result = installer.run()
assert result == 0
installs = installer.installer.installs
assert len(installs) == 0
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
expected_removals = {
package_b.name,
package_c.name,
*managed_reserved_package_names,
}
assert {r.name for r in removals} == expected_removals
def test_run_whitelist_add(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
}
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"files": {"A": []},
},
}
)
package_a = get_package("A", "1.0")
package_a_new = get_package("A", "1.1")
package_b = get_package("B", "1.1")
repo.add_package(package_a)
repo.add_package(package_a_new)
repo.add_package(package_b)
package.add_dependency(Factory.create_dependency("A", "~1.0"))
package.add_dependency(Factory.create_dependency("B", "^1.0"))
installer.update(True)
installer.whitelist(["B"])
result = installer.run()
assert result == 0
expected = fixture("with-dependencies")
assert locker.written_data == expected
def test_run_whitelist_remove(
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
installed: CustomInstalledRepository,
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "B",
"version": "1.1",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"files": {"A": [], "B": []},
},
}
)
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
installed.add_package(package_b)
package.add_dependency(Factory.create_dependency("A", "~1.0"))
installer.update(True)
installer.whitelist(["B"])
result = installer.run()
assert result == 0
expected = fixture("remove")
assert locker.written_data == expected
assert len(installer.installer.installs) == 1
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 1
def test_add_with_sub_dependencies(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c = get_package("C", "1.2")
package_d = get_package("D", "1.3")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
package.add_dependency(Factory.create_dependency("A", "~1.0"))
package.add_dependency(Factory.create_dependency("B", "^1.0"))
package_a.add_dependency(Factory.create_dependency("D", "^1.0"))
package_b.add_dependency(Factory.create_dependency("C", "~1.2"))
result = installer.run()
assert result == 0
expected = fixture("with-sub-dependencies")
assert locker.written_data == expected
def test_run_with_python_versions(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
package.python_versions = "~2.7 || ^3.4"
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c12 = get_package("C", "1.2")
package_c12.python_versions = "~2.7 || ^3.3"
package_c13 = get_package("C", "1.3")
package_c13.python_versions = "~3.3"
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c12)
repo.add_package(package_c13)
package.add_dependency(Factory.create_dependency("A", "~1.0"))
package.add_dependency(Factory.create_dependency("B", "^1.0"))
package.add_dependency(Factory.create_dependency("C", "^1.0"))
result = installer.run()
assert result == 0
expected = fixture("with-python-versions")
assert locker.written_data == expected
def test_run_with_optional_and_python_restricted_dependencies(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
package.python_versions = "~2.7 || ^3.4"
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c12 = get_package("C", "1.2")
package_c13 = get_package("C", "1.3")
package_d = get_package("D", "1.4")
package_c13.add_dependency(Factory.create_dependency("D", "^1.2"))
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c12)
repo.add_package(package_c13)
repo.add_package(package_d)
package.extras = {"foo": [get_dependency("A", "~1.0")]}
package.add_dependency(
Factory.create_dependency("A", {"version": "~1.0", "optional": True})
)
package.add_dependency(
Factory.create_dependency("B", {"version": "^1.0", "python": "~2.4"})
)
package.add_dependency(
Factory.create_dependency("C", {"version": "^1.0", "python": "~2.7 || ^3.4"})
)
result = installer.run()
assert result == 0
expected = fixture("with-optional-dependencies")
assert locker.written_data == expected
installer = installer.installer
# We should only have 2 installs:
# C,D since python version is not compatible
# with B's python constraint and A is optional
assert len(installer.installs) == 2
assert installer.installs[0].name == "d"
assert installer.installs[1].name == "c"
def test_run_with_optional_and_platform_restricted_dependencies(
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
mocker: MockerFixture,
):
mocker.patch("sys.platform", "darwin")
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c12 = get_package("C", "1.2")
package_c13 = get_package("C", "1.3")
package_d = get_package("D", "1.4")
package_c13.add_dependency(Factory.create_dependency("D", "^1.2"))
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c12)
repo.add_package(package_c13)
repo.add_package(package_d)
package.extras = {"foo": [get_dependency("A", "~1.0")]}
package.add_dependency(
Factory.create_dependency("A", {"version": "~1.0", "optional": True})
)
package.add_dependency(
Factory.create_dependency("B", {"version": "^1.0", "platform": "custom"})
)
package.add_dependency(
Factory.create_dependency("C", {"version": "^1.0", "platform": "darwin"})
)
result = installer.run()
assert result == 0
expected = fixture("with-platform-dependencies")
assert locker.written_data == expected
installer = installer.installer
# We should only have 2 installs:
# C,D since the mocked python version is not compatible
# with B's python constraint and A is optional
assert len(installer.installs) == 2
assert installer.installs[0].name == "d"
assert installer.installs[1].name == "c"
def test_run_with_dependencies_extras(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.0")
package_c = get_package("C", "1.0")
package_b.extras = {"foo": [get_dependency("C", "^1.0")]}
package_b.add_dependency(
Factory.create_dependency("C", {"version": "^1.0", "optional": True})
)
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
package.add_dependency(Factory.create_dependency("A", "^1.0"))
package.add_dependency(
Factory.create_dependency("B", {"version": "^1.0", "extras": ["foo"]})
)
result = installer.run()
assert result == 0
expected = fixture("with-dependencies-extras")
assert locker.written_data == expected
def test_run_does_not_install_extras_if_not_requested(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
package.extras["foo"] = [get_dependency("D")]
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.0")
package_c = get_package("C", "1.0")
package_d = get_package("D", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
package.add_dependency(Factory.create_dependency("A", "^1.0"))
package.add_dependency(Factory.create_dependency("B", "^1.0"))
package.add_dependency(Factory.create_dependency("C", "^1.0"))
package.add_dependency(
Factory.create_dependency("D", {"version": "^1.0", "optional": True})
)
result = installer.run()
assert result == 0
# Extras are pinned in lock
expected = fixture("extras")
assert locker.written_data == expected
# But should not be installed
installer = installer.installer
assert len(installer.installs) == 3 # A, B, C
def test_run_installs_extras_if_requested(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
package.extras["foo"] = [get_dependency("D")]
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.0")
package_c = get_package("C", "1.0")
package_d = get_package("D", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
package.add_dependency(Factory.create_dependency("A", "^1.0"))
package.add_dependency(Factory.create_dependency("B", "^1.0"))
package.add_dependency(Factory.create_dependency("C", "^1.0"))
package.add_dependency(
Factory.create_dependency("D", {"version": "^1.0", "optional": True})
)
installer.extras(["foo"])
result = installer.run()
assert result == 0
# Extras are pinned in lock
expected = fixture("extras")
assert locker.written_data == expected
# But should not be installed
installer = installer.installer
assert len(installer.installs) == 4 # A, B, C, D
def test_run_installs_extras_with_deps_if_requested(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
package.extras["foo"] = [get_dependency("C")]
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.0")
package_c = get_package("C", "1.0")
package_d = get_package("D", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
package.add_dependency(Factory.create_dependency("A", "^1.0"))
package.add_dependency(Factory.create_dependency("B", "^1.0"))
package.add_dependency(
Factory.create_dependency("C", {"version": "^1.0", "optional": True})
)
package_c.add_dependency(Factory.create_dependency("D", "^1.0"))
installer.extras(["foo"])
result = installer.run()
assert result == 0
# Extras are pinned in lock
expected = fixture("extras-with-dependencies")
assert locker.written_data == expected
# But should not be installed
installer = installer.installer
assert len(installer.installs) == 4 # A, B, C, D
def test_run_installs_extras_with_deps_if_requested_locked(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
locker.locked(True)
locker.mock_lock_data(fixture("extras-with-dependencies"))
package.extras["foo"] = [get_dependency("C")]
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.0")
package_c = get_package("C", "1.0")
package_d = get_package("D", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
package.add_dependency(Factory.create_dependency("A", "^1.0"))
package.add_dependency(Factory.create_dependency("B", "^1.0"))
package.add_dependency(
Factory.create_dependency("C", {"version": "^1.0", "optional": True})
)
package_c.add_dependency(Factory.create_dependency("D", "^1.0"))
installer.extras(["foo"])
result = installer.run()
assert result == 0
# But should not be installed
installer = installer.installer
assert len(installer.installs) == 4 # A, B, C, D
def test_installer_with_pypi_repository(
package: ProjectPackage,
locker: Locker,
installed: CustomInstalledRepository,
config: Config,
env: NullEnv,
):
pool = RepositoryPool()
pool.add_repository(MockRepository())
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
package.python_versions = ">=3.7"
package.add_dependency(Factory.create_dependency("pytest", "^3.5", groups=["dev"]))
result = installer.run()
assert result == 0
expected = fixture("with-pypi-repository")
assert expected == locker.written_data
def test_run_installs_with_local_file(
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
fixture_dir: FixtureDirGetter,
):
file_path = fixture_dir("distributions/demo-0.1.0-py2.py3-none-any.whl")
package.add_dependency(Factory.create_dependency("demo", {"file": str(file_path)}))
repo.add_package(get_package("pendulum", "1.4.4"))
result = installer.run()
assert result == 0
expected = fixture("with-file-dependency")
assert locker.written_data == expected
assert len(installer.installer.installs) == 2
def test_run_installs_wheel_with_no_requires_dist(
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
fixture_dir: FixtureDirGetter,
):
file_path = fixture_dir(
"wheel_with_no_requires_dist/demo-0.1.0-py2.py3-none-any.whl"
)
package.add_dependency(Factory.create_dependency("demo", {"file": str(file_path)}))
result = installer.run()
assert result == 0
expected = fixture("with-wheel-dependency-no-requires-dist")
assert locker.written_data == expected
assert len(installer.installer.installs) == 1
def test_run_installs_with_local_poetry_directory_and_extras(
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
tmpdir: Path,
fixture_dir: FixtureDirGetter,
):
file_path = fixture_dir("project_with_extras")
package.add_dependency(
Factory.create_dependency(
"project-with-extras", {"path": str(file_path), "extras": ["extras_a"]}
)
)
repo.add_package(get_package("pendulum", "1.4.4"))
result = installer.run()
assert result == 0
expected = fixture("with-directory-dependency-poetry")
assert locker.written_data == expected
assert len(installer.installer.installs) == 2
def test_run_installs_with_local_poetry_directory_transitive(
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
tmpdir: Path,
fixture_dir: FixtureDirGetter,
):
root_dir = fixture_dir("directory")
package.root_dir = root_dir
locker.set_lock_path(root_dir)
directory = root_dir.joinpath("project_with_transitive_directory_dependencies")
package.add_dependency(
Factory.create_dependency(
"project-with-transitive-directory-dependencies",
{"path": str(directory.relative_to(root_dir))},
root_dir=root_dir,
)
)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
result = installer.run()
assert result == 0
expected = fixture("with-directory-dependency-poetry-transitive")
assert locker.written_data == expected
assert len(installer.installer.installs) == 6
def test_run_installs_with_local_poetry_file_transitive(
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
tmpdir: Path,
fixture_dir: FixtureDirGetter,
):
root_dir = fixture_dir("directory")
package.root_dir = root_dir
locker.set_lock_path(root_dir)
directory = root_dir.joinpath("project_with_transitive_file_dependencies")
package.add_dependency(
Factory.create_dependency(
"project-with-transitive-file-dependencies",
{"path": str(directory.relative_to(fixture_dir("directory")))},
root_dir=root_dir,
)
)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
result = installer.run()
assert result == 0
expected = fixture("with-file-dependency-transitive")
assert locker.written_data == expected
assert len(installer.installer.installs) == 4
def test_run_installs_with_local_setuptools_directory(
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
tmpdir: Path,
fixture_dir: FixtureDirGetter,
):
file_path = fixture_dir("project_with_setup/")
package.add_dependency(
Factory.create_dependency("project-with-setup", {"path": str(file_path)})
)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
result = installer.run()
assert result == 0
expected = fixture("with-directory-dependency-setuptools")
assert locker.written_data == expected
assert len(installer.installer.installs) == 3
def test_run_with_prereleases(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0a2",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
}
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"files": {"A": []},
},
}
)
package_a = get_package("A", "1.0a2")
package_b = get_package("B", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
package.add_dependency(
Factory.create_dependency("A", {"version": "*", "allow-prereleases": True})
)
package.add_dependency(Factory.create_dependency("B", "^1.1"))
installer.update(True)
installer.whitelist({"B": "^1.1"})
result = installer.run()
assert result == 0
expected = fixture("with-prereleases")
assert locker.written_data == expected
def test_run_update_all_with_lock(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"optional": True,
"platform": "*",
"python-versions": "*",
"checksum": [],
}
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"files": {"A": []},
},
}
)
package_a = get_package("A", "1.1")
repo.add_package(get_package("A", "1.0"))
repo.add_package(package_a)
package.add_dependency(Factory.create_dependency("A", "*"))
installer.update(True)
result = installer.run()
assert result == 0
expected = fixture("update-with-lock")
assert locker.written_data == expected
def test_run_update_with_locked_extras(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"B": "^1.0", "C": "^1.0"},
},
{
"name": "B",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "C",
"version": "1.1",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"requirements": {"python": "~2.7"},
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"files": {"A": [], "B": [], "C": []},
},
}
)
package_a = get_package("A", "1.0")
package_a.extras["foo"] = [get_dependency("B")]
b_dependency = get_dependency("B", "^1.0", optional=True)
b_dependency.in_extras.append("foo")
c_dependency = get_dependency("C", "^1.0")
c_dependency.python_versions = "~2.7"
package_a.add_dependency(b_dependency)
package_a.add_dependency(c_dependency)
repo.add_package(package_a)
repo.add_package(get_package("B", "1.0"))
repo.add_package(get_package("C", "1.1"))
repo.add_package(get_package("D", "1.1"))
package.add_dependency(
Factory.create_dependency("A", {"version": "^1.0", "extras": ["foo"]})
)
package.add_dependency(Factory.create_dependency("D", "^1.0"))
installer.update(True)
installer.whitelist("D")
result = installer.run()
assert result == 0
expected = fixture("update-with-locked-extras")
assert locker.written_data == expected
def test_run_install_duplicate_dependencies_different_constraints(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
package.add_dependency(Factory.create_dependency("A", "*"))
package_a = get_package("A", "1.0")
package_a.add_dependency(
Factory.create_dependency("B", {"version": "^1.0", "python": "<4.0"})
)
package_a.add_dependency(
Factory.create_dependency("B", {"version": "^2.0", "python": ">=4.0"})
)
package_b10 = get_package("B", "1.0")
package_b20 = get_package("B", "2.0")
package_b10.add_dependency(Factory.create_dependency("C", "1.2"))
package_b20.add_dependency(Factory.create_dependency("C", "1.5"))
package_c12 = get_package("C", "1.2")
package_c15 = get_package("C", "1.5")
repo.add_package(package_a)
repo.add_package(package_b10)
repo.add_package(package_b20)
repo.add_package(package_c12)
repo.add_package(package_c15)
result = installer.run()
assert result == 0
expected = fixture("with-duplicate-dependencies")
assert locker.written_data == expected
installs = installer.installer.installs
assert len(installs) == 3
assert installs[0] == package_c12
assert installs[1] == package_b10
assert installs[2] == package_a
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 0
def test_run_install_duplicate_dependencies_different_constraints_with_lock(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {
"B": [
{"version": "^1.0", "python": "<4.0"},
{"version": "^2.0", "python": ">=4.0"},
]
},
},
{
"name": "B",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"C": "1.2"},
"requirements": {"python": "<4.0"},
},
{
"name": "B",
"version": "2.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"C": "1.5"},
"requirements": {"python": ">=4.0"},
},
{
"name": "C",
"version": "1.2",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "C",
"version": "1.5",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"files": {"A": [], "B": [], "C": []},
},
}
)
package.add_dependency(Factory.create_dependency("A", "*"))
package_a = get_package("A", "1.0")
package_a.add_dependency(
Factory.create_dependency("B", {"version": "^1.0", "python": "<4.0"})
)
package_a.add_dependency(
Factory.create_dependency("B", {"version": "^2.0", "python": ">=4.0"})
)
package_b10 = get_package("B", "1.0")
package_b20 = get_package("B", "2.0")
package_b10.add_dependency(Factory.create_dependency("C", "1.2"))
package_b20.add_dependency(Factory.create_dependency("C", "1.5"))
package_c12 = get_package("C", "1.2")
package_c15 = get_package("C", "1.5")
repo.add_package(package_a)
repo.add_package(package_b10)
repo.add_package(package_b20)
repo.add_package(package_c12)
repo.add_package(package_c15)
installer.update(True)
result = installer.run()
assert result == 0
expected = fixture("with-duplicate-dependencies")
assert locker.written_data == expected
installs = installer.installer.installs
assert len(installs) == 3
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 0
def test_run_update_uninstalls_after_removal_transient_dependency(
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
installed: CustomInstalledRepository,
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"B": {"version": "^1.0", "python": "<2.0"}},
},
{
"name": "B",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"files": {"A": [], "B": []},
},
}
)
package.add_dependency(Factory.create_dependency("A", "*"))
package_a = get_package("A", "1.0")
package_a.add_dependency(
Factory.create_dependency("B", {"version": "^1.0", "python": "<2.0"})
)
package_b10 = get_package("B", "1.0")
repo.add_package(package_a)
repo.add_package(package_b10)
installed.add_package(get_package("A", "1.0"))
installed.add_package(get_package("B", "1.0"))
installer.update(True)
result = installer.run()
assert result == 0
installs = installer.installer.installs
assert len(installs) == 0
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 1
def test_run_install_duplicate_dependencies_different_constraints_with_lock_update(
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
installed: CustomInstalledRepository,
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {
"B": [
{"version": "^1.0", "python": "<2.7"},
{"version": "^2.0", "python": ">=2.7"},
]
},
},
{
"name": "B",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"C": "1.2"},
"requirements": {"python": "<2.7"},
},
{
"name": "B",
"version": "2.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"C": "1.5"},
"requirements": {"python": ">=2.7"},
},
{
"name": "C",
"version": "1.2",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "C",
"version": "1.5",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"files": {"A": [], "B": [], "C": []},
},
}
)
package.add_dependency(Factory.create_dependency("A", "*"))
package_a = get_package("A", "1.1")
package_a.add_dependency(Factory.create_dependency("B", "^2.0"))
package_b10 = get_package("B", "1.0")
package_b20 = get_package("B", "2.0")
package_b10.add_dependency(Factory.create_dependency("C", "1.2"))
package_b20.add_dependency(Factory.create_dependency("C", "1.5"))
package_c12 = get_package("C", "1.2")
package_c15 = get_package("C", "1.5")
repo.add_package(package_a)
repo.add_package(package_b10)
repo.add_package(package_b20)
repo.add_package(package_c12)
repo.add_package(package_c15)
installed.add_package(get_package("A", "1.0"))
installer.update(True)
installer.whitelist(["A"])
result = installer.run()
assert result == 0
expected = fixture("with-duplicate-dependencies-update")
assert locker.written_data == expected
installs = installer.installer.installs
assert len(installs) == 2
updates = installer.installer.updates
assert len(updates) == 1
removals = installer.installer.removals
assert len(removals) == 0
@pytest.mark.skip(
"This is not working at the moment due to limitations in the resolver"
)
def test_installer_test_solver_finds_compatible_package_for_dependency_python_not_fully_compatible_with_package_python( # noqa: E501
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
installed: CustomInstalledRepository,
):
package.python_versions = "~2.7 || ^3.4"
package.add_dependency(
Factory.create_dependency("A", {"version": "^1.0", "python": "^3.5"})
)
package_a101 = get_package("A", "1.0.1")
package_a101.python_versions = ">=3.6"
package_a100 = get_package("A", "1.0.0")
package_a100.python_versions = ">=3.5"
repo.add_package(package_a100)
repo.add_package(package_a101)
result = installer.run()
assert result == 0
expected = fixture("with-conditional-dependency")
assert locker.written_data == expected
installs = installer.installer.installs
assert len(installs) == 1
def test_installer_required_extras_should_not_be_removed_when_updating_single_dependency( # noqa: E501
installer: Installer,
locker: Locker,
repo: Repository,
package: ProjectPackage,
installed: CustomInstalledRepository,
env: NullEnv,
pool: RepositoryPool,
config: Config,
):
package.add_dependency(Factory.create_dependency("A", {"version": "^1.0"}))
package_a = get_package("A", "1.0.0")
package_a.add_dependency(
Factory.create_dependency("B", {"version": "^1.0", "extras": ["foo"]})
)
package_b = get_package("B", "1.0.0")
package_b.add_dependency(
Factory.create_dependency("C", {"version": "^1.0", "optional": True})
)
package_b.extras = {"foo": [get_dependency("C")]}
package_c = get_package("C", "1.0.0")
package_d = get_package("D", "1.0.0")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
installer.update(True)
result = installer.run()
assert result == 0
assert len(installer.installer.installs) == 3
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
package.add_dependency(Factory.create_dependency("D", "^1.0"))
locker.locked(True)
locker.mock_lock_data(locker.written_data)
installed.add_package(package_a)
installed.add_package(package_b)
installed.add_package(package_c)
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
installer.update(True)
installer.whitelist(["D"])
result = installer.run()
assert result == 0
assert len(installer.installer.installs) == 1
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
def test_installer_required_extras_should_not_be_removed_when_updating_single_dependency_pypi_repository( # noqa: E501
locker: Locker,
repo: Repository,
package: ProjectPackage,
installed: CustomInstalledRepository,
env: NullEnv,
mocker: MockerFixture,
config: Config,
):
mocker.patch("sys.platform", "darwin")
pool = RepositoryPool()
pool.add_repository(MockRepository())
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
package.add_dependency(Factory.create_dependency("poetry", {"version": "^0.12.0"}))
installer.update(True)
result = installer.run()
assert result == 0
assert len(installer.installer.installs) == 3
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
package.add_dependency(Factory.create_dependency("pytest", "^3.5"))
locker.locked(True)
locker.mock_lock_data(locker.written_data)
for pkg in installer.installer.installs:
installed.add_package(pkg)
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
installer.update(True)
installer.whitelist(["pytest"])
result = installer.run()
assert result == 0
assert len(installer.installer.installs) == 7
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
def test_installer_required_extras_should_be_installed(
locker: Locker,
repo: Repository,
package: ProjectPackage,
installed: CustomInstalledRepository,
env: NullEnv,
config: Config,
):
pool = RepositoryPool()
pool.add_repository(MockRepository())
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
package.add_dependency(
Factory.create_dependency(
"cachecontrol", {"version": "^0.12.5", "extras": ["filecache"]}
)
)
installer.update(True)
result = installer.run()
assert result == 0
assert len(installer.installer.installs) == 2
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
locker.locked(True)
locker.mock_lock_data(locker.written_data)
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
installer.update(True)
result = installer.run()
assert result == 0
assert len(installer.installer.installs) == 2
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
def test_update_multiple_times_with_split_dependencies_is_idempotent(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"B": ">=1.0"},
},
{
"name": "B",
"version": "1.0.1",
"optional": False,
"platform": "*",
"python-versions": ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*",
"checksum": [],
"dependencies": {},
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"files": {"A": [], "B": []},
},
}
)
package.python_versions = "~2.7 || ^3.4"
package.add_dependency(Factory.create_dependency("A", "^1.0"))
a10 = get_package("A", "1.0")
a11 = get_package("A", "1.1")
a11.add_dependency(Factory.create_dependency("B", ">=1.0.1"))
a11.add_dependency(
Factory.create_dependency("C", {"version": "^1.0", "python": "~2.7"})
)
a11.add_dependency(
Factory.create_dependency("C", {"version": "^2.0", "python": "^3.4"})
)
b101 = get_package("B", "1.0.1")
b110 = get_package("B", "1.1.0")
repo.add_package(a10)
repo.add_package(a11)
repo.add_package(b101)
repo.add_package(b110)
repo.add_package(get_package("C", "1.0"))
repo.add_package(get_package("C", "2.0"))
expected = fixture("with-multiple-updates")
installer.update(True)
result = installer.run()
assert result == 0
assert locker.written_data == expected
locker.mock_lock_data(locker.written_data)
installer.update(True)
result = installer.run()
assert result == 0
assert locker.written_data == expected
locker.mock_lock_data(locker.written_data)
installer.update(True)
result = installer.run()
assert result == 0
assert locker.written_data == expected
def test_installer_can_install_dependencies_from_forced_source(
locker: Locker,
package: ProjectPackage,
installed: CustomInstalledRepository,
env: NullEnv,
config: Config,
):
package.python_versions = "^3.7"
package.add_dependency(
Factory.create_dependency("tomlkit", {"version": "^0.5", "source": "legacy"})
)
pool = RepositoryPool()
pool.add_repository(MockLegacyRepository())
pool.add_repository(MockRepository())
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
installer.update(True)
result = installer.run()
assert result == 0
assert len(installer.installer.installs) == 1
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
def test_run_installs_with_url_file(
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
):
url = "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
package.add_dependency(Factory.create_dependency("demo", {"url": url}))
repo.add_package(get_package("pendulum", "1.4.4"))
result = installer.run()
assert result == 0
expected = fixture("with-url-dependency")
assert locker.written_data == expected
assert len(installer.installer.installs) == 2
def test_installer_uses_prereleases_if_they_are_compatible(
installer: Installer, locker: Locker, package: ProjectPackage, repo: Repository
):
package.python_versions = "~2.7 || ^3.4"
package.add_dependency(
Factory.create_dependency(
"prerelease", {"git": "https://github.com/demo/prerelease.git"}
)
)
package_b = get_package("b", "2.0.0")
package_b.add_dependency(Factory.create_dependency("prerelease", ">=0.19"))
repo.add_package(package_b)
result = installer.run()
assert result == 0
del installer.installer.installs[:]
locker.locked(True)
locker.mock_lock_data(locker.written_data)
package.add_dependency(Factory.create_dependency("b", "^2.0.0"))
installer.whitelist(["b"])
installer.update(True)
result = installer.run()
assert result == 0
assert len(installer.installer.installs) == 2
def test_installer_can_handle_old_lock_files(
locker: Locker,
package: ProjectPackage,
repo: Repository,
installed: CustomInstalledRepository,
config: Config,
):
pool = RepositoryPool()
pool.add_repository(MockRepository())
package.add_dependency(Factory.create_dependency("pytest", "^3.5", groups=["dev"]))
locker.locked()
locker.mock_lock_data(fixture("old-lock"))
installer = Installer(
NullIO(), MockEnv(), package, locker, pool, config, installed=installed
)
result = installer.run()
assert result == 0
assert len(installer.installer.installs) == 6
installer = Installer(
NullIO(),
MockEnv(version_info=(2, 7, 18)),
package,
locker,
pool,
config,
installed=installed,
)
result = installer.run()
assert result == 0
# funcsigs will be added
assert len(installer.installer.installs) == 7
installer = Installer(
NullIO(),
MockEnv(version_info=(2, 7, 18), platform="win32"),
package,
locker,
pool,
config,
installed=installed,
)
result = installer.run()
assert result == 0
# colorama will be added
assert len(installer.installer.installs) == 8
from __future__ import annotations
import re
import shutil
from pathlib import Path
from typing import TYPE_CHECKING
import pytest
from cleo.io.null_io import NullIO
from poetry.core.packages.package import Package
from poetry.installation.pip_installer import PipInstaller
from poetry.repositories.legacy_repository import LegacyRepository
from poetry.repositories.repository_pool import Priority
from poetry.repositories.repository_pool import RepositoryPool
from poetry.utils.authenticator import RepositoryCertificateConfig
from poetry.utils.env import NullEnv
if TYPE_CHECKING:
from pytest_mock import MockerFixture
from poetry.utils.env import VirtualEnv
from tests.conftest import Config
from tests.types import FixtureDirGetter
@pytest.fixture
def package_git() -> Package:
package = Package(
"demo",
"1.0.0",
source_type="git",
source_url="git@github.com:demo/demo.git",
source_reference="master",
)
return package
@pytest.fixture
def package_git_with_subdirectory() -> Package:
package = Package(
"subdirectories",
"2.0.0",
source_type="git",
source_url="https://github.com/demo/subdirectories.git",
source_reference="master",
source_subdirectory="two",
)
return package
@pytest.fixture
def pool() -> RepositoryPool:
return RepositoryPool()
@pytest.fixture()
def env(tmp_path: Path) -> NullEnv:
return NullEnv(path=tmp_path)
@pytest.fixture
def installer(pool: RepositoryPool, env: NullEnv) -> PipInstaller:
return PipInstaller(env, NullIO(), pool)
def test_requirement(installer: PipInstaller) -> None:
package = Package("ipython", "7.5.0")
package.files = [
{"file": "foo-0.1.0.tar.gz", "hash": "md5:dbdc53e3918f28fa335a173432402a00"},
{
"file": "foo.0.1.0.whl",
"hash": "e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26",
},
]
result = installer.requirement(package, formatted=True)
expected = (
"ipython==7.5.0 "
"--hash md5:dbdc53e3918f28fa335a173432402a00 "
"--hash sha256:e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26"
"\n"
)
assert result == expected
def test_requirement_source_type_url(env: NullEnv) -> None:
installer = PipInstaller(env, NullIO(), RepositoryPool())
foo = Package(
"foo",
"0.0.0",
source_type="url",
source_url="https://somewhere.com/releases/foo-1.0.0.tar.gz",
)
result = installer.requirement(foo, formatted=True)
expected = f"{foo.source_url}#egg={foo.name}"
assert result == expected
def test_requirement_git_subdirectory(
pool: RepositoryPool, package_git_with_subdirectory: Package, env: NullEnv
) -> None:
installer = PipInstaller(env, NullIO(), pool)
result = installer.requirement(package_git_with_subdirectory)
expected = (
"git+https://github.com/demo/subdirectories.git"
"@master#egg=subdirectories&subdirectory=two"
)
assert result == expected
installer.install(package_git_with_subdirectory)
assert len(env.executed) == 1
cmd = env.executed[0]
assert Path(cmd[-1]).parts[-3:] == ("demo", "subdirectories", "two")
def test_requirement_git_develop_false(
installer: PipInstaller, package_git: Package
) -> None:
package_git.develop = False
result = installer.requirement(package_git)
expected = "git+git@github.com:demo/demo.git@master#egg=demo"
assert result == expected
def test_install_with_non_pypi_default_repository(
pool: RepositoryPool, installer: PipInstaller
) -> None:
default = LegacyRepository("default", "https://default.com")
another = LegacyRepository("another", "https://another.com")
pool.add_repository(default, priority=Priority.DEFAULT)
pool.add_repository(another)
foo = Package(
"foo",
"0.0.0",
source_type="legacy",
source_reference=default.name,
source_url=default.url,
)
bar = Package(
"bar",
"0.1.0",
source_type="legacy",
source_reference=another.name,
source_url=another.url,
)
installer.install(foo)
installer.install(bar)
@pytest.mark.parametrize(
("key", "option"),
[
("client_cert", "client-cert"),
("cert", "cert"),
],
)
def test_install_with_certs(
mocker: MockerFixture, key: str, option: str, env: NullEnv
) -> None:
client_path = "path/to/client.pem"
mocker.patch(
"poetry.utils.authenticator.Authenticator.get_certs_for_url",
return_value=RepositoryCertificateConfig(**{key: Path(client_path)}),
)
default = LegacyRepository("default", "https://foo.bar")
pool = RepositoryPool()
pool.add_repository(default, priority=Priority.DEFAULT)
installer = PipInstaller(env, NullIO(), pool)
foo = Package(
"foo",
"0.0.0",
source_type="legacy",
source_reference=default.name,
source_url=default.url,
)
installer.install(foo)
assert len(env.executed) == 1
cmd = env.executed[0]
assert f"--{option}" in cmd
cert_index = cmd.index(f"--{option}")
# Need to do the str(Path()) bit because Windows paths get modified by Path
assert cmd[cert_index + 1] == str(Path(client_path))
def test_requirement_git_develop_true(
installer: PipInstaller, package_git: Package
) -> None:
package_git.develop = True
result = installer.requirement(package_git)
expected = ["-e", "git+git@github.com:demo/demo.git@master#egg=demo"]
assert result == expected
def test_uninstall_git_package_nspkg_pth_cleanup(
mocker: MockerFixture, tmp_venv: VirtualEnv, pool: RepositoryPool
) -> None:
# this test scenario requires a real installation using the pip installer
installer = PipInstaller(tmp_venv, NullIO(), pool)
# use a namespace package
package = Package(
"namespace-package-one",
"1.0.0",
source_type="git",
source_url="https://github.com/demo/namespace-package-one.git",
source_reference="master",
)
# in order to reproduce the scenario where the git source is removed prior to proper
# clean up of nspkg.pth file, we need to make sure the fixture is copied and not
# symlinked into the git src directory
def copy_only(source: Path, dest: Path) -> None:
if dest.exists():
dest.unlink()
if source.is_dir():
shutil.copytree(str(source), str(dest))
else:
shutil.copyfile(str(source), str(dest))
mocker.patch("tests.helpers.copy_or_symlink", new=copy_only)
# install package and then remove it
installer.install(package)
installer.remove(package)
pth_file = Path(f"{package.name}-nspkg.pth")
assert not tmp_venv.site_packages.exists(pth_file)
# any command in the virtual environment should trigger the error message
output = tmp_venv.run("python", "-m", "site")
assert not re.match(rf"Error processing line 1 of .*{pth_file}", output)
def test_install_with_trusted_host(config: Config, env: NullEnv) -> None:
config.merge({"certificates": {"default": {"cert": False}}})
default = LegacyRepository("default", "https://foo.bar")
pool = RepositoryPool()
pool.add_repository(default, priority=Priority.DEFAULT)
installer = PipInstaller(env, NullIO(), pool)
foo = Package(
"foo",
"0.0.0",
source_type="legacy",
source_reference=default.name,
source_url=default.url,
)
installer.install(foo)
assert len(env.executed) == 1
cmd = env.executed[0]
assert "--trusted-host" in cmd
cert_index = cmd.index("--trusted-host")
assert cmd[cert_index + 1] == "foo.bar"
def test_install_directory_fallback_on_poetry_create_error(
mocker: MockerFixture,
tmp_venv: VirtualEnv,
pool: RepositoryPool,
fixture_dir: FixtureDirGetter,
) -> None:
mock_create_poetry = mocker.patch(
"poetry.factory.Factory.create_poetry", side_effect=RuntimeError
)
mock_sdist_builder = mocker.patch("poetry.core.masonry.builders.sdist.SdistBuilder")
mock_editable_builder = mocker.patch(
"poetry.masonry.builders.editable.EditableBuilder"
)
mock_pip_install = mocker.patch("poetry.installation.pip_installer.pip_install")
package = Package(
"demo",
"1.0.0",
source_type="directory",
source_url=str(fixture_dir("inspection") / "demo_poetry_package"),
)
installer = PipInstaller(tmp_venv, NullIO(), pool)
installer.install_directory(package)
assert mock_create_poetry.call_count == 1
assert mock_sdist_builder.call_count == 0
assert mock_editable_builder.call_count == 0
assert mock_pip_install.call_count == 1
assert mock_pip_install.call_args[1].get("deps") is None
assert mock_pip_install.call_args[1].get("upgrade") is True
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment