From 94be7d460bf07549ce50f285e25d44b114deee5b Mon Sep 17 00:00:00 2001 From: Nathan Date: Thu, 9 May 2024 19:51:07 -0500 Subject: [PATCH] Allows, intersect, union methods updates --- .../core/constraints/generic/constraint.py | 76 ++++++++++++++----- src/poetry/core/version/markers.py | 30 +++----- tests/constraints/generic/test_constraint.py | 35 +++++++++ tests/constraints/generic/test_main.py | 4 +- tests/version/test_markers.py | 9 ++- 5 files changed, 113 insertions(+), 41 deletions(-) diff --git a/src/poetry/core/constraints/generic/constraint.py b/src/poetry/core/constraints/generic/constraint.py index 044770890..123ac372a 100644 --- a/src/poetry/core/constraints/generic/constraint.py +++ b/src/poetry/core/constraints/generic/constraint.py @@ -67,6 +67,16 @@ 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") @@ -74,10 +84,26 @@ def allows(self, other: BaseConstraint) -> bool: 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 @@ -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 @@ -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() @@ -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() @@ -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}" diff --git a/src/poetry/core/version/markers.py b/src/poetry/core/version/markers.py index d305bf35c..cfade3507 100644 --- a/src/poetry/core/version/markers.py +++ b/src/poetry/core/version/markers.py @@ -325,7 +325,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, @@ -335,7 +335,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) @@ -353,7 +353,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 @@ -390,15 +390,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 @@ -456,9 +447,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: @@ -471,7 +463,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}"' @@ -918,12 +910,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": diff --git a/tests/constraints/generic/test_constraint.py b/tests/constraints/generic/test_constraint.py index 6bc75c2ad..aea28d9a3 100644 --- a/tests/constraints/generic/test_constraint.py +++ b/tests/constraints/generic/test_constraint.py @@ -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( @@ -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( diff --git a/tests/constraints/generic/test_main.py b/tests/constraints/generic/test_main.py index ead0cd351..7e04cbd91 100644 --- a/tests/constraints/generic/test_main.py +++ b/tests/constraints/generic/test_main.py @@ -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"), @@ -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")), ), ], diff --git a/tests/version/test_markers.py b/tests/version/test_markers.py index 7645980fa..807ac80eb 100644 --- a/tests/version/test_markers.py +++ b/tests/version/test_markers.py @@ -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( @@ -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"},