Skip to content

Commit

Permalink
type_hint Context
Browse files Browse the repository at this point in the history
Signed-off-by: Cristian Le <[email protected]>
  • Loading branch information
LecrisUT committed Aug 9, 2023
1 parent c7c6135 commit b7aa7e9
Showing 1 changed file with 51 additions and 39 deletions.
90 changes: 51 additions & 39 deletions fmf/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
See https://fmf.readthedocs.io/en/latest/modules.html#fmf.Tree.adjust
"""

from __future__ import annotations

import re
from collections.abc import Callable


class CannotDecide(Exception):
Expand All @@ -34,7 +37,7 @@ class InvalidContext(Exception):
class ContextValue:
""" Value for dimension """

def __init__(self, origin):
def __init__(self, origin: str | tuple[str, ...]):
"""
ContextValue("foo-1.2.3")
ContextValue(["foo", "1", "2", "3"])
Expand All @@ -44,13 +47,13 @@ def __init__(self, origin):
else:
self._to_compare = self._split_to_version(origin)

def __eq__(self, other):
def __eq__(self, other: ContextValue):
if isinstance(other, self.__class__):
return self._to_compare == other._to_compare
else:
return False

def __ne__(self, other):
def __ne__(self, other: ContextValue):
return not self.__eq__(other)

def __str__(self):
Expand All @@ -59,7 +62,11 @@ def __str__(self):
def __repr__(self):
return "{}({})".format(self.__class__.__name__, repr(self._to_compare))

def version_cmp(self, other, minor_mode=False, ordered=True):
def version_cmp(
self,
other: ContextValue,
minor_mode: bool = False,
ordered: bool = True) -> int:
"""
Comparing two ContextValue objects
Expand Down Expand Up @@ -138,7 +145,7 @@ def version_cmp(self, other, minor_mode=False, ordered=True):
return -1 # other is larger (more pars)

@staticmethod
def compare(first, second):
def compare(first: str, second: str):
""" compare two version parts """
# Ideally use `from packaging import version` but we need older
# python support too so very rough
Expand All @@ -155,7 +162,7 @@ def compare(first, second):
(first_version < second_version))

@staticmethod
def _split_to_version(text):
def _split_to_version(text: str) -> tuple[str, ...]:
"""
Try to split text into name + version parts
Expand Down Expand Up @@ -183,119 +190,121 @@ def __hash__(self):

class Context:
""" Represents https://fmf.readthedocs.io/en/latest/context.html """

# Operators' definitions

def _op_defined(self, dimension_name, values):
def _op_defined(self, dimension_name: str, values: list[ContextValue]):
""" 'is defined' operator """
return dimension_name in self._dimensions

def _op_not_defined(self, dimension_name, values):
def _op_not_defined(self, dimension_name: str, values: list[ContextValue]):
""" 'is not defined' operator """
return dimension_name not in self._dimensions

def _op_eq(self, dimension_name, values):
def _op_eq(self, dimension_name: str, values: list[ContextValue]):
""" '=' operator """

def comparator(dimension_value, it_val):
def comparator(dimension_value: ContextValue, it_val: ContextValue) -> bool:
return dimension_value.version_cmp(it_val, ordered=False) == 0

return self._op_core(dimension_name, values, comparator)

def _op_not_eq(self, dimension_name, values):
def _op_not_eq(self, dimension_name: str, values: list[ContextValue]):
""" '!=' operator """

def comparator(dimension_value, it_val):
def comparator(dimension_value: ContextValue, it_val: ContextValue) -> bool:
return dimension_value.version_cmp(it_val, ordered=False) != 0

return self._op_core(dimension_name, values, comparator)

def _op_minor_eq(self, dimension_name, values):
def _op_minor_eq(self, dimension_name: str, values: list[ContextValue]):
""" '~=' operator """

def comparator(dimension_value, it_val):
def comparator(dimension_value: ContextValue, it_val: ContextValue) -> bool:
return dimension_value.version_cmp(
it_val, minor_mode=True, ordered=False) == 0

return self._op_core(dimension_name, values, comparator)

def _op_minor_not_eq(self, dimension_name, values):
def _op_minor_not_eq(self, dimension_name: str, values: list[ContextValue]):
""" '~!=' operator """

def comparator(dimension_value, it_val):
def comparator(dimension_value: ContextValue, it_val: ContextValue) -> bool:
return dimension_value.version_cmp(
it_val, minor_mode=True, ordered=False) != 0

return self._op_core(dimension_name, values, comparator)

def _op_minor_less_or_eq(self, dimension_name, values):
def _op_minor_less_or_eq(self, dimension_name: str, values: list[ContextValue]):
""" '~<=' operator """

def comparator(dimension_value, it_val):
def comparator(dimension_value: ContextValue, it_val: ContextValue) -> bool:
return dimension_value.version_cmp(
it_val, minor_mode=True, ordered=True) <= 0

return self._op_core(dimension_name, values, comparator)

def _op_minor_less(self, dimension_name, values):
def _op_minor_less(self, dimension_name: str, values: list[ContextValue]):
""" '~<' operator """

def comparator(dimension_value, it_val):
def comparator(dimension_value: ContextValue, it_val: ContextValue) -> bool:
return dimension_value.version_cmp(
it_val, minor_mode=True, ordered=True) < 0

return self._op_core(dimension_name, values, comparator)

def _op_less(self, dimension_name, values):
def _op_less(self, dimension_name: str, values: list[ContextValue]):
""" '<' operator """

def comparator(dimension_value, it_val):
def comparator(dimension_value: ContextValue, it_val: ContextValue) -> bool:
return dimension_value.version_cmp(it_val, ordered=True) < 0

return self._op_core(dimension_name, values, comparator)

def _op_less_or_equal(self, dimension_name, values):
def _op_less_or_equal(self, dimension_name: str, values: list[ContextValue]):
""" '<=' operator """

def comparator(dimension_value, it_val):
def comparator(dimension_value: ContextValue, it_val: ContextValue) -> bool:
return dimension_value.version_cmp(it_val, ordered=True) <= 0

return self._op_core(dimension_name, values, comparator)

def _op_greater_or_equal(self, dimension_name, values):
def _op_greater_or_equal(self, dimension_name: str, values: list[ContextValue]):
""" '>=' operator """

def comparator(dimension_value, it_val):
def comparator(dimension_value: ContextValue, it_val: ContextValue) -> bool:
return dimension_value.version_cmp(it_val, ordered=True) >= 0

return self._op_core(dimension_name, values, comparator)

def _op_minor_greater_or_equal(self, dimension_name, values):
def _op_minor_greater_or_equal(self, dimension_name: str, values: list[ContextValue]):
""" '~>=' operator """

def comparator(dimension_value, it_val):
def comparator(dimension_value: ContextValue, it_val: ContextValue) -> bool:
return dimension_value.version_cmp(
it_val, minor_mode=True, ordered=True) >= 0

return self._op_core(dimension_name, values, comparator)

def _op_greater(self, dimension_name, values):
def _op_greater(self, dimension_name: str, values: list[ContextValue]):
""" '>' operator """

def comparator(dimension_value, it_val):
def comparator(dimension_value: ContextValue, it_val: ContextValue) -> bool:
return dimension_value.version_cmp(it_val, ordered=True) > 0

return self._op_core(dimension_name, values, comparator)

def _op_minor_greater(self, dimension_name, values):
def _op_minor_greater(self, dimension_name: str, values: list[ContextValue]):
""" '~>' operator """

def comparator(dimension_value, it_val):
def comparator(dimension_value: ContextValue, it_val: ContextValue) -> bool:
return dimension_value.version_cmp(
it_val, minor_mode=True, ordered=True) > 0

return self._op_core(dimension_name, values, comparator)

def _op_core(self, dimension_name, values, comparator):
def _op_core(self, dimension_name: str, values: list[ContextValue],
comparator: Callable[[ContextValue, ContextValue], bool]):
"""
Evaluate value from dimension vs target values combination
Expand Down Expand Up @@ -362,6 +371,7 @@ def _op_core(self, dimension_name, values, comparator):

# To split by 'or' operator
re_or_split = re.compile(r'\bor\b')
_dimensions: dict[str]

def __init__(self, *args, **kwargs):
"""
Expand Down Expand Up @@ -393,8 +403,10 @@ def __init__(self, *args, **kwargs):
[self.parse_value(val) for val in values]
)

ExpressionType = tuple[str | None, str | bool, list[str] | None]

@staticmethod
def parse_rule(rule):
def parse_rule(rule: str | bool) -> list[list[ExpressionType]]:
"""
Parses rule into expressions
Expand Down Expand Up @@ -437,12 +449,12 @@ def parse_rule(rule):
return parsed_rule

@staticmethod
def parse_value(value):
def parse_value(value: str) -> ContextValue:
""" Single place to convert to ContextValue """
return ContextValue(str(value))

@staticmethod
def split_rule_to_groups(rule):
def split_rule_to_groups(rule: str) -> list[list[str]]:
"""
Split rule into nested lists, no real parsing
Expand All @@ -467,7 +479,7 @@ def split_rule_to_groups(rule):
return rule_parts

@staticmethod
def split_expression(expression):
def split_expression(expression: str) -> ExpressionType:
"""
Split expression to dimension name, operator and values
Expand Down Expand Up @@ -500,7 +512,7 @@ def split_expression(expression):
return (match.group(1), match.group(2), None)
raise InvalidRule("Cannot parse expression '{}'.".format(expression))

def matches(self, rule):
def matches(self, rule: str | bool) -> bool:
"""
Does the rule match the current Context?
Expand Down Expand Up @@ -570,7 +582,7 @@ def matches(self, rule):
else:
raise CannotDecide() # It's up to callee how to treat this

def evaluate(self, expression):
def evaluate(self, expression: ExpressionType) -> bool:
dimension_name, operator, values = expression
if isinstance(operator, bool):
return operator
Expand Down

0 comments on commit b7aa7e9

Please sign in to comment.