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 ...@@ -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 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 `--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 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 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)). - Added better windows shell support ([#5053](https://github.com/python-poetry/poetry/pull/5053)).
......
...@@ -229,9 +229,6 @@ specific packages. ...@@ -229,9 +229,6 @@ specific packages.
| `package[,package,..]` | Disallow binary distributions for specified packages only. | | `package[,package,..]` | Disallow binary distributions for specified packages only. |
{{% note %}} {{% 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 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 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 applied only when selecting which distribution for dependency should be installed into a Poetry managed
......
...@@ -124,7 +124,6 @@ class Config: ...@@ -124,7 +124,6 @@ class Config:
"prompt": "{project_name}-py{python_version}", "prompt": "{project_name}-py{python_version}",
}, },
"experimental": { "experimental": {
"new-installer": True,
"system-git-client": False, "system-git-client": False,
}, },
"installer": { "installer": {
...@@ -276,7 +275,6 @@ class Config: ...@@ -276,7 +275,6 @@ class Config:
"virtualenvs.options.always-copy", "virtualenvs.options.always-copy",
"virtualenvs.options.system-site-packages", "virtualenvs.options.system-site-packages",
"virtualenvs.options.prefer-active-python", "virtualenvs.options.prefer-active-python",
"experimental.new-installer",
"experimental.system-git-client", "experimental.system-git-client",
"installer.modern-installation", "installer.modern-installation",
"installer.parallel", "installer.parallel",
......
...@@ -335,10 +335,6 @@ class Application(BaseApplication): ...@@ -335,10 +335,6 @@ class Application(BaseApplication):
poetry.config, poetry.config,
disable_cache=poetry.disable_cache, 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) command.set_installer(installer)
def _load_plugins(self, io: IO | None = None) -> None: def _load_plugins(self, io: IO | None = None) -> None:
......
...@@ -68,7 +68,6 @@ To remove a repository (repo is a short alias for repositories): ...@@ -68,7 +68,6 @@ To remove a repository (repo is a short alias for repositories):
), ),
"virtualenvs.path": (str, lambda val: str(Path(val))), "virtualenvs.path": (str, lambda val: str(Path(val))),
"virtualenvs.prefer-active-python": (boolean_validator, boolean_normalizer), "virtualenvs.prefer-active-python": (boolean_validator, boolean_normalizer),
"experimental.new-installer": (boolean_validator, boolean_normalizer),
"experimental.system-git-client": (boolean_validator, boolean_normalizer), "experimental.system-git-client": (boolean_validator, boolean_normalizer),
"installer.modern-installation": (boolean_validator, boolean_normalizer), "installer.modern-installation": (boolean_validator, boolean_normalizer),
"installer.parallel": (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 ...@@ -105,11 +105,6 @@ dependencies and not including the current project, run the command with the
from poetry.masonry.builders.editable import EditableBuilder 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"): if self.option("extras") and self.option("all-extras"):
self.line_error( self.line_error(
"<error>You cannot specify explicit" "<error>You cannot specify explicit"
......
...@@ -35,11 +35,6 @@ file. ...@@ -35,11 +35,6 @@ file.
loggers = ["poetry.repositories.pypi_repository"] loggers = ["poetry.repositories.pypi_repository"]
def handle(self) -> int: 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.option("check"):
if self.poetry.locker.is_locked() and self.poetry.locker.is_fresh(): if self.poetry.locker.is_locked() and self.poetry.locker.is_fresh():
self.line("poetry.lock is consistent with pyproject.toml.") self.line("poetry.lock is consistent with pyproject.toml.")
......
...@@ -40,12 +40,6 @@ class UpdateCommand(InstallerCommand): ...@@ -40,12 +40,6 @@ class UpdateCommand(InstallerCommand):
def handle(self) -> int: def handle(self) -> int:
packages = self.argument("packages") 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: if packages:
self.installer.whitelist({name: "*" for name in 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 from __future__ import annotations
import warnings
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from cleo.io.null_io import NullIO from cleo.io.null_io import NullIO
...@@ -11,13 +9,11 @@ from poetry.installation.executor import Executor ...@@ -11,13 +9,11 @@ from poetry.installation.executor import Executor
from poetry.installation.operations import Install from poetry.installation.operations import Install
from poetry.installation.operations import Uninstall from poetry.installation.operations import Uninstall
from poetry.installation.operations import Update from poetry.installation.operations import Update
from poetry.installation.pip_installer import PipInstaller
from poetry.repositories import Repository from poetry.repositories import Repository
from poetry.repositories import RepositoryPool from poetry.repositories import RepositoryPool
from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.installed_repository import InstalledRepository
from poetry.repositories.lockfile_repository import LockfileRepository from poetry.repositories.lockfile_repository import LockfileRepository
from poetry.utils.extras import get_extra_package_names from poetry.utils.extras import get_extra_package_names
from poetry.utils.helpers import pluralize
if TYPE_CHECKING: if TYPE_CHECKING:
...@@ -28,7 +24,6 @@ if TYPE_CHECKING: ...@@ -28,7 +24,6 @@ if TYPE_CHECKING:
from poetry.core.packages.project_package import ProjectPackage from poetry.core.packages.project_package import ProjectPackage
from poetry.config.config import Config from poetry.config.config import Config
from poetry.installation.base_installer import BaseInstaller
from poetry.installation.operations.operation import Operation from poetry.installation.operations.operation import Operation
from poetry.packages import Locker from poetry.packages import Locker
from poetry.utils.env import Env from poetry.utils.env import Env
...@@ -74,9 +69,7 @@ class Installer: ...@@ -74,9 +69,7 @@ class Installer:
) )
self._executor = executor self._executor = executor
self._use_executor = True
self._installer = self._get_installer()
if installed is None: if installed is None:
installed = self._get_installed() installed = self._get_installed()
...@@ -86,10 +79,6 @@ class Installer: ...@@ -86,10 +79,6 @@ class Installer:
def executor(self) -> Executor: def executor(self) -> Executor:
return self._executor return self._executor
@property
def installer(self) -> BaseInstaller:
return self._installer
def set_package(self, package: ProjectPackage) -> Installer: def set_package(self, package: ProjectPackage) -> Installer:
self._package = package self._package = package
...@@ -187,19 +176,6 @@ class Installer: ...@@ -187,19 +176,6 @@ class Installer:
return self 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: def _do_refresh(self) -> int:
from poetry.puzzle.solver import Solver from poetry.puzzle.solver import Solver
...@@ -384,128 +360,8 @@ class Installer: ...@@ -384,128 +360,8 @@ class Installer:
self._io.write_line("<info>Writing lock file</>") self._io.write_line("<info>Writing lock file</>")
def _execute(self, operations: list[Operation]) -> int: def _execute(self, operations: list[Operation]) -> int:
if self._use_executor:
return self._executor.execute(operations) 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)
def _populate_lockfile_repo( def _populate_lockfile_repo(
self, repo: LockfileRepository, ops: Iterable[Operation] self, repo: LockfileRepository, ops: Iterable[Operation]
) -> None: ) -> None:
...@@ -588,8 +444,5 @@ class Installer: ...@@ -588,8 +444,5 @@ class Installer:
return get_extra_package_names(repo.packages, extras, self._extras) 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: def _get_installed(self) -> InstalledRepository:
return InstalledRepository.load(self._env) 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: ...@@ -22,7 +22,6 @@ if TYPE_CHECKING:
from cleo.testers.command_tester import CommandTester from cleo.testers.command_tester import CommandTester
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from poetry.installation.noop_installer import NoopInstaller
from poetry.poetry import Poetry from poetry.poetry import Poetry
from poetry.utils.env import MockEnv from poetry.utils.env import MockEnv
from poetry.utils.env import VirtualEnv from poetry.utils.env import VirtualEnv
...@@ -65,13 +64,6 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: ...@@ -65,13 +64,6 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("add") 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( def test_add_no_constraint(
app: PoetryTestApplication, repo: TestRepository, tester: CommandTester app: PoetryTestApplication, repo: TestRepository, tester: CommandTester
) -> None: ) -> None:
...@@ -804,7 +796,6 @@ def test_add_constraint_with_platform( ...@@ -804,7 +796,6 @@ def test_add_constraint_with_platform(
) -> None: ) -> None:
platform = sys.platform platform = sys.platform
env._platform = platform env._platform = platform
env._marker_env = None
cachy2 = get_package("cachy", "0.2.0") cachy2 = get_package("cachy", "0.2.0")
...@@ -1223,16 +1214,15 @@ Writing lock file ...@@ -1223,16 +1214,15 @@ Writing lock file
assert content_hash != app.poetry.locker.lock_data["metadata"]["content-hash"] 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, app: PoetryTestApplication,
repo: TestRepository, repo: TestRepository,
installer: NoopInstaller, tester: CommandTester,
old_tester: CommandTester,
) -> None: ) -> None:
repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0")) repo.add_package(get_package("cachy", "0.2.0"))
old_tester.execute("cachy") tester.execute("cachy --group dev")
expected = """\ expected = """\
Using version ^0.2.0 for cachy Using version ^0.2.0 for cachy
...@@ -1242,938 +1232,43 @@ Resolving dependencies... ...@@ -1242,938 +1232,43 @@ Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0) Installing cachy (0.2.0)
Writing lock file 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"] content = app.poetry.file.read()["tool"]["poetry"]
assert "cachy" in content["dependencies"] assert "cachy" in content["group"]["dev"]["dependencies"]
assert content["dependencies"]["cachy"] == "^0.2.0" assert content["group"]["dev"]["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
def test_add_greater_constraint_old_installer( def test_add_preferes_stable_releases(
app: PoetryTestApplication, app: PoetryTestApplication,
repo: TestRepository, repo: TestRepository,
installer: NoopInstaller, tester: CommandTester,
old_tester: CommandTester,
) -> None: ) -> None:
repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("foo", "1.2.3"))
repo.add_package(get_package("cachy", "0.2.0")) repo.add_package(get_package("foo", "1.2.4b1"))
old_tester.execute("cachy>=0.1.0") tester.execute("foo")
expected = """\ expected = """\
Using version ^1.2.3 for foo
Updating dependencies Updating dependencies
Resolving dependencies... Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0) • Installing foo (1.2.3)
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
""" """
assert old_tester.io.fetch_output() == expected assert expected in tester.io.fetch_output()
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
def test_add_keyboard_interrupt_restore_content( def test_add_keyboard_interrupt_restore_content(
......
...@@ -52,7 +52,6 @@ def test_list_displays_default_value_if_not_set( ...@@ -52,7 +52,6 @@ def test_list_displays_default_value_if_not_set(
cache_dir = json.dumps(str(config_cache_dir)) cache_dir = json.dumps(str(config_cache_dir))
venv_path = json.dumps(os.path.join("{cache-dir}", "virtualenvs")) venv_path = json.dumps(os.path.join("{cache-dir}", "virtualenvs"))
expected = f"""cache-dir = {cache_dir} expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false experimental.system-git-client = false
installer.max-workers = null installer.max-workers = null
installer.modern-installation = true installer.modern-installation = true
...@@ -82,7 +81,6 @@ def test_list_displays_set_get_setting( ...@@ -82,7 +81,6 @@ def test_list_displays_set_get_setting(
cache_dir = json.dumps(str(config_cache_dir)) cache_dir = json.dumps(str(config_cache_dir))
venv_path = json.dumps(os.path.join("{cache-dir}", "virtualenvs")) venv_path = json.dumps(os.path.join("{cache-dir}", "virtualenvs"))
expected = f"""cache-dir = {cache_dir} expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false experimental.system-git-client = false
installer.max-workers = null installer.max-workers = null
installer.modern-installation = true installer.modern-installation = true
...@@ -136,7 +134,6 @@ def test_list_displays_set_get_local_setting( ...@@ -136,7 +134,6 @@ def test_list_displays_set_get_local_setting(
cache_dir = json.dumps(str(config_cache_dir)) cache_dir = json.dumps(str(config_cache_dir))
venv_path = json.dumps(os.path.join("{cache-dir}", "virtualenvs")) venv_path = json.dumps(os.path.join("{cache-dir}", "virtualenvs"))
expected = f"""cache-dir = {cache_dir} expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false experimental.system-git-client = false
installer.max-workers = null installer.max-workers = null
installer.modern-installation = true installer.modern-installation = true
...@@ -174,7 +171,6 @@ def test_list_must_not_display_sources_from_pyproject_toml( ...@@ -174,7 +171,6 @@ def test_list_must_not_display_sources_from_pyproject_toml(
cache_dir = json.dumps(str(config_cache_dir)) cache_dir = json.dumps(str(config_cache_dir))
venv_path = json.dumps(os.path.join("{cache-dir}", "virtualenvs")) venv_path = json.dumps(os.path.join("{cache-dir}", "virtualenvs"))
expected = f"""cache-dir = {cache_dir} expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false experimental.system-git-client = false
installer.max-workers = null installer.max-workers = null
installer.modern-installation = true installer.modern-installation = true
......
...@@ -11,7 +11,6 @@ from cleo.testers.application_tester import ApplicationTester ...@@ -11,7 +11,6 @@ from cleo.testers.application_tester import ApplicationTester
from cleo.testers.command_tester import CommandTester from cleo.testers.command_tester import CommandTester
from poetry.installation import Installer from poetry.installation import Installer
from poetry.installation.noop_installer import NoopInstaller
from poetry.utils.env import MockEnv from poetry.utils.env import MockEnv
from tests.helpers import MOCK_DEFAULT_GIT_REVISION from tests.helpers import MOCK_DEFAULT_GIT_REVISION
from tests.helpers import PoetryTestApplication from tests.helpers import PoetryTestApplication
...@@ -35,11 +34,6 @@ if TYPE_CHECKING: ...@@ -35,11 +34,6 @@ if TYPE_CHECKING:
from tests.types import ProjectFactory from tests.types import ProjectFactory
@pytest.fixture()
def installer() -> NoopInstaller:
return NoopInstaller()
@pytest.fixture @pytest.fixture
def env(tmp_path: Path) -> MockEnv: def env(tmp_path: Path) -> MockEnv:
path = tmp_path / ".venv" path = tmp_path / ".venv"
...@@ -50,15 +44,10 @@ def env(tmp_path: Path) -> MockEnv: ...@@ -50,15 +44,10 @@ def env(tmp_path: Path) -> MockEnv:
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def setup( def setup(
mocker: MockerFixture, mocker: MockerFixture,
installer: NoopInstaller,
installed: Repository, installed: Repository,
config: Config, config: Config,
env: MockEnv, env: MockEnv,
) -> Iterator[None]: ) -> 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 # Do not run pip commands of the executor
mocker.patch("poetry.installation.executor.Executor.run_pip") mocker.patch("poetry.installation.executor.Executor.run_pip")
...@@ -117,11 +106,6 @@ def app_tester(app: PoetryTestApplication) -> ApplicationTester: ...@@ -117,11 +106,6 @@ def app_tester(app: PoetryTestApplication) -> ApplicationTester:
return ApplicationTester(app) return ApplicationTester(app)
@pytest.fixture
def new_installer_disabled(config: Config) -> None:
config.merge({"experimental": {"new-installer": False}})
@pytest.fixture() @pytest.fixture()
def executor(poetry: Poetry, config: Config, env: MockEnv) -> TestExecutor: def executor(poetry: Poetry, config: Config, env: MockEnv) -> TestExecutor:
return TestExecutor(env, poetry.pool, config, NullIO()) return TestExecutor(env, poetry.pool, config, NullIO())
......
...@@ -20,9 +20,8 @@ from poetry.core.packages.package import Package ...@@ -20,9 +20,8 @@ from poetry.core.packages.package import Package
from poetry.core.packages.project_package import ProjectPackage from poetry.core.packages.project_package import ProjectPackage
from poetry.factory import Factory 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.executor import Executor as BaseExecutor
from poetry.installation.noop_installer import NoopInstaller
from poetry.packages import Locker as BaseLocker from poetry.packages import Locker as BaseLocker
from poetry.repositories import Repository from poetry.repositories import Repository
from poetry.repositories import RepositoryPool from poetry.repositories import RepositoryPool
...@@ -51,11 +50,6 @@ if TYPE_CHECKING: ...@@ -51,11 +50,6 @@ if TYPE_CHECKING:
RESERVED_PACKAGES = ("pip", "setuptools", "wheel") RESERVED_PACKAGES = ("pip", "setuptools", "wheel")
class Installer(BaseInstaller):
def _get_installer(self) -> NoopInstaller:
return NoopInstaller()
class Executor(BaseExecutor): class Executor(BaseExecutor):
def __init__(self, *args: Any, **kwargs: Any) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
...@@ -190,7 +184,7 @@ def installer( ...@@ -190,7 +184,7 @@ def installer(
installed: CustomInstalledRepository, installed: CustomInstalledRepository,
config: Config, config: Config,
) -> Installer: ) -> Installer:
installer = Installer( return Installer(
NullIO(), NullIO(),
env, env,
package, package,
...@@ -200,7 +194,6 @@ def installer( ...@@ -200,7 +194,6 @@ def installer(
installed=installed, installed=installed,
executor=Executor(env, pool, config, NullIO()), executor=Executor(env, pool, config, NullIO()),
) )
return installer
def fixture(name: str) -> dict[str, Any]: def fixture(name: str) -> dict[str, Any]:
...@@ -2264,7 +2257,6 @@ def test_installer_uses_prereleases_if_they_are_compatible( ...@@ -2264,7 +2257,6 @@ def test_installer_uses_prereleases_if_they_are_compatible(
result = installer.run() result = installer.run()
assert result == 0 assert result == 0
del installer.installer.installs[:]
locker.locked(True) locker.locked(True)
locker.mock_lock_data(locker.written_data) 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