diff --git a/src/poetry/installation/installer.py b/src/poetry/installation/installer.py index 8ba5ddfa9ea..39f1c7f1425 100644 --- a/src/poetry/installation/installer.py +++ b/src/poetry/installation/installer.py @@ -13,7 +13,6 @@ from poetry.repositories import RepositoryPool from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.lockfile_repository import LockfileRepository -from poetry.utils.extras import get_extra_package_names if TYPE_CHECKING: @@ -301,11 +300,6 @@ def _do_install(self) -> int: extras=set(self._extras), ) - # We need to filter operations so that packages - # not compatible with the current system, - # or optional and not requested, are dropped - self._filter_operations(ops, lockfile_repo) - # Validate the dependencies for op in ops: dep = op.package.to_dependency() @@ -344,34 +338,5 @@ def _populate_lockfile_repo( if not repo.has_package(package): repo.add_package(package) - def _filter_operations(self, ops: Iterable[Operation], repo: Repository) -> None: - extra_packages = self._get_extra_packages(repo) - for op in ops: - package = op.target_package if isinstance(op, Update) else op.package - - if op.job_type == "uninstall": - continue - - if not self._env.is_valid_for_marker(package.marker): - op.skip("Not needed for the current environment") - continue - - # If a package is optional and not requested - # in any extra we skip it - if package.optional and package.name not in extra_packages: - op.skip("Not required") - - def _get_extra_packages(self, repo: Repository) -> set[NormalizedName]: - """ - Returns all package names required by extras. - - Maybe we just let the solver handle it? - """ - return get_extra_package_names( - repo.packages, - {k: [d.name for d in v] for k, v in self._package.extras.items()}, - self._extras, - ) - def _get_installed(self) -> InstalledRepository: return InstalledRepository.load(self._env) diff --git a/src/poetry/packages/locker.py b/src/poetry/packages/locker.py index 589b63a9593..420cd392d10 100644 --- a/src/poetry/packages/locker.py +++ b/src/poetry/packages/locker.py @@ -18,7 +18,6 @@ from poetry.core.constraints.version import parse_constraint from poetry.core.packages.dependency import Dependency from poetry.core.packages.package import Package -from poetry.core.version.markers import parse_marker from poetry.core.version.requirements import InvalidRequirement from tomlkit import array from tomlkit import comment @@ -205,22 +204,6 @@ def locked_repository(self) -> LockfileRepository: package.extras = package_extras - if "marker" in info: - package.marker = parse_marker(info["marker"]) - else: - # Compatibility for old locks - if "requirements" in info: - dep = Dependency("foo", "0.0.0") - for name, value in info["requirements"].items(): - if name == "python": - dep.python_versions = value - elif name == "platform": - dep.platform = value - - split_dep = dep.to_pep_508(False).split(";") - if len(split_dep) > 1: - package.marker = parse_marker(split_dep[1].strip()) - for dep_name, constraint in info.get("dependencies", {}).items(): root_dir = self.lock.parent if package.source_type == "directory": @@ -348,7 +331,12 @@ def _get_lock_data(self) -> dict[str, Any]: ) metadata = lock_data["metadata"] - lock_version = Version.parse(metadata.get("lock-version", "1.0")) + if "lock-version" not in metadata: + raise RuntimeError( + "The lock file is not compatible with the current version of Poetry.\n" + "Regenerate the lock file with the `poetry lock` command." + ) + lock_version = Version.parse(metadata["lock-version"]) current_version = Version.parse(self._VERSION) accepted_versions = parse_constraint(self._READ_VERSION_RANGE) lock_version_allowed = accepted_versions.allows(lock_version) diff --git a/src/poetry/puzzle/transaction.py b/src/poetry/puzzle/transaction.py index a1c948eae99..2c3f8bfcf70 100644 --- a/src/poetry/puzzle/transaction.py +++ b/src/poetry/puzzle/transaction.py @@ -54,16 +54,16 @@ def calculate_operations( for result_package, priority in self._result_packages: installed = False + is_unsolicited_extra = extras is not None and ( + result_package.optional and result_package.name not in extra_packages + ) for installed_package in self._installed_packages: if result_package.name == installed_package.name: installed = True # Extras that were not requested are always uninstalled. - if extras is not None and ( - result_package.optional - and result_package.name not in extra_packages - ): + if is_unsolicited_extra: operations.append(Uninstall(installed_package)) # We have to perform an update if the version or another @@ -98,7 +98,10 @@ def calculate_operations( installed or (skip_directory and result_package.source_type == "directory") ): - operations.append(Install(result_package, priority=priority)) + op = Install(result_package, priority=priority) + if is_unsolicited_extra: + op.skip("Not required") + operations.append(op) if with_uninstalls: uninstalls: set[str] = set() diff --git a/tests/console/commands/self/test_remove_plugins.py b/tests/console/commands/self/test_remove_plugins.py index be5e44f6130..6e525e78906 100644 --- a/tests/console/commands/self/test_remove_plugins.py +++ b/tests/console/commands/self/test_remove_plugins.py @@ -48,14 +48,13 @@ def install_plugin(installed: Repository) -> None: "optional": False, "platform": "*", "python-versions": "*", - "checksum": [], + "files": [], }, ], "metadata": { + "lock-version": "2.0", "python-versions": "^3.6", - "platform": "*", "content-hash": "123456789", - "files": {"poetry-plugin": []}, }, } system_pyproject_file.parent.joinpath("poetry.lock").write_text( diff --git a/tests/installation/fixtures/old-lock.test b/tests/installation/fixtures/old-lock.test deleted file mode 100644 index c6b13e72f81..00000000000 --- a/tests/installation/fixtures/old-lock.test +++ /dev/null @@ -1,112 +0,0 @@ -[[package]] -name = "attrs" -version = "17.4.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = "*" - -[package.extras] -dev = ["coverage", "hypothesis", "pympler", "pytest", "six", "zope.interface", "sphinx", "zope.interface"] -docs = ["sphinx", "zope.interface"] -tests = ["coverage", "hypothesis", "pympler", "pytest", "six", "zope.interface"] - -[[package]] -name = "colorama" -version = "0.3.9" -description = "Cross-platform colored terminal text." -marker = "sys_platform == \"win32\"" -optional = false -python-versions = "*" - -[[package]] -name = "funcsigs" -version = "1.0.2" -description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+" -marker = "python_version < \"3.0\"" -optional = false -python-versions = "*" - -[[package]] -name = "more-itertools" -version = "4.1.0" -description = "More routines for operating on iterables, beyond itertools" -optional = false -python-versions = "*" - -[package.dependencies] -six = ">=1.0.0,<2.0.0" - -[[package]] -name = "pluggy" -version = "0.6.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "py" -version = "1.5.3" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pytest" -version = "3.5.0" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -py = ">=1.5.0" -six = ">=1.10.0" -attrs = ">=17.4.0" -more-itertools = ">=4.0.0" -pluggy = ">=0.5,<0.7" -funcsigs = {"version" = "*", "python" = "<3.0"} -colorama = "*" - -[[package]] -name = "six" -version = "1.11.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = "*" - -[metadata] -python-versions = "*" -content-hash = "123456789" - -[metadata.files] -attrs = [ - {file = "attrs-17.4.0-py2.py3-none-any.whl", hash = "sha256:d38e57f381e891928357c68e300d28d3d4dcddc50486d5f8dfaf743d40477619"}, - {file = "attrs-17.4.0.tar.gz", hash = "sha256:eb7536a1e6928190b3008c5b350bdf9850d619fff212341cd096f87a27a5e564"}, -] -colorama = [ - {file = "colorama-0.3.9-py2.py3-none-any.whl", hash = "sha256:5b632359f1ed2b7676a869812ba0edaacb99be04679b29eb56c07a5e137ab5a2"}, - {file = "colorama-0.3.9.tar.gz", hash = "sha256:4c5a15209723ce1330a5c193465fe221098f761e9640d823a2ce7c03f983137f"}, -] -funcsigs = [ - {file = "funcsigs-1.0.2-py2.py3-none-any.whl", hash = "sha256:510ab97424949e726b4b44294018e90142c9aadf8e737cf3a125b4cffed42e79"}, - {file = "funcsigs-1.0.2.tar.gz", hash = "sha256:c55716fcd1228645c214b44568d1fb9af2e28668a9c58e72a17a89a75d24ecd6"}, -] -more-itertools = [ - {file = "more-itertools-4.1.0.tar.gz", hash = "sha256:bab2dc6f4be8f9a4a72177842c5283e2dff57c167439a03e3d8d901e854f0f2e"}, - {file = "more_itertools-4.1.0-py2-none-any.whl", hash = "sha256:5dd7dfd88d2fdaea446da478ffef8d7151fdf26ee92ac7ed7b14e8d71efe4b62"}, - {file = "more_itertools-4.1.0-py3-none-any.whl", hash = "sha256:29b1e1661aaa56875ce090fa219fa84dfc13daecb52cd4fae321f6f57b419ec4"}, -] -pluggy = [ - {file = "pluggy-0.6.0.tar.gz", hash = "sha256:a982e208d054867661d27c6d2a86b17ba05fbb6b1bdc01f42660732dd107f865"}, -] -py = [ - {file = "py-1.5.3-py2.py3-none-any.whl", hash = "sha256:43ee6c7f95e0ec6a906de49906b79d138d89728fff17109d49f086abc2fdd985"}, - {file = "py-1.5.3.tar.gz", hash = "sha256:2df2c513c3af11de15f58189ba5539ddc4768c6f33816dc5c03950c8bd6180fa"}, -] -pytest = [ - {file = "pytest-3.5.0-py2.py3-none-any.whl", hash = "sha256:28e4d9c2ae3196d74805c2eba24f350ae4c791a5b9b397c79b41506a48dc64ca"}, - {file = "pytest-3.5.0.tar.gz", hash = "sha256:677b1d6decd29c041fe64276f29f79fbe66e40c59e445eb251366b4a8ab8bf68"}, -] -six = [ - {file = "six-1.11.0-py2.py3-none-any.whl", hash = "sha256:112f5b46e6aa106db3e4e2494a03694c938f41c4c4535edbdfc816c2e0cb50f2"}, - {file = "six-1.11.0.tar.gz", hash = "sha256:268a4ccb159c1a2d2c79336b02e75058387b0cdbb4cea2f07846a758f48a356d"}, -] diff --git a/tests/installation/test_installer.py b/tests/installation/test_installer.py index 3b0debdf1ac..681e882b19c 100644 --- a/tests/installation/test_installer.py +++ b/tests/installation/test_installer.py @@ -2234,80 +2234,6 @@ def test_installer_uses_prereleases_if_they_are_compatible( assert installer.executor.installations_count == 2 -def test_installer_can_handle_old_lock_files( - locker: Locker, - package: ProjectPackage, - repo: Repository, - installed: CustomInstalledRepository, - config: Config, - pypi_repository: PyPiRepository, -) -> None: - pool = RepositoryPool() - pool.add_repository(pypi_repository) - - 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, - executor=Executor(MockEnv(), pool, config, NullIO()), - ) - result = installer.run() - assert result == 0 - - assert installer.executor.installations_count == 6 - - installer = Installer( - NullIO(), - MockEnv(version_info=(2, 7, 18)), - package, - locker, - pool, - config, - installed=installed, - executor=Executor( - MockEnv(version_info=(2, 7, 18)), - pool, - config, - NullIO(), - ), - ) - result = installer.run() - assert result == 0 - - # funcsigs will be added - assert installer.executor.installations_count == 7 - - installer = Installer( - NullIO(), - MockEnv(version_info=(2, 7, 18), platform="win32"), - package, - locker, - pool, - config, - installed=installed, - executor=Executor( - MockEnv(version_info=(2, 7, 18), platform="win32"), - pool, - config, - NullIO(), - ), - ) - result = installer.run() - assert result == 0 - - # colorama will be added - assert installer.executor.installations_count == 8 - - def test_installer_does_not_write_lock_file_when_installation_fails( installer: Installer, locker: Locker, diff --git a/tests/packages/test_locker.py b/tests/packages/test_locker.py index dcf1122b190..fd11eab8dec 100644 --- a/tests/packages/test_locker.py +++ b/tests/packages/test_locker.py @@ -745,6 +745,24 @@ def test_locker_should_raise_an_error_if_lock_version_is_newer_and_not_allowed( _ = locker.lock_data +def test_locker_should_raise_an_error_if_no_lock_version( + locker: Locker, caplog: LogCaptureFixture +) -> None: + """Lock file prior Poetry 1.1 have no lock file version.""" + content = """\ +[metadata] +python-versions = "~2.7 || ^3.4" +content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77" +""" + caplog.set_level(logging.WARNING, logger="poetry.packages.locker") + + with open(locker.lock, "w", encoding="utf-8") as f: + f.write(content) + + with pytest.raises(RuntimeError, match="^The lock file is not compatible"): + _ = locker.lock_data + + def test_root_extras_dependencies_are_ordered( locker: Locker, root: ProjectPackage, fixture_base: Path ) -> None: