Structured failures¶
The exception raised when an assertion fails, carrying the structured diff.
AssertionFailure subclasses AssertionError, so existing except AssertionError handlers keep
working unchanged. See Errors & reporting for usage.
AssertionFailure ¶
AssertionFailure(
message: str,
*,
actual: object = None,
expected: object = None,
diff: DiffResult | None = None,
trace: PollTrace | None = None,
requirement: Requirement | None = None,
failures: tuple[AssertionOutcome, ...] = (),
)
Structured assertion failure with optional diff data.
Subclasses AssertionError for full backward compatibility:
existing except AssertionError handlers catch it transparently.
Source code in assertpy2/errors.py
requirement
instance-attribute
¶
What was asked of the value, as data: the operation, its parameters, and whether it was negated.
None where no operation was asked: fail(), a bare error() carrying a message of the
caller's own, and a precondition of one of the few members that assert nothing on their own.
failures
instance-attribute
¶
The failures a soft block collected, in the order they were collected.
Empty on a failure that is about one value, which is every failure except the aggregate a
soft_assertions() block raises when it closes. The aggregate's message is these rendered
into a list; this is the same thing before it became a string.
has_expected
property
¶
Whether the assertion named an expected value at all.
expected is None cannot answer it: is_equal_to(None) names one and is_not_empty() names
none, and both leave the attribute at None. A reporter deciding whether to show an expected
column needs the difference, and had to read the private record to get it.
Read from the record a failure composed through error() carries, which the polling and
snapshot re-wraps preserve. A failure built directly has none, and fail() and the aggregate a
soft block raises are built that way: there the older reading is all there is, and an
expectation counts as named when it is not None. Both of those name none and hold None,
so the fallback answers them correctly.
DiffResult
dataclass
¶
Structured diff between two values.
kind names the diff category - the shape of comparison that produced the entries. It is one
of "dict", "sequence", "dataclass", "namedtuple", "model", "attrs",
"set", "string", "scalar", "contains", "match", or "openapi".
It is unrelated to the assertion builder's kind argument, which selects the failure mode
(None/"soft"/"warn").
Step ¶
One hop from a value to one of its parts, as DiffEntry.steps records it.
path is written for a person and is lossy by construction: a mapping key goes through str(),
so {3: ...} and {"3": ...} land on the same text, and a key holding a dot or a bracket
cannot be read back out. A step keeps the key itself, so a reader can walk back into the value it
came from instead of parsing a string that was never a grammar.
kind
instance-attribute
¶
What kind of hop this is.
key indexes a mapping, index a sequence, attr reads a field of a dataclass, namedtuple,
attrs class or model. item names a member of a set, which has no position to index by.
line is the 1-based line number of a text or bytes diff.
value
instance-attribute
¶
The key, index, field name, member or line number. Not stringified: that is the whole point.
side
class-attribute
instance-attribute
¶
Which sequence the index belongs to, when the two have shifted apart.
Sequence alignment reports an inserted element against one side only, and once the two index spaces
disagree an index without a side names two different elements. None whenever both sides share
the position, which is every step that is not a one-sided element of an aligned sequence.
DiffEntry
dataclass
¶
DiffEntry(
*,
path: str,
actual: object = None,
expected: object = None,
absent: Literal["actual", "expected"] | None = None,
steps: tuple[Step, ...] = (),
)
Single difference between actual and expected values at a specific path.
absent
class-attribute
instance-attribute
¶
Which side had no value here at all, as opposed to holding None.
Without this the two are indistinguishable, since both leave the field at None, and a
dictionary compared against one whose value is None renders exactly like a dictionary with an
extra key. Readers of a diff have to be able to tell "this key is not there" from "this key is
there and its value is None", and so does anything reasoning about the diff afterwards.
Defaults to None, so an entry built the old way keeps its old meaning and only the producers
that mean absence say so.
steps
class-attribute
instance-attribute
¶
The same location as path, in the form a program can use.
Empty at the root, which is the entry path renders as .: the difference is the whole value.
Also empty on an entry whose path is a label rather than a location, which is what a containment
or matcher failure produces.
PollTrace
dataclass
¶
PollTrace(
*,
samples: list[PollSample],
total_polls: int,
dropped: int,
elapsed: float,
summary: str,
)
Convergence telemetry attached to an eventually() timeout failure.
samples keeps the first and last polls, with middle entries beyond the retention window
counted in dropped, and total_polls is the real number of polls.
summary is a one-line trend classification of why the condition never held.
PollSample
dataclass
¶
One recorded poll of an eventually() probe.
outcome is "fail" (the probe returned a value and the assertion on it failed) or
"error" (the probe raised an ignored exception before producing a value).
value is a JSON-safe point-in-time snapshot of the probed value, None for "error"
samples, and detail carries the failure message or the exception repr.
Consecutive identical polls are collapsed into one sample: repeats counts the run, and
elapsed is its first occurrence, in seconds from the start of polling.
DanglingAssertionWarning ¶
An assert_that() statement that asserts nothing.
Found at collection under --assertpy2-dangling and emitted when the test that contains it is
set up, so pytest attributes it to that test rather than to whichever ran first. A separate
category so it can be turned into an error on its own with
-W error::assertpy2.DanglingAssertionWarning, the same way the vacuity and snapshot-key
warnings are escalated.
VacuousAssertionWarning ¶
Emitted when a universal assertion passes because there was nothing to check.
all_satisfy over an empty collection is true the way all([]) is true, so a query that
returned no rows makes the assertion pass without examining anything. That is the most common
silent false pass in a test suite, and the one a green run never reveals. Raise it to a failure
with -W error::assertpy2.VacuousAssertionWarning or silence it per call with allow_empty.
is_equal_to under ignore or include emits it too, when the filter left no key of the
compared dict or object to compare.