Skip to content

Commit

Permalink
provider: raise error if there are incompatible constraints in the re…
Browse files Browse the repository at this point in the history
…quirements of a package
  • Loading branch information
radoering committed Dec 1, 2022
1 parent c6a5f3f commit ee80212
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 6 deletions.
29 changes: 23 additions & 6 deletions src/poetry/puzzle/provider.py
Expand Up @@ -59,6 +59,18 @@
logger = logging.getLogger(__name__)


class IncompatibleConstraintsError(Exception):
"""
Exception when there are duplicate dependencies with incompatible constraints.
"""

def __init__(self, package: Package, *dependencies: Dependency) -> None:
constraints = "\n".join(dep.to_pep_508() for dep in dependencies)
super().__init__(
f"Incompatible constraints in requirements of {package}:\n{constraints}"
)


class Indicator(ProgressIndicator):
CONTEXT: str | None = None

Expand Down Expand Up @@ -740,7 +752,7 @@ def fmt_warning(d: Dependency) -> str:
f"<warning>Different requirements found for {warnings}.</warning>"
)

deps = self._handle_any_marker_dependencies(deps)
deps = self._handle_any_marker_dependencies(package, deps)

overrides = []
overrides_marker_intersection: BaseMarker = AnyMarker()
Expand Down Expand Up @@ -975,7 +987,7 @@ def _merge_dependencies_by_marker(
return deps

def _handle_any_marker_dependencies(
self, dependencies: list[Dependency]
self, package: Package, dependencies: list[Dependency]
) -> list[Dependency]:
"""
We need to check if one of the duplicate dependencies
Expand All @@ -999,11 +1011,16 @@ def _handle_any_marker_dependencies(
any_markers_dependencies = [d for d in dependencies if d.marker.is_any()]
other_markers_dependencies = [d for d in dependencies if not d.marker.is_any()]

for dep_any in any_markers_dependencies:
if any_markers_dependencies:
for dep_other in other_markers_dependencies:
dep_other.constraint = dep_other.constraint.intersect(
dep_any.constraint
)
new_constraint = dep_other.constraint
for dep_any in any_markers_dependencies:
new_constraint = new_constraint.intersect(dep_any.constraint)
if new_constraint.is_empty():
raise IncompatibleConstraintsError(
package, dep_other, *any_markers_dependencies
)
dep_other.constraint = new_constraint

marker = other_markers_dependencies[0].marker
for other_dep in other_markers_dependencies[1:]:
Expand Down
24 changes: 24 additions & 0 deletions tests/puzzle/test_solver.py
@@ -1,5 +1,7 @@
from __future__ import annotations

import re

from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
Expand All @@ -19,6 +21,7 @@
from poetry.packages import DependencyPackage
from poetry.puzzle import Solver
from poetry.puzzle.exceptions import SolverProblemError
from poetry.puzzle.provider import IncompatibleConstraintsError
from poetry.repositories.repository import Repository
from poetry.repositories.repository_pool import RepositoryPool
from poetry.utils.env import MockEnv
Expand Down Expand Up @@ -1480,6 +1483,27 @@ def test_solver_duplicate_dependencies_different_constraints_merge_no_markers(
)


def test_solver_duplicate_dependencies_different_constraints_conflict(
solver: Solver, repo: Repository, package: ProjectPackage
) -> None:
package.add_dependency(Factory.create_dependency("A", ">=1.1"))
package.add_dependency(
Factory.create_dependency("A", {"version": "<1.1", "python": "3.10"})
)

repo.add_package(get_package("A", "1.0"))
repo.add_package(get_package("A", "1.1"))
repo.add_package(get_package("A", "1.2"))

expectation = (
"Incompatible constraints in requirements of root (1.0):\n"
'A (<1.1) ; python_version == "3.10"\n'
"A (>=1.1)"
)
with pytest.raises(IncompatibleConstraintsError, match=re.escape(expectation)):
solver.solve()


def test_solver_duplicate_dependencies_different_constraints_discard_no_markers1(
solver: Solver, repo: Repository, package: ProjectPackage
) -> None:
Expand Down

0 comments on commit ee80212

Please sign in to comment.