Add position context to deep validation errors - #1621
Open
Shy7777 wants to merge 2 commits into
Open
Conversation
There was a problem hiding this comment.
🟢 Approval recommended
The change is narrowly scoped, preserves validator/exception contracts as stated, and is backed by comprehensive regression tests and documentation updates.
Pull request overview
This PR improves diagnostics for deep_iterable() / deep_mapping() by adding Python 3.11+ exception notes that point to the failing member position (or mapping key/value role and entry index) while preserving the original exception object and message/args. This addresses the long-standing confusion where inner-validator failures appear to refer to the containing attribute without indicating which member actually failed.
Changes:
- Add
_add_validation_note()and annotate inner-validator failures indeep_iterable/deep_mappingusing exception notes on Python 3.11+. - Add targeted regression tests to ensure exception identity/args are preserved and that iteration/lookup failures are not misclassified as validation failures.
- Update API docs and add a changelog fragment describing the new context notes behavior.
File summaries
| File | Description |
|---|---|
| tests/test_validators.py | Adds regression tests validating note content, preservation of exception identity/args, and “do not annotate” cases for iteration/lookup/base exceptions. |
| src/attr/validators.py | Implements context-note attachment for deep validators on Python 3.11+ while preserving exception contracts and avoiding annotating iteration/lookup failures. |
| docs/api.rst | Documents the new note behavior and updates doctest outputs to show the added context lines. |
| changelog.d/1621.change.md | Announces the new Python 3.11+ deep-validation context notes and preservation guarantees. |
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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.
Summary
When an inner validator fails, its error can refer to the containing attribute without identifying the failing member. For example, validating
["abc", ""]withdeep_iterable([instance_of(str), min_len(1)])reports a length error forx.On Python 3.11+, append exception notes identifying the member position or the mapping key/value role and entry position when the exception supports notes. Nested validators add context from the innermost failure outwards. Preserve the original exception object, arguments, and
Attributepassed to custom validators; do not format arbitrary mapping keys or annotate iterator/lookup failures as validation failures.This enhances traceback diagnostics: standard exception messages remain unchanged, and Python 3.10 retains its existing behavior. Related to #1245.
Local validation on macOS arm64:
Python 3.14.5: 1,426 passed, 4 skipped, 2 expected failures.
Python 3.10.20: 1,416 passed, 14 skipped, 1 expected failure.
The new regression group has 19 cases; before the implementation, 7 failed because the context was missing and 12 passed.
Combined statement and branch coverage: 100%.
Ruff check/format, 473 Sphinx doctests, and a strict HTML documentation build passed. Pytest directly asserts the note text; Sphinx's default exception-detail matching is less strict.
Towncrier successfully rendered the news fragment for this PR.
Public signatures and type stubs are unchanged. The complete upstream interpreter, wheel, type-checking, and pre-commit matrices have not been run locally.
Pull Request Checklist
mainbranch..pyi).typing_tests/baseline.pyor, if necessary,typing_tests/mypy.py.attr/__init__.pyi, they've also been re-imported inattrs/__init__.pyi.docs/api.rstby hand.@attr.s()and@attrs.define()have to be added by hand too.versionadded,versionchanged, ordeprecateddirectives.The next version is the second number in the current release + 1.
The first number represents the current year.
So if the current version on PyPI is 26.2.0, the next version is gonna be 26.3.0.
If the next version is the first in the new year, it'll be 27.1.0.
.rstand.mdfiles is written using semantic newlines.changelog.d.