Skip to content

Commit

Permalink
Allows, intersect, union methods updates
Browse files Browse the repository at this point in the history
  • Loading branch information
npapapietro committed May 10, 2024
1 parent cf6847b commit 157cbe1
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 41 deletions.
76 changes: 57 additions & 19 deletions src/poetry/core/constraints/generic/constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,43 @@ def operator(self) -> str:
return self._operator

def allows(self, other: BaseConstraint) -> bool:
"""Logic table to help
|| != | == | in | not in
--------||--------|--------|--------|--------
!= || != | != | not in | not in
== || != | == | in | not in
in || not in | in | in | not in
not in || not in | not in | not in | false
"""
if not isinstance(other, Constraint):
raise ValueError("Unimplemented comparison of constraints")

is_equal_op = self._operator == "=="
is_non_equal_op = self._operator == "!="
is_other_equal_op = other.operator == "=="
is_other_non_equal_op = other.operator == "!="
is_in_op = self._operator == "in"
is_not_in_op = self._operator == "not in"
is_other_in_op = other.operator == "in"
is_other_not_in_op = other.operator == "not in"

if is_equal_op and is_other_equal_op:
return self._value == other.value

if (
is_in_op
and is_other_in_op
or is_in_op
and is_equal_op
or is_equal_op
and is_other_in_op
or is_in_op
and is_other_equal_op
):
return bool(self._trans_op_str["in"](other.value, self._value))

if (
is_equal_op
and is_other_non_equal_op
Expand All @@ -88,8 +114,21 @@ def allows(self, other: BaseConstraint) -> bool:
):
return self._value != other.value

if self._operator in {"in", "not in"} and other.operator == "==":
return bool(self._trans_op_str[self._operator](other.value, self._value))
if (
is_in_op
and is_other_non_equal_op
or is_in_op
and is_other_not_in_op
or is_not_in_op
and is_other_non_equal_op
or is_not_in_op
and is_other_equal_op
or is_not_in_op
and is_other_in_op
or is_non_equal_op
and is_other_not_in_op
):
return bool(self._trans_op_str["not in"](other.value, self._value))

return False

Expand Down Expand Up @@ -126,20 +165,20 @@ def intersect(self, other: BaseConstraint) -> BaseConstraint:
return self

if (
(self.operator == "!=" and other.operator == "==")
or (self.operator == "not in" and other.operator == "in")
) and self.allows(other):
self.operator in {"!=", "not in", "in"}
and other.operator in {"==", "in", "not in"}
and self.allows(other)
):
return other

if (
(other.operator == "!=" and self.operator == "==")
or (other.operator == "not in" and self.operator == "in")
) and other.allows(self):
other.operator in {"!=", "not in", "in"}
and self.operator in {"==", "in", "not in"}
and other.allows(self)
):
return self

if (other.operator == "!=" and self.operator == "!=") or (
other.operator == "not in" and self.operator == "not in"
):
if other.operator in {"!=", "not in"} and self.operator in {"!=", "not in"}:
return MultiConstraint(self, other)

return EmptyConstraint()
Expand All @@ -154,20 +193,18 @@ def union(self, other: BaseConstraint) -> BaseConstraint:
return self

if (
(self.operator == "!=" and other.operator == "==")
or (self.operator == "not in" and other.operator == "in")
self.operator in {"!=", "not in", "in"}
and other.operator in {"==", "in", "not in"}
) and self.allows(other):
return self

if (
(other.operator == "!=" and self.operator == "==")
or (other.operator == "not in" and self.operator == "in")
other.operator in {"!=", "not in", "in"}
and self.operator in {"==", "in", "not in"}
) and other.allows(self):
return other

if (other.operator == "==" and self.operator == "==") or (
other.operator == "in" and self.operator == "in"
):
if other.operator in {"==", "in"} and self.operator in {"==", "in"}:
return UnionConstraint(self, other)

return AnyConstraint()
Expand All @@ -194,5 +231,6 @@ def __hash__(self) -> int:
return hash((self._operator, self._value))

def __str__(self) -> str:
space = " " if self._operator in {"in", "not in"} else ""
op = self._operator if self._operator != "==" else ""
return f"{op}{self._value}"
return f"{op}{space}{self._value}"
30 changes: 12 additions & 18 deletions src/poetry/core/version/markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ def __init__(
self,
name: str,
constraint: str | BaseConstraint | VersionConstraint,
str_cmp: bool = False,
swapped_name_value: bool = False,
) -> None:
from poetry.core.constraints.generic import (
parse_constraint as parse_generic_constraint,
Expand All @@ -336,7 +336,7 @@ def __init__(
parsed_constraint: BaseConstraint | VersionConstraint
parser: Callable[[str], BaseConstraint | VersionConstraint]
original_constraint_string = constraint_string = str(constraint)
self._str_cmp: bool = str_cmp
self._swapped_name_value: bool = swapped_name_value

# Extract operator and value
m = self._CONSTRAINT_RE.match(constraint_string)
Expand All @@ -354,7 +354,7 @@ def __init__(
# when string comparison flag is set. We let
# the generic constraint parser handle it and
# fixup the constraint object after
if name == "platform_release" and str_cmp:
if name == "platform_release" and swapped_name_value:
pass
elif name in self._VERSION_LIKE_MARKER_NAME:
parser = parse_marker_version_constraint
Expand Down Expand Up @@ -391,15 +391,6 @@ def __init__(
f"Invalid marker for '{name}': {original_constraint_string}"
) from e

# This is a fixup on constraint if its a string like
# comparison for platform_release
if (
self._operator in {"in", "not in"}
and self._str_cmp
and isinstance(parsed_constraint, Constraint)
):
parsed_constraint = Constraint(self._value, self._operator)

super().__init__(name, parsed_constraint)

@property
Expand Down Expand Up @@ -457,9 +448,10 @@ def invert(self) -> BaseMarker:
# We should never go there
raise RuntimeError(f"Invalid marker operator '{self._operator}'")

constraint = f"{self._name} {operator} '{self._value}'"
if self._str_cmp:
if self._swapped_name_value:
constraint = f'"{self._value}" {operator} {self._name}'
else:
constraint = f"{self._name} {operator} '{self._value}'"
return parse_marker(constraint)

def __eq__(self, other: object) -> bool:
Expand All @@ -472,7 +464,7 @@ def __hash__(self) -> int:
return hash(self._key)

def __str__(self) -> str:
if self._str_cmp:
if self._swapped_name_value:
return f'"{self._value}" {self._operator} {self._name}'
return f'{self._name} {self._operator} "{self._value}"'

Expand Down Expand Up @@ -919,12 +911,14 @@ def _compact_markers(

elif token.data == f"{tree_prefix}item":
name, op, value = token.children
str_cmp = value.type == f"{tree_prefix}MARKER_NAME"
if str_cmp:
swapped_name_value = value.type == f"{tree_prefix}MARKER_NAME"
if swapped_name_value:
name, value = value, name

value = value[1:-1]
sub_marker = SingleMarker(str(name), f"{op}{value}", str_cmp=str_cmp)
sub_marker = SingleMarker(
str(name), f"{op}{value}", swapped_name_value=swapped_name_value
)
groups[-1].append(sub_marker)

elif token.data == f"{tree_prefix}BOOL_OP" and token.children[0] == "or":
Expand Down
35 changes: 35 additions & 0 deletions tests/constraints/generic/test_constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,26 @@ def test_invert(constraint: BaseConstraint, inverted: BaseConstraint) -> None:
Constraint("tegra", "not in"), Constraint("rpi-v8", "not in")
),
),
(
Constraint("5.10.123-tegra", "!="),
Constraint("tegra", "not in"),
Constraint("tegra", "not in"),
),
(
Constraint("5.10.123-tegra", "!="),
Constraint("tegra", "in"),
EmptyConstraint(),
),
(
Constraint("5.10.123-tegra", "=="),
Constraint("tegra", "in"),
Constraint("5.10.123-tegra"),
),
(
Constraint("5.10.123", "=="),
Constraint("tegra", "in"),
EmptyConstraint(),
),
],
)
def test_intersect(
Expand Down Expand Up @@ -500,6 +520,21 @@ def test_intersect(
Constraint("tegra", "not in"),
AnyConstraint(),
),
(
Constraint("5.10.123-tegra", "!="),
Constraint("tegra", "in"),
AnyConstraint(),
),
(
Constraint("5.10.123", "!="),
Constraint("tegra", "in"),
AnyConstraint(),
),
(
Constraint("5.10.123-tegra", "!="),
Constraint("tegra", "not in"),
Constraint("5.10.123-tegra", "!="),
),
],
)
def test_union(
Expand Down
4 changes: 2 additions & 2 deletions tests/constraints/generic/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_parse_constraint(input: str, constraint: AnyConstraint | Constraint) ->
),
),
(
"not integra,not inrpi-v8",
"not in tegra,not in rpi-v8",
MultiConstraint(
Constraint("tegra", "not in"),
Constraint("rpi-v8", "not in"),
Expand All @@ -63,7 +63,7 @@ def test_parse_constraint_multi(input: str, constraint: MultiConstraint) -> None
UnionConstraint(Constraint("win32"), Constraint("linux2", "!=")),
),
(
"integra || inrpi-v8",
"in tegra || in rpi-v8",
UnionConstraint(Constraint("tegra", "in"), Constraint("rpi-v8", "in")),
),
],
Expand Down
9 changes: 7 additions & 2 deletions tests/version/test_markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ def test_parse_marker(marker: str) -> None:
"platform_machine",
"!=aarch64, !=loongarch64",
),
('"tegra" not in platform_release', "platform_release", "not integra"),
('"rpi-v8" in platform_release', "platform_release", "inrpi-v8"),
('"tegra" not in platform_release', "platform_release", "not in tegra"),
('"rpi-v8" in platform_release', "platform_release", "in rpi-v8"),
],
)
def test_parse_single_marker(
Expand Down Expand Up @@ -978,6 +978,11 @@ def test_multi_marker_removes_duplicates() -> None:
{"platform_release": "4.9.254-tegra"},
True,
),
(
"platform_release != '4.9.253-tegra'",
{"platform_release": "4.9.253"},
True,
),
(
"platform_release >= '6.6.0+rpt-rpi-v8'",
{"platform_release": "6.6.20+rpt-rpi-v8"},
Expand Down

0 comments on commit 157cbe1

Please sign in to comment.