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,127 +360,7 @@ class Installer: ...@@ -384,127 +360,7 @@ 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]
...@@ -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)
...@@ -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 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