Skip to content

Instantly share code, notes, and snippets.

@atifaziz
Created June 12, 2026 11:09
Show Gist options
  • Select an option

  • Save atifaziz/b6e4615197abd67acb6aa1b082dd2c87 to your computer and use it in GitHub Desktop.

Select an option

Save atifaziz/b6e4615197abd67acb6aa1b082dd2c87 to your computer and use it in GitHub Desktop.
Match!
# MIT License
#
# Copyright (c) Microsoft Corporation.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE
from collections.abc import Callable, Iterable, Sized
from typing import Any, Final
from unittest import TestCase
class _Match:
"""
Represents a matching condition to implement relaxed and custom equality
semantics. An instance of this class will compare equal to its right-hand
value or comparand if it satisfies a given condition. The condition is
supplied during initialisation and summoned when `__eq__` is invoked. The
condition is a function (or any callable) that is supplied the value of
the `other` argument of `__eq__` and returns a Boolean indicating whether
the match is satisfied.
The condition is immutable once created and various methods permit
combining conditions together to form more complex conditions. The `any`
instance is special and matches any value of any type:
>>> print(any == 42) # True
>>> print(any == 43) # True
>>> print(any == "foo") # True
>>> print(any == [1, 2, 3]) # True
The `any` instance is the starting point for building more complex
matching conditions. For example:
>>> even = any.where(lambda x: x % 2 == 0) # Equal to any even integer
>>> print(even == 42) # True
>>> print(even == 43) # False
You can also continue to build on top of existing match conditions, such
in the next example that negates the even condition from the previous
example using `not_()` to match odd integers:
>>> odd = even.not_()
>>> print(odd == 42) # False
>>> print(odd == 43) # True
You can also combine many conditions together:
>>> negative = any.where(lambda x: x < 0)
>>> positive = any.where(lambda x: x > 0)
>>> zero = negative.not_().and_(positive.not_())
>>> # Above is for illustration only and logically equal to doing:
>>> # zero = any.eq(0)
>>> print(negative == -42) # True
>>> print(negative == 42) # False
>>> print(negative == 0) # False
>>> print(positive == -42) # False
>>> print(positive == 42) # True
>>> print(positive == 0) # False
>>> print(zero == -42) # False
>>> print(zero == 42) # False
>>> print(zero == 0) # True
"""
def __init__(self, condition: Callable[[Any], bool]) -> None:
"""Initialises this instance with the condition callable/function."""
self._condition = condition
def __eq__(self, other: Any) -> bool:
"""Provides equality semantics based on the condition supplied during initialisation."""
return self._condition(other)
def and_(self, other: "_Match") -> "_Match":
"""Adds another condition to match."""
return _Match(lambda x: self == x and other == x)
def where(self, condition: Callable[[Any], bool]) -> "_Match":
"""Adds another condition, specifically a predicate function, to match."""
return self.and_(_Match(lambda x: condition(x)))
def not_(self) -> "_Match":
"""Inverts this condition."""
return _Match(lambda x: not self._condition(x))
def of_type(self, type_: type) -> "_Match":
"""Matches only values of a given type."""
return self.where(lambda x: isinstance(x, type_))
def none(self) -> "_Match":
"""Matches only `None`."""
return self.where(lambda x: x is None)
def not_none(self) -> "_Match":
"""Matches anything but `None`."""
return self.none().not_()
def eq(self, value: Any) -> "_Match":
"""Matches only a given value."""
return self.where(lambda x: x == value)
def ne(self, value: Any) -> "_Match":
"""Matches anything but a given value."""
return self.eq(value).not_()
def in_(self, *values: Any) -> "_Match":
"""Matches any value contained in the given set of values."""
return self.where(lambda x: x in values)
def having(self, f: Callable[[Any], Any], match: "_Match") -> "_Match":
"""Matches only values whose projection satisfies a given condition to match."""
return self.and_(_Match(lambda x: f(x) == match))
def len(self, value: int) -> "_Match":
"""Matches only (sized) values whose length is of the given value."""
return self.of_type(Sized).having(len, any.eq(value))
def count_equal(self, *values: Any) -> "_Match":
"""Wraps and behaves exactly like `TestCase.assertCountEqual`."""
def count_equal(actual: Iterable[Any]) -> bool:
try:
TestCase().assertCountEqual(actual, values)
return True
except AssertionError:
return False
return self.of_type(Iterable).where(count_equal)
def non_empty_list(self):
"""Matches any list that is not empty."""
return self.and_(_NON_EMPTY_LIST)
def either(a: _Match, b: _Match) -> _Match:
"""Matches any value that satisfies the conditions of either of the given matches."""
return _Match(lambda x: a == x or b == x)
any = _Match(lambda _: True) # Matches anything
_NON_EMPTY_LIST: Final[_Match] = any.of_type(list).having(len, any.ne(0))
from collections.abc import Callable
from typing import Any, Final
from unittest.mock import MagicMock as Mock
import pytest
from .match import any, either
class Custom:
pass
# The following sample set contains values of various types except `None`.`
SOME_SAMPLES: Final = [
True,
False,
42,
4.2,
"string",
pytest.param([1, 2, 3], id="list"),
pytest.param({"foo": 1, "bar": 2, "baz": 3}, id="dict"),
pytest.param((1, 2, 3), id="tuple"),
pytest.param(Custom(), id="custom"),
]
SAMPLES: Final = [None] + SOME_SAMPLES
@pytest.mark.parametrize(
"comparand",
SAMPLES,
)
def test_any(comparand: Any) -> None:
assert any == comparand
class NonSampled:
pass
NON_SAMPLED: Final = NonSampled()
@pytest.mark.parametrize(
"comparand",
SAMPLES,
)
def test_eq(comparand: Any) -> None:
subject = any.eq(comparand)
assert subject == comparand
assert subject != NON_SAMPLED
@pytest.mark.parametrize(
"comparand",
SAMPLES,
)
def test_ne(comparand: Any) -> None:
subject = any.ne(comparand)
assert subject == NON_SAMPLED
@pytest.mark.parametrize(
"comparand",
SAMPLES,
)
def test_of_type(comparand: Any) -> None:
subject = any.of_type(type(comparand))
assert subject == comparand
assert subject != NON_SAMPLED
@pytest.mark.parametrize(
"data, comparand, expected",
[
pytest.param(["foo", "bar", "baz"], "foo", True, id="contains first"),
pytest.param(["foo", "bar", "baz"], "bar", True, id="contains middle"),
pytest.param(["foo", "bar", "baz"], "baz", True, id="contains last"),
pytest.param(["foo", "bar", "baz"], "BAR", False, id="non-member"),
pytest.param([], "foo", False, id="empty"),
],
)
def test_in(data: list[str], comparand: Any, expected: bool) -> None:
subject = any.in_(*data)
actual = subject == comparand
assert actual == expected
@pytest.mark.parametrize(
"n, comparand, expected",
[
pytest.param(0, "", True, id="empty string"),
pytest.param(6, "foobar", True, id="non-empty string"),
pytest.param(3, ["foo", "bar", "baz"], True, id="list"),
pytest.param(3, ("foo", "bar", "baz"), True, id="tuple"),
pytest.param(3, {"foo": 1, "bar": 2, "baz": 3}, True, id="dict"),
pytest.param(3, "foobar", False, id="string length mismatch"),
pytest.param(3, [], False, id="list length mismatch"),
pytest.param(0, 123, False, id="unsized"),
],
)
def test_len(n: int, comparand: Any, expected: bool) -> None:
subject = any.len(n)
actual = subject == comparand
assert actual == expected
@pytest.mark.parametrize(
"comparand, expected",
[(1, False), (2, True), (3, False), (4, True), (5, False), (6, True), (7, False), (8, True), (9, False)],
)
def test_where(comparand: int, expected: bool) -> None:
subject = any.where(lambda x: x % 2 == 0)
actual = subject == comparand
assert actual == expected
@pytest.mark.parametrize(
"comparand, expected",
[(1, False), (2, False), (3, False), (4, True), (5, False), (6, True), (7, False), (8, False), (9, False)],
)
def test_where_combined(comparand: int, expected: bool) -> None:
subject = any.where(lambda x: x % 2 == 0).where(lambda x: 3 <= x <= 7)
actual = subject == comparand
assert actual == expected
def test_and() -> None:
even = any.where(lambda x: x % 2 == 0)
neg = any.where(lambda x: x < 0)
subject = even.and_(neg)
assert subject == -42
@pytest.mark.parametrize(
"a, b, expected", [(False, False, False), (True, False, False), (False, True, False), (True, True, True)]
)
def test_and_table(a: bool, b: bool, expected: bool) -> None:
# arrange
first = Mock()
first.__eq__ = Mock(return_value=a)
second = Mock()
second.__eq__ = Mock(return_value=b)
subject = any.and_(first).and_(second)
# act
actual = subject == Custom()
# assert
assert actual == expected
def test_and_never_evaluates_second_when_first_is_unsatisfied() -> None:
# arrange
first = Mock()
first.__eq__ = Mock(return_value=False)
second = Mock()
second.__eq__ = Mock(side_effect=AssertionError("Unexpected call"))
subject = any.and_(first).and_(second)
# act
actual = subject == (other := Custom())
# assert
assert not actual
first.__eq__.assert_called_once_with(other)
second.__eq__.assert_not_called()
def test_and_evaluates_second_when_first_is_satisfied() -> None:
# arrange
first = Mock()
first.__eq__ = Mock(return_value=True)
second = Mock()
second.__eq__ = Mock(return_value=True)
subject = any.and_(first).and_(second)
# act
actual = subject == (other := Custom())
# assert
assert actual
first.__eq__.assert_called_once_with(other)
second.__eq__.assert_called_once_with(other)
def test_not() -> None:
zero = any.eq(0)
non_zero = zero.not_()
even = any.where(lambda x: x % 2 == 0)
odd = even.not_()
neg = any.where(lambda x: x < 0)
pos = neg.not_()
assert non_zero == 1
assert odd == -3
assert pos == 42
assert odd.and_(pos) == 5
def test_not_not() -> None:
zero = any.eq(0).not_().not_()
assert zero == 0
assert zero != 42
assert zero != -42
def test_having() -> None:
comparand = {"foo": 1, "bar": 2, "baz": 3, "456": [4, 5, 6]}
def key(name: str) -> Callable[[dict[str, Any]], Any]:
return lambda x: x[name]
foo = key("foo")
bar = key("bar")
baz = key("baz")
non_zero = any.ne(0)
in_123 = any.in_(1, 2, 3)
# The following set of assertions are really partial equality tests. They
# test that the condition on the left is satisfied by the comparand (a
# dictionary) on the right side of the `==` equal operator, such that the
# following is saying that the dictionary is expected to have a key "foo"
# with a value of 1. Other keys of the dictionary are ignored.
assert any.having(foo, any.eq(1)) == comparand
# The remaining assertions are in similar vein.
assert any.having(bar, any.eq(2)) == comparand
assert any.having(baz, any.eq(3)) == comparand
assert any.having(foo, non_zero) == comparand
assert any.having(bar, non_zero) == comparand
assert any.having(baz, non_zero) == comparand
assert any.having(foo, in_123) == comparand
assert any.having(bar, in_123) == comparand
assert any.having(baz, in_123) == comparand
with pytest.raises(KeyError):
assert any.having(key("qux"), any) == comparand
def test_none() -> None:
#
# The following error is intentionally suppressed:
#
# > E711 Comparison to `None` should be `cond is None`
#
# because `__eq__` implementation is being abused for matching rather than
# proper equality semantics.
#
assert any.none() == None # noqa: E711
@pytest.mark.parametrize(
"comparand",
SOME_SAMPLES,
)
def test_none_inequality(comparand: Any) -> None:
assert not (any.none() == comparand)
@pytest.mark.parametrize(
"comparand",
SOME_SAMPLES,
)
def test_not_none(comparand: Any) -> None:
assert any.not_none() == comparand
@pytest.mark.parametrize(
"values, comparand, expected",
[
pytest.param([1, 2, 3], [1, 2, 3], True, id="same"),
pytest.param([1, 2, 3], [3, 2, 1], True, id="unordered"),
pytest.param([1, 2, 3], [1, 1, 1], False, id="same length, different values"),
pytest.param([Custom()], [Custom()], False, id="same length, different objects"),
pytest.param([1, 2, 3], [], False, id="different lengths (empty)"),
pytest.param([1, 2, 3], [1], False, id="different lengths"),
pytest.param([], [], True, id="empty"),
pytest.param([], 123, False, id="comparand non-iterable"),
],
)
def test_count_equal(values: list[Any], comparand: Any, expected: bool) -> None:
subject = any.count_equal(*values)
actual = subject == comparand
assert actual == expected
@pytest.mark.parametrize(
"comparand, expected",
[
pytest.param([], False, id="empty"),
pytest.param([42], True, id="not empty"),
pytest.param(42, False, id="non-list"),
],
)
def test_non_empty_list(comparand: Any, expected: bool) -> None:
subject = any.non_empty_list()
actual = subject == comparand
assert actual == expected
@pytest.mark.parametrize(
"comparand, expected",
[(0, False), (1, True), (2, True), (3, True), (4, False), (5, False), (6, False), (7, True), (8, True), (9, True)],
)
def test_either_123_789(comparand: Any, expected: bool) -> None:
one_two_three = any.in_(1, 2, 3)
seven_eight_nine = any.in_(7, 8, 9)
subject = either(one_two_three, seven_eight_nine)
actual = subject == comparand
assert actual == expected
@pytest.mark.parametrize(
"a, b, expected", [(False, False, False), (True, False, True), (False, True, True), (True, True, True)]
)
def test_either_table(a: bool, b: bool, expected: bool) -> None:
# arrange
first = Mock()
first.__eq__ = Mock(return_value=a)
second = Mock()
second.__eq__ = Mock(return_value=b)
subject = either(first, second)
# act
actual = subject == Custom()
# assert
assert actual == expected
def test_either_never_evaluates_second_when_first_is_satisfied() -> None:
# arrange
first = Mock()
first.__eq__ = Mock(return_value=True)
second = Mock()
second.__eq__ = Mock(side_effect=AssertionError("Unexpected call"))
subject = either(first, second)
# act
actual = subject == (other := Custom())
# assert
assert actual
first.__eq__.assert_called_once_with(other)
second.__eq__.assert_not_called()
def test_either_evaluates_second_when_first_is_unsatisfied() -> None:
# arrange
first = Mock()
first.__eq__ = Mock(return_value=False)
second = Mock()
second.__eq__ = Mock(return_value=True)
subject = either(first, second)
# act
actual = subject == (other := Custom())
# assert
assert actual
first.__eq__.assert_called_once_with(other)
second.__eq__.assert_called_once_with(other)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment