Skip to content

Commit 0aef22d

Browse files
committed
installer: remove unrequested extras if requires_synchronization is set, increase test coverage of installer regarding extras
1 parent 5f08ac1 commit 0aef22d

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
@@ -1009,73 +1009,20 @@ def test_run_with_dependencies_nested_extras(
10091009
assert locker.written_data == expected
10101010

10111011

1012-
def test_run_does_not_install_extras_if_not_requested(
1013-
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
1014-
) -> None:
1015-
package.extras[canonicalize_name("foo")] = [get_dependency("D")]
1016-
package_a = get_package("A", "1.0")
1017-
package_b = get_package("B", "1.0")
1018-
package_c = get_package("C", "1.0")
1019-
package_d = get_package("D", "1.1")
1020-
1021-
repo.add_package(package_a)
1022-
repo.add_package(package_b)
1023-
repo.add_package(package_c)
1024-
repo.add_package(package_d)
1025-
1026-
package.add_dependency(Factory.create_dependency("A", "^1.0"))
1027-
package.add_dependency(Factory.create_dependency("B", "^1.0"))
1028-
package.add_dependency(Factory.create_dependency("C", "^1.0"))
1029-
package.add_dependency(
1030-
Factory.create_dependency("D", {"version": "^1.0", "optional": True})
1031-
)
1032-
1033-
result = installer.run()
1034-
assert result == 0
1035-
1036-
expected = fixture("extras")
1037-
# Extras are pinned in lock
1038-
assert locker.written_data == expected
1039-
1040-
# But should not be installed
1041-
assert installer.executor.installations_count == 3 # A, B, C
1042-
1043-
1044-
def test_run_installs_extras_if_requested(
1045-
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
1046-
) -> None:
1047-
package.extras[canonicalize_name("foo")] = [get_dependency("D")]
1048-
package_a = get_package("A", "1.0")
1049-
package_b = get_package("B", "1.0")
1050-
package_c = get_package("C", "1.0")
1051-
package_d = get_package("D", "1.1")
1052-
1053-
repo.add_package(package_a)
1054-
repo.add_package(package_b)
1055-
repo.add_package(package_c)
1056-
repo.add_package(package_d)
1057-
1058-
package.add_dependency(Factory.create_dependency("A", "^1.0"))
1059-
package.add_dependency(Factory.create_dependency("B", "^1.0"))
1060-
package.add_dependency(Factory.create_dependency("C", "^1.0"))
1061-
package.add_dependency(
1062-
Factory.create_dependency("D", {"version": "^1.0", "optional": True})
1063-
)
1064-
1065-
installer.extras(["foo"])
1066-
result = installer.run()
1067-
assert result == 0
1068-
1069-
# Extras are pinned in lock
1070-
expected = fixture("extras")
1071-
assert locker.written_data == expected
1072-
1073-
# But should not be installed
1074-
assert installer.executor.installations_count == 4 # A, B, C, D
1075-
1076-
1012+
@pytest.mark.parametrize("is_locked", [False, True])
1013+
@pytest.mark.parametrize("is_installed", [False, True])
1014+
@pytest.mark.parametrize("with_extras", [False, True])
1015+
@pytest.mark.parametrize("do_sync", [False, True])
10771016
def test_run_installs_extras_with_deps_if_requested(
1078-
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
1017+
installer: Installer,
1018+
locker: Locker,
1019+
repo: Repository,
1020+
installed: CustomInstalledRepository,
1021+
package: ProjectPackage,
1022+
is_locked: bool,
1023+
is_installed: bool,
1024+
with_extras: bool,
1025+
do_sync: bool,
10791026
) -> None:
10801027
package.extras[canonicalize_name("foo")] = [get_dependency("C")]
10811028
package_a = get_package("A", "1.0")
@@ -1096,48 +1043,38 @@ def test_run_installs_extras_with_deps_if_requested(
10961043

10971044
package_c.add_dependency(Factory.create_dependency("D", "^1.0"))
10981045

1099-
installer.extras(["foo"])
1100-
result = installer.run()
1101-
assert result == 0
1102-
1103-
expected = fixture("extras-with-dependencies")
1104-
1105-
# Extras are pinned in lock
1106-
assert locker.written_data == expected
1107-
1108-
# But should not be installed
1109-
assert installer.executor.installations_count == 4 # A, B, C, D
1110-
1111-
1112-
def test_run_installs_extras_with_deps_if_requested_locked(
1113-
installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage
1114-
) -> None:
1115-
locker.locked(True)
1116-
locker.mock_lock_data(fixture("extras-with-dependencies"))
1117-
package.extras[canonicalize_name("foo")] = [get_dependency("C")]
1118-
package_a = get_package("A", "1.0")
1119-
package_b = get_package("B", "1.0")
1120-
package_c = get_package("C", "1.0")
1121-
package_d = get_package("D", "1.1")
1122-
1123-
repo.add_package(package_a)
1124-
repo.add_package(package_b)
1125-
repo.add_package(package_c)
1126-
repo.add_package(package_d)
1127-
1128-
package.add_dependency(Factory.create_dependency("A", "^1.0"))
1129-
package.add_dependency(Factory.create_dependency("B", "^1.0"))
1130-
package.add_dependency(
1131-
Factory.create_dependency("C", {"version": "^1.0", "optional": True})
1132-
)
1046+
if is_locked:
1047+
locker.locked(True)
1048+
locker.mock_lock_data(fixture("extras-with-dependencies"))
11331049

1134-
package_c.add_dependency(Factory.create_dependency("D", "^1.0"))
1050+
if is_installed:
1051+
installed.add_package(package_a)
1052+
installed.add_package(package_b)
1053+
installed.add_package(package_c)
1054+
installed.add_package(package_d)
11351055

1136-
installer.extras(["foo"])
1056+
if with_extras:
1057+
installer.extras(["foo"])
1058+
installer.requires_synchronization(do_sync)
11371059
result = installer.run()
11381060
assert result == 0
11391061

1140-
assert installer.executor.installations_count == 4 # A, B, C, D
1062+
if not is_locked:
1063+
assert locker.written_data == fixture("extras-with-dependencies")
1064+
1065+
if with_extras:
1066+
# A, B, C, D
1067+
expected_installations_count = 0 if is_installed else 4
1068+
expected_removals_count = 0
1069+
else:
1070+
# A, B
1071+
expected_installations_count = 0 if is_installed else 2
1072+
# We only want to uninstall extras if we do a "poetry install" without extras,
1073+
# not if we do a "poetry update" or "poetry add".
1074+
expected_removals_count = 2 if is_installed and is_locked else 0
1075+
1076+
assert installer.executor.installations_count == expected_installations_count
1077+
assert installer.executor.removals_count == expected_removals_count
11411078

11421079

11431080
@pytest.mark.network

0 commit comments

Comments
 (0)