Skip to content

Commit

Permalink
Implement regular expression search for context
Browse files Browse the repository at this point in the history
Support new operator `~` for matching patterns and `!~` for
negative matching patterns in the context rules.

Fix #226.
  • Loading branch information
psss committed Jan 6, 2025
1 parent 95c9745 commit 316ea9a
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 1 deletion.
8 changes: 7 additions & 1 deletion docs/context.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:]]+
Expand All @@ -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 <original rule>

Expand Down
8 changes: 8 additions & 0 deletions docs/releases.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
21 changes: 21 additions & 0 deletions fmf/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 """

Expand Down Expand Up @@ -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,
Expand Down
25 changes: 25 additions & 0 deletions tests/unit/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down

0 comments on commit 316ea9a

Please sign in to comment.