Skip to content

Matchers

The composable matchers exposed by the match namespace.

Call them as match.greater_than(5), match.is_uuid(), and so on, then combine with &, |, and ~. See the Matchers guide for usage, composition, and custom matchers.

Factory namespace for creating matcher instances.

Usage:

from assertpy2 import match

assert_that(value).satisfies(match.greater_than(5) & match.less_than(10))
assert_that(items).each(match.is_positive())

equal_to staticmethod

equal_to(
    expected: object,
    strict_types: bool = False,
    **options: object,
) -> EqualToMatcher

Matcher for a value equal to expected.

Parameters:

Name Type Description Default
expected object

the value to compare against

required
strict_types bool

also require the same type, so True no longer matches 1. The same relation is_equal_to(..., strict_types=True) applies, spelled for a spec.

False
**options object

the rest of what is_equal_to() accepts, with the same meaning: tolerance, comparators, ignore, include and ignore_null. One relation, one set of knobs, whichever way it is spelled.

{}

Examples:

Usage:

assert_that(reading).satisfies(match.equal_to(expected, tolerance=0.01))
assert_that(payload).matches_structure({"user": match.equal_to(user, ignore="updated_at")})
Source code in assertpy2/matchers.py
@staticmethod
def equal_to(expected: object, strict_types: bool = False, **options: object) -> EqualToMatcher:
    """Matcher for a value equal to ``expected``.

    Args:
        expected: the value to compare against
        strict_types: also require the same type, so ``True`` no longer matches ``1``.  The same
            relation ``is_equal_to(..., strict_types=True)`` applies, spelled for a spec.
        **options: the rest of what
            [`is_equal_to()`][assertpy2.base.BaseMixin.is_equal_to] accepts, with the same meaning:
            ``tolerance``, ``comparators``, ``ignore``, ``include`` and ``ignore_null``.  One
            relation, one set of knobs, whichever way it is spelled.

    Examples:
        Usage:

            assert_that(reading).satisfies(match.equal_to(expected, tolerance=0.01))
            assert_that(payload).matches_structure({"user": match.equal_to(user, ignore="updated_at")})
    """
    return EqualToMatcher(expected, strict_types, **options)

greater_than staticmethod

greater_than(val: object) -> GreaterThanMatcher

Matcher for a value greater than val.

Source code in assertpy2/matchers.py
@staticmethod
def greater_than(val: object) -> GreaterThanMatcher:
    """Matcher for a value greater than ``val``."""
    return GreaterThanMatcher(val)

greater_than_or_equal_to staticmethod

greater_than_or_equal_to(
    val: object,
) -> GreaterThanOrEqualToMatcher

Matcher for a value greater than or equal to val.

Source code in assertpy2/matchers.py
@staticmethod
def greater_than_or_equal_to(val: object) -> GreaterThanOrEqualToMatcher:
    """Matcher for a value greater than or equal to ``val``."""
    return GreaterThanOrEqualToMatcher(val)

less_than staticmethod

less_than(val: object) -> LessThanMatcher

Matcher for a value less than val.

Source code in assertpy2/matchers.py
@staticmethod
def less_than(val: object) -> LessThanMatcher:
    """Matcher for a value less than ``val``."""
    return LessThanMatcher(val)

less_than_or_equal_to staticmethod

less_than_or_equal_to(
    val: object,
) -> LessThanOrEqualToMatcher

Matcher for a value less than or equal to val.

Source code in assertpy2/matchers.py
@staticmethod
def less_than_or_equal_to(val: object) -> LessThanOrEqualToMatcher:
    """Matcher for a value less than or equal to ``val``."""
    return LessThanOrEqualToMatcher(val)

between staticmethod

between(low: object, high: object) -> BetweenMatcher

Matcher for a value in the inclusive range low to high.

Source code in assertpy2/matchers.py
@staticmethod
def between(low: object, high: object) -> BetweenMatcher:
    """Matcher for a value in the inclusive range ``low`` to ``high``."""
    return BetweenMatcher(low, high)

close_to staticmethod

close_to(
    expected: object, tolerance: object
) -> CloseToMatcher

Matcher for a value within tolerance of expected (abs(value - expected) <= tolerance).

Parameters:

Name Type Description Default
expected object

the target value

required
tolerance object

the maximum allowed absolute difference from expected

required
Source code in assertpy2/matchers.py
@staticmethod
def close_to(expected: object, tolerance: object) -> CloseToMatcher:
    """Matcher for a value within ``tolerance`` of ``expected`` (``abs(value - expected) <= tolerance``).

    Args:
        expected: the target value
        tolerance: the maximum allowed absolute difference from ``expected``
    """
    return CloseToMatcher(expected, tolerance)

is_none staticmethod

is_none() -> IsNoneMatcher

Matcher for None.

Source code in assertpy2/matchers.py
@staticmethod
def is_none() -> IsNoneMatcher:
    """Matcher for ``None``."""
    return IsNoneMatcher()

is_not_none staticmethod

is_not_none() -> IsNotNoneMatcher

Matcher for any value that is not None.

Source code in assertpy2/matchers.py
@staticmethod
def is_not_none() -> IsNotNoneMatcher:
    """Matcher for any value that is not ``None``."""
    return IsNotNoneMatcher()

is_instance_of staticmethod

is_instance_of(
    expected_type: ClassInfo,
) -> IsInstanceOfMatcher

Matcher for an instance of expected_type (via isinstance).

Accepts whatever isinstance accepts: a class, a union (int | str), or a tuple of either. The builder assertion of the same name stays narrower on purpose - its overloads refine the tracked value to the given class, and a union has no single class to refine to. Reach for is_instance_of_any there.

Source code in assertpy2/matchers.py
@staticmethod
def is_instance_of(expected_type: ClassInfo) -> IsInstanceOfMatcher:
    """Matcher for an instance of ``expected_type`` (via ``isinstance``).

    Accepts whatever ``isinstance`` accepts: a class, a union (``int | str``), or a tuple of
    either.  The builder assertion of the same name stays narrower on purpose - its overloads
    refine the tracked value to the given class, and a union has no single class to refine to.
    Reach for `is_instance_of_any` there.
    """
    return IsInstanceOfMatcher(expected_type)

is_type_of staticmethod

is_type_of(expected_type: type) -> IsTypeOfMatcher

Matcher for exactly expected_type, rejecting subclasses (int but not bool).

Source code in assertpy2/matchers.py
@staticmethod
def is_type_of(expected_type: type) -> IsTypeOfMatcher:
    """Matcher for exactly ``expected_type``, rejecting subclasses (``int`` but not ``bool``)."""
    return IsTypeOfMatcher(expected_type)

is_truthy staticmethod

is_truthy() -> IsTruthyMatcher

Matcher for a truthy value.

Source code in assertpy2/matchers.py
@staticmethod
def is_truthy() -> IsTruthyMatcher:
    """Matcher for a truthy value."""
    return IsTruthyMatcher()

is_falsy staticmethod

is_falsy() -> IsFalsyMatcher

Matcher for a falsy value.

Source code in assertpy2/matchers.py
@staticmethod
def is_falsy() -> IsFalsyMatcher:
    """Matcher for a falsy value."""
    return IsFalsyMatcher()

has_length staticmethod

has_length(length: int) -> HasLengthMatcher

Matcher for a value whose len() equals length.

Source code in assertpy2/matchers.py
@staticmethod
def has_length(length: int) -> HasLengthMatcher:
    """Matcher for a value whose ``len()`` equals ``length``."""
    return HasLengthMatcher(length)

is_length staticmethod

is_length(length: int) -> HasLengthMatcher

Matcher for a value whose len() equals length.

The same matcher as has_length(), under the name the fluent assertion uses (is_length()). One relation was reachable as has_length from the matcher namespace and as is_length from the builder, so which name worked depended on which of the two a reader had seen first. Both work from both now.

Source code in assertpy2/matchers.py
@staticmethod
def is_length(length: int) -> HasLengthMatcher:
    """Matcher for a value whose ``len()`` equals ``length``.

    The same matcher as `has_length()`, under the name the fluent assertion uses
    ([`is_length()`][assertpy2.base.BaseMixin.is_length]).  One relation was reachable as
    ``has_length`` from the matcher namespace and as ``is_length`` from the builder, so which name
    worked depended on which of the two a reader had seen first.  Both work from both now.
    """
    return HasLengthMatcher(length)

is_empty staticmethod

is_empty() -> IsEmptyMatcher

Matcher for an empty value (len() == 0).

Source code in assertpy2/matchers.py
@staticmethod
def is_empty() -> IsEmptyMatcher:
    """Matcher for an empty value (``len() == 0``)."""
    return IsEmptyMatcher()

is_not_empty staticmethod

is_not_empty() -> IsNotEmptyMatcher

Matcher for a non-empty value (len() > 0).

Source code in assertpy2/matchers.py
@staticmethod
def is_not_empty() -> IsNotEmptyMatcher:
    """Matcher for a non-empty value (``len() > 0``)."""
    return IsNotEmptyMatcher()

is_positive staticmethod

is_positive() -> IsPositiveMatcher

Matcher for a value greater than zero.

Source code in assertpy2/matchers.py
@staticmethod
def is_positive() -> IsPositiveMatcher:
    """Matcher for a value greater than zero."""
    return IsPositiveMatcher()

is_negative staticmethod

is_negative() -> IsNegativeMatcher

Matcher for a value less than zero.

Source code in assertpy2/matchers.py
@staticmethod
def is_negative() -> IsNegativeMatcher:
    """Matcher for a value less than zero."""
    return IsNegativeMatcher()

is_zero staticmethod

is_zero() -> IsZeroMatcher

Matcher for a value equal to zero.

Source code in assertpy2/matchers.py
@staticmethod
def is_zero() -> IsZeroMatcher:
    """Matcher for a value equal to zero."""
    return IsZeroMatcher()

is_even staticmethod

is_even() -> IsEvenMatcher

Matcher for an even integer.

Source code in assertpy2/matchers.py
@staticmethod
def is_even() -> IsEvenMatcher:
    """Matcher for an even integer."""
    return IsEvenMatcher()

is_odd staticmethod

is_odd() -> IsOddMatcher

Matcher for an odd integer.

Source code in assertpy2/matchers.py
@staticmethod
def is_odd() -> IsOddMatcher:
    """Matcher for an odd integer."""
    return IsOddMatcher()

is_divisible_by staticmethod

is_divisible_by(divisor: int) -> IsDivisibleByMatcher

Matcher for an integer divisible by divisor.

Source code in assertpy2/matchers.py
@staticmethod
def is_divisible_by(divisor: int) -> IsDivisibleByMatcher:
    """Matcher for an integer divisible by ``divisor``."""
    return IsDivisibleByMatcher(divisor)

is_callable staticmethod

is_callable() -> IsCallableMatcher

Matcher for a callable object.

Source code in assertpy2/matchers.py
@staticmethod
def is_callable() -> IsCallableMatcher:
    """Matcher for a callable object."""
    return IsCallableMatcher()

is_in staticmethod

is_in(*values: object) -> IsInMatcher

Matcher for a value present in values.

Parameters:

Name Type Description Default
*values object

the candidate values; the matched value must equal one of them

()
Source code in assertpy2/matchers.py
@staticmethod
def is_in(*values: object) -> IsInMatcher:
    """Matcher for a value present in ``values``.

    Args:
        *values: the candidate values; the matched value must equal one of them
    """
    return IsInMatcher(*values)

has_property staticmethod

has_property(
    name: str, matcher: Matcher | None = None
) -> HasPropertyMatcher

Matcher for an object with attribute name, optionally matching matcher.

Parameters:

Name Type Description Default
name str

the attribute name the object must have

required
matcher Matcher | None

optional matcher the attribute value must satisfy; if None, only the presence of the attribute is checked

None
Source code in assertpy2/matchers.py
@staticmethod
def has_property(name: str, matcher: Matcher | None = None) -> HasPropertyMatcher:
    """Matcher for an object with attribute ``name``, optionally matching ``matcher``.

    Args:
        name: the attribute name the object must have
        matcher: optional matcher the attribute value must satisfy; if ``None``,
            only the presence of the attribute is checked
    """
    return HasPropertyMatcher(name, matcher)

contains_string staticmethod

contains_string(
    substring: str | bytes,
) -> Matcher[str | bytes]

Matcher for text containing substring, on str and on bytes alike.

Source code in assertpy2/matchers.py
@staticmethod
def contains_string(substring: str | bytes) -> Matcher[str | bytes]:
    """Matcher for text containing ``substring``, on `str` and on `bytes` alike."""
    return ContainsStringMatcher(substring)

matches_regex staticmethod

matches_regex(pattern: str) -> Matcher[str]

Matcher for a string in which pattern is found (re.search).

Source code in assertpy2/matchers.py
@staticmethod
def matches_regex(pattern: str) -> Matcher[str]:
    """Matcher for a string in which ``pattern`` is found (``re.search``)."""
    return MatchesRegexMatcher(pattern)

starts_with staticmethod

starts_with(prefix: str | bytes) -> Matcher[str | bytes]

Matcher for text starting with prefix, on str and on bytes alike.

Source code in assertpy2/matchers.py
@staticmethod
def starts_with(prefix: str | bytes) -> Matcher[str | bytes]:
    """Matcher for text starting with ``prefix``, on `str` and on `bytes` alike."""
    return StartsWithMatcher(prefix)

ends_with staticmethod

ends_with(suffix: str | bytes) -> Matcher[str | bytes]

Matcher for text ending with suffix, on str and on bytes alike.

Source code in assertpy2/matchers.py
@staticmethod
def ends_with(suffix: str | bytes) -> Matcher[str | bytes]:
    """Matcher for text ending with ``suffix``, on `str` and on `bytes` alike."""
    return EndsWithMatcher(suffix)

all_of staticmethod

all_of(*matchers: Matcher[Any]) -> AllOfMatcher

Matcher that holds when every one of matchers matches (the & operator).

Source code in assertpy2/matchers.py
@staticmethod
def all_of(*matchers: Matcher[Any]) -> AllOfMatcher:
    """Matcher that holds when every one of ``matchers`` matches (the ``&`` operator)."""
    return AllOfMatcher(*matchers)

any_of staticmethod

any_of(*matchers: Matcher[Any]) -> AnyOfMatcher

Matcher that holds when at least one of matchers matches (the | operator).

Source code in assertpy2/matchers.py
@staticmethod
def any_of(*matchers: Matcher[Any]) -> AnyOfMatcher:
    """Matcher that holds when at least one of ``matchers`` matches (the ``|`` operator)."""
    return AnyOfMatcher(*matchers)

not_ staticmethod

not_(matcher: Matcher[Any]) -> NotMatcher

Matcher that inverts matcher (the ~ operator).

Source code in assertpy2/matchers.py
@staticmethod
def not_(matcher: Matcher[Any]) -> NotMatcher:
    """Matcher that inverts ``matcher`` (the ``~`` operator)."""
    return NotMatcher(matcher)

ignore staticmethod

ignore() -> IgnoreMatcher

Matcher that accepts anything; useful as a placeholder in structure specs.

Source code in assertpy2/matchers.py
@staticmethod
def ignore() -> IgnoreMatcher:
    """Matcher that accepts anything; useful as a placeholder in ``structure`` specs."""
    return IgnoreMatcher()

is_uuid staticmethod

is_uuid() -> IsUuidMatcher

Matcher for a string parseable as a UUID.

Source code in assertpy2/matchers.py
@staticmethod
def is_uuid() -> IsUuidMatcher:
    """Matcher for a string parseable as a UUID."""
    return IsUuidMatcher()

is_non_empty_string staticmethod

is_non_empty_string() -> IsNonEmptyStringMatcher

Matcher for a non-empty string.

Source code in assertpy2/matchers.py
@staticmethod
def is_non_empty_string() -> IsNonEmptyStringMatcher:
    """Matcher for a non-empty string."""
    return IsNonEmptyStringMatcher()

is_now staticmethod

is_now(delta: float | timedelta = 2.0) -> IsNowMatcher

Matcher for a datetime within delta of the current time.

Parameters:

Name Type Description Default
delta float | timedelta

tolerance as seconds (a number) or a timedelta; defaults to 2 seconds. Naive and timezone-aware values are both handled (compared against now in the same awareness).

2.0
Source code in assertpy2/matchers.py
@staticmethod
def is_now(delta: float | timedelta = 2.0) -> IsNowMatcher:
    """Matcher for a ``datetime`` within ``delta`` of the current time.

    Args:
        delta: tolerance as seconds (a number) or a ``timedelta``; defaults to 2 seconds. Naive and
            timezone-aware values are both handled (compared against ``now`` in the same awareness).
    """
    return IsNowMatcher(delta if isinstance(delta, timedelta) else timedelta(seconds=delta))

is_before staticmethod

is_before(other: datetime) -> IsBeforeMatcher

Matcher for a datetime strictly before other (a non-comparable value never matches).

Source code in assertpy2/matchers.py
@staticmethod
def is_before(other: datetime) -> IsBeforeMatcher:
    """Matcher for a ``datetime`` strictly before ``other`` (a non-comparable value never matches)."""
    return IsBeforeMatcher(other)

is_after staticmethod

is_after(other: datetime) -> IsAfterMatcher

Matcher for a datetime strictly after other (a non-comparable value never matches).

Source code in assertpy2/matchers.py
@staticmethod
def is_after(other: datetime) -> IsAfterMatcher:
    """Matcher for a ``datetime`` strictly after ``other`` (a non-comparable value never matches)."""
    return IsAfterMatcher(other)

contains staticmethod

contains(*items: _Item) -> Matcher[Iterable[_Item]]

Matcher for a collection containing every one of items.

The spec spelling of contains(), with the same rules: a mapping is searched by key, and a matcher among the items is satisfied by any element.

Examples:

Usage:

assert_that(payload).matches_structure({"tags": match.contains("beta")})
assert_that(rows).satisfies(match.contains(match.greater_than(100)))
Source code in assertpy2/matchers.py
@staticmethod
def contains(*items: _Item) -> Matcher[Iterable[_Item]]:
    """Matcher for a collection containing every one of ``items``.

    The spec spelling of [`contains()`][assertpy2.contains.ContainsMixin.contains], with the same
    rules: a mapping is searched by key, and a matcher among the items is satisfied by any element.

    Examples:
        Usage:

            assert_that(payload).matches_structure({"tags": match.contains("beta")})
            assert_that(rows).satisfies(match.contains(match.greater_than(100)))
    """
    return ContainsMatcher(*items)

contains_only staticmethod

contains_only(*items: _Item) -> Matcher[Iterable[_Item]]

Matcher for a collection holding these items and nothing else.

The spec spelling of contains_only().

Source code in assertpy2/matchers.py
@staticmethod
def contains_only(*items: _Item) -> Matcher[Iterable[_Item]]:
    """Matcher for a collection holding these items and nothing else.

    The spec spelling of [`contains_only()`][assertpy2.contains.ContainsMixin.contains_only].
    """
    return ContainsOnlyMatcher(*items)

is_subset_of staticmethod

is_subset_of(
    superset: Iterable[_Item],
) -> Matcher[Iterable[_Item]]
is_subset_of(*superset: _Item) -> Matcher[Iterable[_Item]]

Matcher for a collection whose items all appear in superset.

The spec spelling of is_subset_of(). Takes the superset either as one collection or as loose items, which is why it is overloaded: read off a single union, a checker cannot tell [1, 2] the collection from [1, 2] the item.

An ordinary collection stays a live view of itself, the way equal_to keeps its expected value; a one-shot iterator is drained when the matcher is built, since it could not answer a second time otherwise. Handing in an endless iterator therefore never returns.

Source code in assertpy2/matchers.py
@staticmethod
def is_subset_of(*superset: object) -> Matcher[Iterable[Any]]:
    """Matcher for a collection whose items all appear in ``superset``.

    The spec spelling of [`is_subset_of()`][assertpy2.collection.CollectionMixin.is_subset_of].
    Takes the superset either as one collection or as loose items, which is why it is overloaded:
    read off a single union, a checker cannot tell `[1, 2]` the collection from `[1, 2]` the item.

    An ordinary collection stays a live view of itself, the way ``equal_to`` keeps its expected
    value; a one-shot iterator is drained when the matcher is built, since it could not answer a
    second time otherwise.  Handing in an endless iterator therefore never returns.
    """
    return IsSubsetOfMatcher(*superset)

is_sorted staticmethod

is_sorted(
    key: Callable[[Any], Any] | None = None,
    reverse: bool = False,
) -> Matcher[Iterable[Any]]

Matcher for a collection in order, optionally by key and optionally reversed.

The spec spelling of is_sorted().

Source code in assertpy2/matchers.py
@staticmethod
def is_sorted(key: Callable[[Any], Any] | None = None, reverse: bool = False) -> Matcher[Iterable[Any]]:
    """Matcher for a collection in order, optionally by ``key`` and optionally reversed.

    The spec spelling of [`is_sorted()`][assertpy2.collection.CollectionMixin.is_sorted].
    """
    return IsSortedMatcher(key, reverse)

each_item staticmethod

each_item(matcher: Matcher[Any]) -> EachMatcher

Matcher for an iterable whose every item matches matcher.

Parameters:

Name Type Description Default
matcher Matcher[Any]

the matcher each item of the iterable must satisfy; a non-iterable value never matches

required
Source code in assertpy2/matchers.py
@staticmethod
def each_item(matcher: Matcher[Any]) -> EachMatcher:
    """Matcher for an iterable whose every item matches ``matcher``.

    Args:
        matcher: the matcher each item of the iterable must satisfy; a non-iterable
            value never matches
    """
    return EachMatcher(matcher)

structure staticmethod

structure(spec: dict[Any, Any]) -> StructureMatcher

Matcher for a dict matching spec.

Parameters:

Name Type Description Default
spec dict[Any, Any]

dict whose values are matchers, raw values (compared with ==), or nested dict specs. Keys present in the value but absent from the spec are ignored.

required

Examples:

Usage:

assert_that(user).satisfies(
    match.structure({"id": match.is_instance_of(int), "name": "Alice"})
)
Source code in assertpy2/matchers.py
@staticmethod
def structure(spec: dict[Any, Any]) -> StructureMatcher:
    """Matcher for a dict matching ``spec``.

    Args:
        spec: dict whose values are matchers, raw values (compared with ``==``),
            or nested dict specs. Keys present in the value but absent from the spec are ignored.

    Examples:
        Usage:

            assert_that(user).satisfies(
                match.structure({"id": match.is_instance_of(int), "name": "Alice"})
            )
    """
    return StructureMatcher(spec)

BaseMatcher

Abstract base for all matchers with operator support.

A subclass implements either matches() or evaluate(), and gets the other one from here. matches() is the cheap primitive: it is what == calls, and matchers are used as dict values in matches_structure and as snapshot placeholders, so a comparison must not have to build a result object. evaluate() is the whole answer, for a caller that would otherwise ask the same value three questions in a row.

matches

matches(value: Any) -> bool
Source code in assertpy2/_matcher_impls.py
def matches(self, value: Any) -> bool:
    if type(self).evaluate is BaseMatcher.evaluate:
        raise NotImplementedError("a matcher must implement matches() or evaluate()")
    return self.evaluate(value).matched

evaluate

evaluate(value: Any) -> MatchResult

The verdict, the requirement and the reason, from one look at value.

The default composes the three older methods, so a matcher written before this existed answers it without changing. Overriding it is for a matcher whose reason costs what the verdict already paid for: the alternative is matches() and describe_mismatch() walking the same value twice, which is how a matcher over a one-shot iterator used to name the wrong element.

Source code in assertpy2/_matcher_impls.py
def evaluate(self, value: Any) -> MatchResult:
    """The verdict, the requirement and the reason, from one look at *value*.

    The default composes the three older methods, so a matcher written before this existed answers
    it without changing.  Overriding it is for a matcher whose reason costs what the verdict already
    paid for: the alternative is `matches()` and `describe_mismatch()` walking the same value twice,
    which is how a matcher over a one-shot iterator used to name the wrong element.
    """
    if type(self).matches is BaseMatcher.matches:
        raise NotImplementedError("a matcher must implement matches() or evaluate()")
    matched = self.matches(value)
    return MatchResult(
        matched=matched,
        description=self.describe(),
        mismatch="" if matched else self.describe_mismatch(value),
    )

describe

describe() -> str
Source code in assertpy2/_matcher_impls.py
def describe(self) -> str:
    raise NotImplementedError

describe_mismatch

describe_mismatch(value: Any) -> str
Source code in assertpy2/_matcher_impls.py
def describe_mismatch(self, value: Any) -> str:
    return f"was <{value}>"

MatchResult dataclass

MatchResult(
    *,
    matched: bool,
    description: str,
    mismatch: str = "",
    diff: DiffResult | None = None,
)

What a matcher decided about one value, in one object instead of three calls.

Deliberately not an AssertionOutcome, which is the record of a failed assertion. A matcher is asked about every leaf of a structure and about every element of a collection, so its result has to stay cheap: four fields, no location, no group, nothing that has to be computed before it is known whether anyone will read it.

matched instance-attribute

matched: bool

Whether the value matched.

description instance-attribute

description: str

What the matcher requires, in the words it uses in a failure message: a positive value.

mismatch class-attribute instance-attribute

mismatch: str = ''

Why this value did not match, in the words a failure message continues with: was <-1>.

Empty when it matched. There is nothing to say about a value that satisfied the matcher, and the text would have to be invented.

diff class-attribute instance-attribute

diff: DiffResult | None = None

A structured diff, from a matcher that compares rather than tests: equality has one, is_odd does not.