Skip to content

Commit 13f84d1

Browse files
committed
revert python-poetry#5770, provide new fix
1 parent 6ac2e1f commit 13f84d1

File tree

4 files changed

+85
-30
lines changed

4 files changed

+85
-30
lines changed

src/poetry/mixology/partial_solution.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44

55
from poetry.mixology.assignment import Assignment
66
from poetry.mixology.set_relation import SetRelation
7-
from poetry.mixology.term import Term
87

98

109
if TYPE_CHECKING:
1110
from poetry.core.packages.dependency import Dependency
1211
from poetry.core.packages.package import Package
1312

1413
from poetry.mixology.incompatibility import Incompatibility
14+
from poetry.mixology.term import Term
1515

1616

1717
class PartialSolution:
@@ -146,15 +146,6 @@ def _register(self, assignment: Assignment) -> None:
146146
"""
147147
name = assignment.dependency.complete_name
148148
old_positive = self._positive.get(name)
149-
if old_positive is None and assignment.dependency.features:
150-
old_positive_without_features = self._positive.get(
151-
assignment.dependency.name
152-
)
153-
if old_positive_without_features is not None:
154-
dep = old_positive_without_features.dependency.with_features(
155-
assignment.dependency.features
156-
)
157-
old_positive = Term(dep, is_positive=True)
158149
if old_positive is not None:
159150
value = old_positive.intersect(assignment)
160151
assert value is not None

src/poetry/mixology/version_solver.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,12 @@ def _choose_package_version(self) -> str | None:
378378
# Prefer packages with as few remaining versions as possible,
379379
# so that if a conflict is necessary it's forced quickly.
380380
def _get_min(dependency: Dependency) -> tuple[bool, int]:
381+
# Direct origin dependencies must be handled first: we don't want to resolve
382+
# a regular dependency for some package only to find later that we had a
383+
# direct-origin dependency.
384+
if dependency.is_direct_origin():
385+
return False, -1
386+
381387
if dependency.name in self._use_latest:
382388
# If we're forced to use the latest version of a package, it effectively
383389
# only has one version to choose from.
@@ -387,16 +393,6 @@ def _get_min(dependency: Dependency) -> tuple[bool, int]:
387393
if locked:
388394
return not dependency.marker.is_any(), 1
389395

390-
# VCS, URL, File or Directory dependencies
391-
# represent a single version
392-
if (
393-
dependency.is_vcs()
394-
or dependency.is_url()
395-
or dependency.is_file()
396-
or dependency.is_directory()
397-
):
398-
return not dependency.marker.is_any(), 1
399-
400396
try:
401397
return (
402398
not dependency.marker.is_any(),

src/poetry/puzzle/provider.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ def __init__(
141141
self._load_deferred = True
142142
self._source_root: Path | None = None
143143
self._installed_packages = installed if installed is not None else []
144+
self._direct_origin_packages: dict[str, Package] = {}
144145

145146
@property
146147
def pool(self) -> Pool:
@@ -269,18 +270,33 @@ def search_for(self, dependency: Dependency) -> list[DependencyPackage]:
269270
return PackageCollection(dependency, [self._package])
270271

271272
if dependency.is_direct_origin():
272-
packages = [self.search_for_direct_origin_dependency(dependency)]
273+
package = self.search_for_direct_origin_dependency(dependency)
274+
self._direct_origin_packages[dependency.name] = package
275+
return PackageCollection(dependency, [package])
273276

274-
else:
275-
packages = self._pool.find_packages(dependency)
276-
277-
packages.sort(
278-
key=lambda p: (
279-
not p.is_prerelease() and not dependency.allows_prereleases(),
280-
p.version,
281-
),
282-
reverse=True,
277+
# If we've previously found a direct-origin package that meets this dependency,
278+
# use it.
279+
#
280+
# We rely on the VersionSolver resolving direct-origin dependencies first.
281+
direct_origin_package = self._direct_origin_packages.get(dependency.name)
282+
if direct_origin_package is not None:
283+
package = direct_origin_package.with_features(dependency.features)
284+
packages = (
285+
[package]
286+
if package.satisfies(dependency, ignore_source_type=True)
287+
else []
283288
)
289+
return PackageCollection(dependency, packages)
290+
291+
packages = self._pool.find_packages(dependency)
292+
293+
packages.sort(
294+
key=lambda p: (
295+
not p.is_prerelease() and not dependency.allows_prereleases(),
296+
p.version,
297+
),
298+
reverse=True,
299+
)
284300

285301
if not packages:
286302
packages = self.search_for_installed_packages(dependency)

tests/puzzle/test_solver.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3596,3 +3596,55 @@ def test_solver_direct_origin_dependency_with_extras_requested_by_other_package(
35963596
assert op.package.version.text == "0.1.2"
35973597
assert op.package.source_type == "directory"
35983598
assert op.package.source_url == path
3599+
3600+
3601+
def test_solver_incompatible_dependency_with_and_without_extras(
3602+
solver: Solver, repo: Repository, package: ProjectPackage
3603+
):
3604+
"""
3605+
The solver first encounters a requirement for google-auth and then later an
3606+
incompatible requirement for google-auth[aiohttp].
3607+
3608+
Testcase derived from https://github.com/python-poetry/poetry/issues/6054.
3609+
"""
3610+
# Incompatible requirements from foo and bar2.
3611+
foo = get_package("foo", "1.0.0")
3612+
foo.add_dependency(Factory.create_dependency("google-auth", {"version": "^1"}))
3613+
3614+
bar = get_package("bar", "1.0.0")
3615+
3616+
bar2 = get_package("bar", "2.0.0")
3617+
bar2.add_dependency(
3618+
Factory.create_dependency(
3619+
"google-auth", {"version": "^2", "extras": ["aiohttp"]}
3620+
)
3621+
)
3622+
3623+
baz = get_package("baz", "1.0.0") # required by google-auth[aiohttp]
3624+
3625+
google_auth = get_package("google-auth", "1.2.3")
3626+
google_auth.extras = {"aiohttp": [get_dependency("baz", "^1.0")]}
3627+
3628+
google_auth2 = get_package("google-auth", "2.3.4")
3629+
google_auth2.extras = {"aiohttp": [get_dependency("baz", "^1.0")]}
3630+
3631+
repo.add_package(foo)
3632+
repo.add_package(bar)
3633+
repo.add_package(bar2)
3634+
repo.add_package(baz)
3635+
repo.add_package(google_auth)
3636+
repo.add_package(google_auth2)
3637+
3638+
package.add_dependency(Factory.create_dependency("foo", ">=1"))
3639+
package.add_dependency(Factory.create_dependency("bar", ">=1"))
3640+
3641+
transaction = solver.solve()
3642+
3643+
check_solver_result(
3644+
transaction,
3645+
[
3646+
{"job": "install", "package": google_auth},
3647+
{"job": "install", "package": bar},
3648+
{"job": "install", "package": foo},
3649+
],
3650+
)

0 commit comments

Comments
 (0)