From b3dba519d9f8508614b75b4fc6a752cce1e96a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 2 Oct 2020 01:23:39 +0200 Subject: [PATCH] Fix resolution of packages with missing required extras --- poetry/puzzle/provider.py | 16 ++++++-------- poetry/repositories/repository.py | 7 +----- tests/puzzle/test_solver.py | 36 ++++++++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/poetry/puzzle/provider.py b/poetry/puzzle/provider.py index 44d8b6e3796..cd06b4738af 100755 --- a/poetry/puzzle/provider.py +++ b/poetry/puzzle/provider.py @@ -453,23 +453,19 @@ def complete_package( self.search_for_url(r) optional_dependencies = [] - activated_extras = [] - for extra in package.dependency.extras: - if extra not in package.extras: - continue - - activated_extras.append(extra) - optional_dependencies += [d.name for d in package.extras[extra]] - _dependencies = [] # If some extras/features were required, we need to # add a special dependency representing the base package # to the current package if package.dependency.extras: - if activated_extras: - package = package.with_features(activated_extras) + for extra in package.dependency.extras: + if extra not in package.extras: + continue + + optional_dependencies += [d.name for d in package.extras[extra]] + package = package.with_features(list(package.dependency.extras)) _dependencies.append(package.without_features().to_dependency()) for dep in requires: diff --git a/poetry/repositories/repository.py b/poetry/repositories/repository.py index 556c77e9862..1ebe702bb9c 100755 --- a/poetry/repositories/repository.py +++ b/poetry/repositories/repository.py @@ -24,14 +24,9 @@ def name(self): def package(self, name, version, extras=None): name = name.lower() - if extras is None: - extras = [] - for package in self.packages: if name == package.name and package.version.text == version: - package = package.with_features(extras) - - return package + return package.clone() def find_packages(self, dependency): constraint = dependency.constraint diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index bf9075e186a..38ca6a4e292 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -2435,7 +2435,6 @@ def test_solver_can_resolve_transitive_extras(solver, repo, package): requests = get_package("requests", "2.24.0") requests.add_dependency(Factory.create_dependency("certifi", ">=2017.4.17")) dep = get_dependency("PyOpenSSL", ">=0.14") - dep.in_extras.append("security") requests.add_dependency( Factory.create_dependency("PyOpenSSL", {"version": ">=0.14", "optional": True}) ) @@ -2465,6 +2464,41 @@ def test_solver_can_resolve_transitive_extras(solver, repo, package): ) +def test_solver_can_resolve_for_packages_with_missing_extras(solver, repo, package): + package.add_dependency( + Factory.create_dependency( + "django-anymail", {"version": "^6.0", "extras": ["postmark"]} + ) + ) + + django_anymail = get_package("django-anymail", "6.1.0") + django_anymail.add_dependency(Factory.create_dependency("django", ">=2.0")) + django_anymail.add_dependency(Factory.create_dependency("requests", ">=2.4.3")) + django_anymail.add_dependency( + Factory.create_dependency("boto3", {"version": "*", "optional": True}) + ) + django_anymail.extras["amazon_ses"] = [Factory.create_dependency("boto3", "*")] + django = get_package("django", "2.2.0") + boto3 = get_package("boto3", "1.0.0") + requests = get_package("requests", "2.24.0") + + repo.add_package(django_anymail) + repo.add_package(django) + repo.add_package(boto3) + repo.add_package(requests) + + ops = solver.solve() + + check_solver_result( + ops, + [ + {"job": "install", "package": django}, + {"job": "install", "package": requests}, + {"job": "install", "package": django_anymail}, + ], + ) + + def test_solver_can_resolve_python_restricted_package_dependencies( solver, repo, package, locked ):