Repository navigation
Introduce exception records and traceback renderers - #15332
Closed
doubledare704 wants to merge 12 commits into
Closed
doubledare704 wants to merge 12 commits into
doubledare704 wants to merge 12 commits into
Conversation
…st-dev#15072) ExceptionInfo.getrepr() and the Repr* classes currently bake display style, path resolution, and syntax markers into pre-rendered strings at capture time, while report serialization reconstructs only a subset of those dataclasses by hand and degrades other representations across process boundaries. Introduce the first increment of pytest-dev#15072 alongside existing code without changing current consumers: - Add frozen, schema-versioned data model classes (Location, SyntaxErrorDetails, FrameRecord, ExceptionRecord) in _pytest._code.record with JSON-compatible to_dict()/from_dict() serialization and a lossless to_rich_trace() adapter. - Model exception chains and BaseExceptionGroup sub-exceptions as a tree (cause, context, children) while breaking reference cycles eagerly at capture time so all downstream traversal and serialization operate on a finite acyclic structure. - Capture raw frame statement lines eagerly within generous bounds (MAX_STATEMENT_LINES, MAX_STATEMENT_LINE_CHARS) anchored at statement_lineno so pathological frames cannot bloat wire payloads while preserving original indentation for PEP 657 byte-offset column spans and render-time deindenting. - Add ExceptionInfo.record() to snapshot full tracebacks, hidden-frame flags, per-entry style overrides, assertion striptext, notes, syntax error details, and RecursionError indices.
Introduce `PytestTraceback` as the presentation layer over the pure-data `ExceptionRecord` model so traceback formatting is decoupled from live Python frames and pre-baked `Repr*` strings (Step 2 of pytest-dev#15072). `PytestTraceback` supports all existing traceback styles (`long`, `short`, `auto`, `line`, `no`, `value`, `native`) as well as a new `rich` style powered by `ExceptionRecord.to_rich_trace()`, and implements both pytest's `toterminal(tw)` protocol (`to_repr()` compatibility shim) and Rich's `__rich_console__` protocol. Wire `--tb=rich` through CLI options, `Node._repr_failure_py`, and `ExceptionInfo.getrepr()`. Refs pytest-dev#15072
…d-then-render Previously, ExceptionInfo.getrepr() baked the requested --tb style directly into the constructed Repr* objects, dropping per-frame file locations under --tb=line and --tb=no (pytest-dev#14720) and preventing reporting plugins or cross-process consumers from accessing the full structured traceback when terminal output was suppressed or shortened. Route ExceptionInfo.getrepr() through ExceptionInfo.record() and PytestTraceback.to_repr() across all traceback styles so that exception capture always records the complete structured ExceptionRecord before rendering: - Attach the underlying ExceptionRecord to ExceptionRepr.record and preserve ReprEntry.reprfileloc for all frames even when rendering --tb=line or --tb=no, while keeping terminal output for those styles compact. - Add compatibility properties (reprcrash, reprtraceback, chain) on ExceptionRecord and make FrameRecord hashable when locals are captured so records can serve as xdist collection-error dictionary keys. - Preserve None syntax error messages in SyntaxErrorDetails so bare SyntaxError() instances format identically to stdlib (<no detail available>). - Extend report JSON serialization (_report_to_json / _report_kwargs_from_json) to round-trip ExceptionRecord payloads both when attached to ExceptionRepr and when stored directly as report.longrepr. Fixes pytest-dev#14720 Refs pytest-dev#15072
…4 of pytest-dev#15072) Separate exception capture from formatting by introducing a structured, serializable data model (`Location`, `SyntaxErrorDetails`, `FrameRecord`, `ExceptionRecord`, and the `FailureRecord` protocol) alongside a dedicated `PytestTraceback` renderer: - Add `ExceptionInfo.record()` to capture traceback frames, chain topology (`cause`, `context`, `suppress_context`, `children`), bounded source statements with PEP 657 byte spans, and formatted locals into an immutable `ExceptionRecord` tree with `to_dict()`/`from_dict()` and `to_rich_trace()` support. - Introduce `PytestTraceback` to render `ExceptionRecord` across all pytest traceback styles (`auto`, `long`, `short`, `line`, `native`, `value`, `no`) and add `--tb=rich` backed by Rich's `Traceback` renderer. - Switch `ExceptionInfo.getrepr()` and report JSON serialization to a record-then-render pipeline, preserving `longrepr.record` and crash locations even under `--tb=line` and `--tb=no` (fixes pytest-dev#14720). - Migrate `ReprFailDoctest`, `FixtureLookupErrorRepr`, and `CollectErrorRepr` to implement the `FailureRecord` protocol (`sections`, `crash`, `to_dict()`/`from_dict()`, `__rich_console__()`), and replace raw skip 3-tuples on `report.longrepr` with `Location` while maintaining tuple-unpacking and xdist wire compatibility. Refs pytest-dev#15072 Fixes pytest-dev#14720
Previously, non-native traceback styles fell back to Python's stdlib `TracebackException.format()` whenever an `ExceptionGroup` or `BaseExceptionGroup` appeared anywhere in the exception chain (pytest-dev#9159). That workaround bypassed pytest's traceback formatting—losing `E ` error prefixes, statement range extraction, locals inspection, and style-specific rendering (`long`, `short`, `auto`, `no`, `value`)—and required special-case filtering on live `TracebackException` instances. With `ExceptionRecord.children` capturing sub-exceptions as part of the recursive exception model, `PytestTraceback` now renders exception groups directly in the active pytest traceback style: - Recursively format each sub-exception (including its own cause/context chain and nested subgroups) using the configured pytest style, boxed with tree prefixes (`| `) and numbered group separators matching stdlib header widths. - Keep full `ExceptionGroup: <msg> (N sub-exception[s])` headers on group frames while preserving `tryshort` crash summaries for leaf exceptions. - Preserve the outermost exception's `reprtraceback` and `reprcrash` on `ExceptionChainRepr` when converting or deserializing exception group records. - Delegate `ExceptionInfoFormatter.repr_excinfo()` to `PytestTraceback` and remove the `pytest-dev#9159` `TracebackException` fallback and `_filter_tracebackexception` helper. Part of pytest-dev#15072 (Step 5).
…tured failures (Step 6 of pytest-dev#15072) Add _pytest._code.matching with check_type/check_regex primitives and plain-data MatchFailure objects that render to the legacy text or a rich Tree. ExceptionInfo.match, group_contains and RaisesExc/RaisesGroup now use them; fail_reason is derived from the structured failure. Add regression tests for pytest-dev#12175 (striptext captured in the record).
…5072) - Use co_positions() range in Python >= 3.11 when slicing statement range, keeping getstatementrange_ast fallback for Python 3.10 and dynamic frames without line positions. - Capture exact line and column ranges in FrameRecord eagerly at capture time. - Update PytestTraceback to render PEP 657 carets across long, short, auto, and native styles, and underline error spans in rich style. - Strip trailing newlines and handle multibyte UTF-8 offsets in _highlight_arrows_for_line. - Add comprehensive TestPep657Carets suite in testing/code/test_excinfo.py covering position capture, serialization round-trip, all styles, multibyte UTF-8, multiline expressions, and Python 3.10 position-free fallback.
… classes and getrepr() (Step 8 of pytest-dev#15072) - Export ExceptionRecord, FrameRecord, Location, SyntaxErrorDetails, FailureRecord, and PytestTraceback in top-level pytest namespace (__all__). - Add autoclass documentation for data model classes in doc/en/reference/reference.rst. - Deprecate ExceptionInfo.getrepr() and its formatting parameters in favor of ExceptionInfo.record() and PytestTraceback. - Deprecate legacy Repr* classes (TerminalRepr, ExceptionRepr, ExceptionChainRepr, ReprExceptionInfo, ReprTraceback, ReprTracebackNative, ReprEntryNative, ReprEntry, ReprFileLocation, ReprLocals, ReprFuncArgs) and compatibility properties on ExceptionRecord (reprcrash, reprtraceback, chain). - Document deprecations and migration guides in doc/en/deprecations.rst. - Update internal callers to pass _warn_deprecated=False or _ispytest=True to avoid spurious deprecation warnings during test execution. - Add comprehensive deprecation and public API tests in testing/code/test_record.py.
for more information, see https://pre-commit.ci
Member
|
just no - this is a deep topic with missing discussion and everything here doesnt even tryto understand/mess around with it - |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #15072.
Exception capture, traceback formatting, and report serialization currently share the legacy
ExceptionInfo/Repr*pipeline. This draft introduces a plain-data exception snapshot and renderers so consumers can retain structured frames and exception groups even when terminal output is shortened with--tb=lineor suppressed with--tb=no.I would like feedback on the model and migration boundaries before treating this as a complete implementation of the proposal.
Implemented
ExceptionInfo.record()and frozen record dataclasses for exceptions, frames, locations, and syntax errors, with versioned JSON serialization, eager source capture, notes, cause/context links, and group children.PytestTracebackand--tb=rich, record-backed legacy rendering, pytest-style exception-group rendering, and PEP 657 positions/carets.linecachestate. Honorchain=Falseacross rendering entry points.ExceptionInfoand raisers.Locationfor skips.Remaining migration work
This draft does not complete #15072:
ExceptionChainRepr/ReprExceptionInfoobjects with an attached record. Their wire payload still includes rendered legacy data alongside the record.pytest_internalerrorstill receives a legacy representation; forwarding structured internal errors through xdist remains pending.reprcrash.Local validation
On macOS with Python 3.13.3:
git diff --checkpassed for the latest patch's files.36a1c7d01, before the latest regression fixes. These local tool versions differ from the pinned pre-commit versions.The three full-suite failures are:
testing/acceptance_test.py::TestInvocationVariants::test_doctest_idtesting/test_assertion.py::test_raise_assertion_error_raising_reprtesting/test_doctest.py::TestDoctests::test_fixture_doctest_skip_has_line_numberAll three also reproduce at
36a1c7d01; that establishes they predate the latest fixes, not that they are unrelated to this PR. They still need resolution or comparison with upstream before this is ready. CI and the complete xdist integration suite are not claimed green.Feedback requested
Would maintainers prefer this split into the proposal's independently reviewable increments, starting with the model/capture layer? The main remaining decisions are when to switch
longreprand the wire format, when to start deprecations, and whether the tree/source-capture/Rich-adapter choices are suitable.AI assistance: Codex was used for code review, regression fixes, and local validation.