Skip to content

Commit 9eb5363

Browse files
committed
installer: remove unrequested extras if requires_synchronization is set, increase test coverage of installer regarding extras
1 parent 46bfd69 commit 9eb5363

File tree

2 files changed

+49
-109
lines changed

2 files changed

+49
-109
lines changed

src/poetry/installation/installer.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ def _do_install(self) -> int:
267267
ops = self._get_operations_from_lock(locked_repository)
268268

269269
lockfile_repo = LockfileRepository()
270-
self._populate_lockfile_repo(lockfile_repo, ops)
270+
uninstalls = self._populate_lockfile_repo(lockfile_repo, ops)
271271

272272
if not self.executor.enabled:
273273
# If we are only in lock mode, no need to go any further
@@ -324,6 +324,8 @@ def _do_install(self) -> int:
324324
for op in transaction.calculate_operations(with_uninstalls=True)
325325
if op.job_type == "uninstall"
326326
] + ops
327+
else:
328+
ops = uninstalls + ops
327329

328330
# We need to filter operations so that packages
329331
# not compatible with the current system,
@@ -359,18 +361,19 @@ def _execute(self, operations: list[Operation]) -> int:
359361

360362
def _populate_lockfile_repo(
361363
self, repo: LockfileRepository, ops: Iterable[Operation]
362-
) -> None:
364+
) -> list[Uninstall]:
365+
uninstalls = []
363366
for op in ops:
364367
if isinstance(op, Uninstall):
368+
uninstalls.append(op)
365369
continue
366-
elif isinstance(op, Update):
367-
package = op.target_package
368-
else:
369-
package = op.package
370370

371+
package = op.target_package if isinstance(op, Update) else op.package
371372
if not repo.has_package(package):
372373
repo.add_package(package)
373374

375+
return uninstalls
376+
374377
def _get_operations_from_lock(
375378
self, locked_repository: Repository
376379
) -> list[Operation]:

tests/installation/test_installer.py

Lines changed: 40 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -993,73 +993,20 @@ def test_run_with_dependencies_nested_extras(
993993
assert locker.written_data == expected
994994

995995

996-
def test_run_does_not_install_extras_if_not_requested(
997-
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
998-
) -> None:
999-
package.extras[canonicalize_name("foo")] = [get_dependency("D")]
1000-
package_a = get_package("A", "1.0")
1001-
package_b = get_package("B", "1.0")
1002-
package_c = get_package("C", "1.0")
1003-
package_d = get_package("D", "1.1")
1004-
1005-
repo.add_package(package_a)
1006-
repo.add_package(package_b)
1007-
repo.add_package(package_c)
1008-
repo.add_package(package_d)
1009-
1010-
package.add_dependency(Factory.create_dependency("A", "^1.0"))
1011-
package.add_dependency(Factory.create_dependency("B", "^1.0"))
1012-
package.add_dependency(Factory.create_dependency("C", "^1.0"))
1013-
package.add_dependency(
1014-
Factory.create_dependency("D", {"version": "^1.0", "optional": True})
1015-
)
1016-
1017-
result = installer.run()
1018-
assert result == 0
1019-
1020-
expected = fixture("extras")
1021-
# Extras are pinned in lock
1022-
assert locker.written_data == expected
1023-
1024-
# But should not be installed
1025-
assert installer.executor.installations_count == 3 # A, B, C
1026-
1027-
1028-
def test_run_installs_extras_if_requested(
1029-
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
1030-
) -> None:
1031-
package.extras[canonicalize_name("foo")] = [get_dependency("D")]
1032-
package_a = get_package("A", "1.0")
1033-
package_b = get_package("B", "1.0")
1034-
package_c = get_package("C", "1.0")
1035-
package_d = get_package("D", "1.1")
1036-
1037-
repo.add_package(package_a)
1038-
repo.add_package(package_b)
1039-
repo.add_package(package_c)
1040-
repo.add_package(package_d)
1041-
1042-
package.add_dependency(Factory.create_dependency("A", "^1.0"))
1043-
package.add_dependency(Factory.create_dependency("B", "^1.0"))
1044-
package.add_dependency(Factory.create_dependency("C", "^1.0"))
1045-
package.add_dependency(
1046-
Factory.create_dependency("D", {"version": "^1.0", "optional": True})
1047-
)
1048-
1049-
installer.extras(["foo"])
1050-
result = installer.run()
1051-
assert result == 0
1052-
1053-
# Extras are pinned in lock
1054-
expected = fixture("extras")
1055-
assert locker.written_data == expected
1056-
1057-
# But should not be installed
1058-
assert installer.executor.installations_count == 4 # A, B, C, D
1059-
1060-
996+
@pytest.mark.parametrize("is_locked", [False, True])
997+
@pytest.mark.parametrize("is_installed", [False, True])
998+
@pytest.mark.parametrize("with_extras", [False, True])
999+
@pytest.mark.parametrize("do_sync", [False, True])
10611000
def test_run_installs_extras_with_deps_if_requested(
1062-
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
1001+
installer: Installer,
1002+
locker: Locker,
1003+
repo: Repository,
1004+
installed: CustomInstalledRepository,
1005+
package: ProjectPackage,
1006+
is_locked: bool,
1007+
is_installed: bool,
1008+
with_extras: bool,
1009+
do_sync: bool,
10631010
) -> None:
10641011
package.extras[canonicalize_name("foo")] = [get_dependency("C")]
10651012
package_a = get_package("A", "1.0")
@@ -1080,48 +1027,38 @@ def test_run_installs_extras_with_deps_if_requested(
10801027

10811028
package_c.add_dependency(Factory.create_dependency("D", "^1.0"))
10821029

1083-
installer.extras(["foo"])
1084-
result = installer.run()
1085-
assert result == 0
1086-
1087-
expected = fixture("extras-with-dependencies")
1088-
1089-
# Extras are pinned in lock
1090-
assert locker.written_data == expected
1091-
1092-
# But should not be installed
1093-
assert installer.executor.installations_count == 4 # A, B, C, D
1094-
1095-
1096-
def test_run_installs_extras_with_deps_if_requested_locked(
1097-
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
1098-
) -> None:
1099-
locker.locked(True)
1100-
locker.mock_lock_data(fixture("extras-with-dependencies"))
1101-
package.extras[canonicalize_name("foo")] = [get_dependency("C")]
1102-
package_a = get_package("A", "1.0")
1103-
package_b = get_package("B", "1.0")
1104-
package_c = get_package("C", "1.0")
1105-
package_d = get_package("D", "1.1")
1106-
1107-
repo.add_package(package_a)
1108-
repo.add_package(package_b)
1109-
repo.add_package(package_c)
1110-
repo.add_package(package_d)
1111-
1112-
package.add_dependency(Factory.create_dependency("A", "^1.0"))
1113-
package.add_dependency(Factory.create_dependency("B", "^1.0"))
1114-
package.add_dependency(
1115-
Factory.create_dependency("C", {"version": "^1.0", "optional": True})
1116-
)
1030+
if is_locked:
1031+
locker.locked(True)
1032+
locker.mock_lock_data(fixture("extras-with-dependencies"))
11171033

1118-
package_c.add_dependency(Factory.create_dependency("D", "^1.0"))
1034+
if is_installed:
1035+
installed.add_package(package_a)
1036+
installed.add_package(package_b)
1037+
installed.add_package(package_c)
1038+
installed.add_package(package_d)
11191039

1120-
installer.extras(["foo"])
1040+
if with_extras:
1041+
installer.extras(["foo"])
1042+
installer.requires_synchronization(do_sync)
11211043
result = installer.run()
11221044
assert result == 0
11231045

1124-
assert installer.executor.installations_count == 4 # A, B, C, D
1046+
if not is_locked:
1047+
assert locker.written_data == fixture("extras-with-dependencies")
1048+
1049+
if with_extras:
1050+
# A, B, C, D
1051+
expected_installations_count = 0 if is_installed else 4
1052+
expected_removals_count = 0
1053+
else:
1054+
# A, B
1055+
expected_installations_count = 0 if is_installed else 2
1056+
# We only want to uninstall extras if we do a "poetry install" without extras,
1057+
# not if we do a "poetry update" or "poetry add".
1058+
expected_removals_count = 2 if is_installed and is_locked else 0
1059+
1060+
assert installer.executor.installations_count == expected_installations_count
1061+
assert installer.executor.removals_count == expected_removals_count
11251062

11261063

11271064
@pytest.mark.network

0 commit comments

Comments
 (0)