Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/poetry/core/constraints/version/empty_constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ def is_any(self) -> bool:
def is_simple(self) -> bool:
return True

def has_upper_bound(self) -> bool:
# Rationale:
# 1. If no version can satisfy the constraint,
# this is like an upper bound of 0 (not included).
# 2. The opposite of an empty constraint, which is *, has no upper bound
# and the two extremes often behave the other way around.
return True

def allows(self, version: Version) -> bool:
return False

Expand Down
4 changes: 4 additions & 0 deletions src/poetry/core/constraints/version/version_constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ def is_any(self) -> bool:
def is_simple(self) -> bool:
raise NotImplementedError

@abstractmethod
def has_upper_bound(self) -> bool:
raise NotImplementedError

@abstractmethod
def allows(self, version: Version) -> bool:
raise NotImplementedError
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ def allowed_max(self) -> Version | None:
# https://peps.python.org/pep-0440/#exclusive-ordered-comparison
return self.max.first_devrelease()

def has_upper_bound(self) -> bool:
return self.max is not None

def allows_lower(self, other: VersionRangeConstraint) -> bool:
_this, _other = self.allowed_min, other.allowed_min

Expand Down
3 changes: 3 additions & 0 deletions src/poetry/core/constraints/version/version_union.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ def is_any(self) -> bool:
def is_simple(self) -> bool:
return self.excludes_single_version

def has_upper_bound(self) -> bool:
return all(constraint.has_upper_bound() for constraint in self._ranges)

def allows(self, version: Version) -> bool:
if self.excludes_single_version:
# when excluded version is local, special handling is required
Expand Down
29 changes: 29 additions & 0 deletions tests/constraints/version/test_version_constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,32 @@ def test_constraints_are_hashable(constraint: VersionConstraint) -> None:
# We're just testing that constraints are hashable, there's nothing much to say
# about the result.
hash(constraint)


@pytest.mark.parametrize(
("constraint", "expected"),
[
(EmptyConstraint(), True),
(Version.parse("1"), True),
(VersionRange(), False),
(VersionRange(Version.parse("1")), False),
(VersionRange(max=Version.parse("2")), True),
(VersionRange(Version.parse("1"), Version.parse("2")), True),
(
VersionUnion(
VersionRange(Version.parse("1"), Version.parse("2")),
VersionRange(Version.parse("3"), Version.parse("4")),
),
True,
),
(
VersionUnion(
VersionRange(Version.parse("1"), Version.parse("2")),
VersionRange(Version.parse("3")),
),
False,
),
],
)
def test_has_upper_bound(constraint: VersionConstraint, expected: bool) -> None:
assert constraint.has_upper_bound() is expected