diff --git a/src/poetry/puzzle/transaction.py b/src/poetry/puzzle/transaction.py index 5c5dbd061c9..74d0d6c5e61 100644 --- a/src/poetry/puzzle/transaction.py +++ b/src/poetry/puzzle/transaction.py @@ -74,6 +74,7 @@ def calculate_operations( operations.append(Install(result_package, priority=priority)) if with_uninstalls: + uninstalls: set[str] = set() for current_package in self._current_packages: found = any( current_package.name == result_package.name @@ -83,11 +84,12 @@ def calculate_operations( if not found: for installed_package in self._installed_packages: if installed_package.name == current_package.name: + uninstalls.add(installed_package.name) operations.append(Uninstall(current_package)) if synchronize: - current_package_names = { - current_package.name for current_package in self._current_packages + result_package_names = { + result_package.name for result_package, _ in self._result_packages } # We preserve pip/setuptools/wheel when not managed by poetry, this is # done to avoid externally managed virtual environments causing @@ -96,9 +98,12 @@ def calculate_operations( "pip", "setuptools", "wheel", - } - current_package_names + } - result_package_names for installed_package in self._installed_packages: + if installed_package.name in uninstalls: + continue + if ( self._root_package and installed_package.name == self._root_package.name @@ -108,7 +113,8 @@ def calculate_operations( if installed_package.name in preserved_package_names: continue - if installed_package.name not in current_package_names: + if installed_package.name not in result_package_names: + uninstalls.add(installed_package.name) operations.append(Uninstall(installed_package)) return sorted( diff --git a/tests/puzzle/test_transaction.py b/tests/puzzle/test_transaction.py index ae4093f5b12..67d3499183a 100644 --- a/tests/puzzle/test_transaction.py +++ b/tests/puzzle/test_transaction.py @@ -121,6 +121,31 @@ def test_it_should_remove_installed_packages_if_required(): ) +def test_it_should_not_remove_installed_packages_that_are_in_result(): + transaction = Transaction( + [], + [ + (Package("a", "1.0.0"), 1), + (Package("b", "2.0.0"), 2), + (Package("c", "3.0.0"), 0), + ], + installed_packages=[ + Package("a", "1.0.0"), + Package("b", "2.0.0"), + Package("c", "3.0.0"), + ], + ) + + check_operations( + transaction.calculate_operations(synchronize=True), + [ + {"job": "install", "package": Package("a", "1.0.0"), "skipped": True}, + {"job": "install", "package": Package("b", "2.0.0"), "skipped": True}, + {"job": "install", "package": Package("c", "3.0.0"), "skipped": True}, + ], + ) + + def test_it_should_update_installed_packages_if_sources_are_different(): transaction = Transaction( [Package("a", "1.0.0")],