v0.4.1 #19
IKrysanov
announced in
Announcements
v0.4.1
#19
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
0.4.1 - 2026-06-06
Fixed
TestRunResult.casesis now atuple[CaseResult, ...]instead of alist, so thefrozen=Trueclaim on the dataclass is honest.Before this change,
frozen=Trueonly blocked attributereassignment (
result.cases = [...]); the list itself remainedmutable so
result.cases.append(...)silently modified a"frozen" instance. A
__post_init__coerces any iterable thecaller passes (list, generator, etc.) into a tuple, so the existing
parsers that accumulate cases via
.appendon a local list andpass it through continue to work without change. Breaking only
for external code that depended on the mutability of
result.cases-- which was the bug we're fixing.
JSONResultParserandJUnitResultParsernow produce identicalfailed_node_idsfor the same suite. The JSON parser previouslyemitted pytest's native form (
"tests/test_x.py::test_y") whilethe JUnit parser emitted the dotted JUnit-XML form
(
"tests.test_x::test_y"), making downstream consumers (Airflowbranches reading XCom, alerting that diffs the list across runs)
silently parser-dependent. The JSON parser is now normalised to the
same dotted form as JUnit. Breaking for any consumer that pinned
on the slash form coming out of
JSONResultParsersince 0.4.0 --they get the new format starting with this release. The conversion
direction was chosen for information-preservation: from the slash
path with
.pywe can always derive the dotted module, but thereverse from JUnit's classname alone is ambiguous (
module.Classvs
module.subname).JUnitResultParser.parseno longer catchesExceptionfrom theunderlying XML parser. The handler now lists exactly the exception
types that a JUnit parse can legitimately fail with:
xml.etree.ElementTree.ParseError(malformed XML),ValueError(covers everydefusedxmlsecurity exception viaDefusedXmlException), andOSError(file became unreadablebetween our
exists()check and the actual read). Anything else--
MemoryError,AttributeErrorfrom a bug in our code,RecursionError-- now escapes the parser uncaught so the workerlogs the real problem instead of seeing a misleading
"Failed to parse JUnit report" message.
JSONResultParsernow reports per-casetimeas the sum of thesetup+call+teardownphase durations frompytest-json-report, instead of reading only thecallphase.Previously, a case that errored during
setup(e.g. a fixtureraised) had no
callsection in the JSON document and ended upwith
time=0.0-- making per-case timings misleading exactly forthe failures users most want to investigate. The new behaviour
matches what pytest's own JUnit XML writer reports as the case
timeand so restores parity with :class:JUnitResultParser.Malformed durations on individual phases are still tolerated: that
phase contributes
0and the others are summed as usual.Changed
SubprocessPytestRunnerdrainer threads now count the per-streamoutput cap via
len(chunk)(character count) instead oflen(chunk.encode("utf-8", errors="replace")). The previousimplementation allocated a throwaway
bytesobject on everyreadline()-- tens of thousands of allocations on a verbosesuite -- just to get a precise byte count.
len(chunk)is anO(1) cached lookup on
str. The trade-off is that the cap is nowan approximate byte count: exact for ASCII output (which is what
pytest emits in practically all cases), under-counting bytes by up
to 4x for UTF-8 multi-byte content. The cap parameter is still
named
max_output_bytesfor back-compat; the docstringdocuments the approximation. Microbench on a 10k-line ASCII/Cyrillic
mix: 41ms -> 14ms per 50 iterations (~3x speedup) with a 1.002x
under-count ratio.
TestRunResult.to_xcomno longer goes throughdataclasses.asdict.The previous implementation recursively converted every nested
:class:
CaseResultinto a dict tree before discarding the wholecasesentry; for suites with thousands of tests that's a realamount of CPU and short-lived garbage on the worker. The new path
builds the payload field-by-field, ~30x faster on a 5k-case suite
(~227ms -> ~7ms in a microbench; bigger speedup on larger suites
due to the per-case dataclass-to-dict overhead). The wire format is
unchanged. A new structural test pins the set of XCom keys against
the :class:
TestRunResultschema so any future field addition is aconscious choice rather than a silent omission.
This discussion was created from the release v0.4.1.
Beta Was this translation helpful? Give feedback.
All reactions