diff --git a/docs/context.rst b/docs/context.rst index 6d4ea7f..5250653 100644 --- a/docs/context.rst +++ b/docs/context.rst @@ -47,7 +47,7 @@ supported operators consult the following grammar outline:: expression ::= 'true' | 'false' dimension ::= [[:alnum:]]+ binary_operator ::= '==' | '!=' | '<' | '<=' | '>' | '>=' | - '~=' | '~!=' | '~<' | '~<=' | '~>' | '~>=' + '~=' | '~!=' | '~<' | '~<=' | '~>' | '~>=' | '~' | '!~' unary_operator ::= 'is defined' | 'is not defined' values ::= value (',' value)* value ::= [[:alnum:]]+ @@ -65,6 +65,12 @@ Let's demonstrate the syntax on a couple of real-life examples:: # check whether a dimension is defined collection is not defined + # search dimension value for a regular expression + initiator ~ .*-ci + + # make sure that the value does not match given regular expression + arch !~ ppc64.* + # disable adjust rule (e.g. during debugging / experimenting) false and diff --git a/docs/releases.rst b/docs/releases.rst index 944383d..154cabf 100644 --- a/docs/releases.rst +++ b/docs/releases.rst @@ -5,6 +5,14 @@ ====================== +fmf-1.6.0 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to search :ref:`context` dimension values using regular +expressions, it is now possible to use operator ``~`` for matching +patterns and operator ``!~`` for non matching patterns. + + fmf-1.5.0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/fmf/context.py b/fmf/context.py index 61404f0..56a953a 100644 --- a/fmf/context.py +++ b/fmf/context.py @@ -44,6 +44,9 @@ def __init__(self, origin): else: self._to_compare = self._split_to_version(origin) + # Store the original string for regexp processing + self.origin = origin + def __eq__(self, other): if isinstance(other, self.__class__): return self._to_compare == other._to_compare @@ -238,6 +241,22 @@ def comparator(dimension_value, it_val): return self._op_core(dimension_name, values, comparator) + def _op_match(self, dimension_name, values): + """ '~' operator, regular expression matches """ + + def comparator(dimension_value, it_val): + return re.search(it_val.origin, dimension_value.origin) is not None + + return self._op_core(dimension_name, values, comparator) + + def _op_not_match(self, dimension_name, values): + """ '~' operator, regular expression does not match """ + + def comparator(dimension_value, it_val): + return re.search(it_val.origin, dimension_value.origin) is None + + return self._op_core(dimension_name, values, comparator) + def _op_minor_eq(self, dimension_name, values): """ '~=' operator """ @@ -365,8 +384,10 @@ def _op_core(self, dimension_name, values, comparator): "~<=": _op_minor_less_or_eq, "==": _op_eq, "~=": _op_minor_eq, + "~": _op_match, "!=": _op_not_eq, "~!=": _op_minor_not_eq, + "!~": _op_not_match, ">=": _op_greater_or_equal, "~>=": _op_minor_greater_or_equal, ">": _op_greater, diff --git a/tests/unit/test_context.py b/tests/unit/test_context.py index 5d17891..915d57c 100644 --- a/tests/unit/test_context.py +++ b/tests/unit/test_context.py @@ -282,6 +282,31 @@ def test_case_insensitive(self): assert python.matches("component > python3-3.7") assert python.matches("component < PYTHON3-3.9") + def test_regular_expression_matching(self): + """ Matching regular expressions """ + + assert Context(distro="fedora-42").matches("distro ~ ^fedora-42$") + assert Context(distro="fedora-42").matches("distro ~ fedora") + assert Context(distro="fedora-42").matches("distro ~ fedora|rhel") + assert Context(distro="fedora-42").matches("distro ~ fedora-4.*") + assert not Context(distro="fedora-42").matches("distro ~ fedora-3.*") + assert not Context(distro="fedora-42").matches("distro ~ ubuntu") + + assert Context(arch="ppc64").matches("arch ~ ppc64.*") + assert Context(arch="ppc64le").matches("arch ~ ppc64.*") + assert not Context(arch="ppc64le").matches("arch ~ ppc64$") + + assert not Context(distro="fedora-42").matches("distro !~ ^fedora-42$") + assert not Context(distro="fedora-42").matches("distro !~ fedora") + assert not Context(distro="fedora-42").matches("distro !~ fedora|rhel") + assert not Context(distro="fedora-42").matches("distro !~ fedora-4.*") + assert Context(distro="fedora-42").matches("distro !~ fedora-3.*") + assert Context(distro="fedora-42").matches("distro !~ ubuntu") + + assert not Context(arch="ppc64").matches("arch !~ ppc64.*") + assert not Context(arch="ppc64le").matches("arch !~ ppc64.*") + assert Context(arch="ppc64le").matches("arch !~ ppc64$") + class TestContextValue: impossible_split = ["x86_64", "ppc64", "fips", "errata"]