Skip to content

WIP: broaden isdict() to support generic Mapping types (#14461) [no review yet]#14463

Draft
RonnyPfannschmidt wants to merge 1 commit into
pytest-dev:mainfrom
RonnyPfannschmidt:fix/14461-mapping-assertrepr
Draft

WIP: broaden isdict() to support generic Mapping types (#14461) [no review yet]#14463
RonnyPfannschmidt wants to merge 1 commit into
pytest-dev:mainfrom
RonnyPfannschmidt:fix/14461-mapping-assertrepr

Conversation

@RonnyPfannschmidt
Copy link
Copy Markdown
Member

Summary

[AI-assisted WIP experiment — no review yet]

Broadens isdict() from isinstance(x, dict) to isinstance(x, Mapping) so non-dict Mapping types get structured assertion diff output (#14461).

  • One-line fix in src/_pytest/assertion/util.py
  • _compare_eq_dict already accepts Mapping in its type signature, so only the guard needed changing
  • Backward-compatible: dict is a subclass of Mapping

Mapping types surveyed

Now get structured diff (previously fell through to generic iterable diff):

  • types.MappingProxyType (stdlib)
  • collections.ChainMap (stdlib)
  • collections.UserDict (stdlib)
  • immutables.Map
  • bidict / frozenbidict
  • httpx.Headers (normalizes keys on iteration, so set() works)

Already handled (dict subclasses):

  • collections.OrderedDict
  • python-box.Box / dynaconf.DynaBox
  • sortedcontainers.SortedDict

Known-problematic — xfail tests added as documentation:

  • requests.CaseInsensitiveDict — iterates original-cased keys, so set(left) & set(right) misses common keys when casing differs (misleading, not crash)
  • multidict.MultiDictset() collapses duplicate keys, __getitem__ returns only first value (silently lossy, not crash)
  • multidict.CIMultiDict — both problems combined

Key insight

The failure modes for non-dict Mappings are misleading output, not crashes. The _compare_eq_dict algorithm (set(keys) intersection/difference + mapping[k] lookups) works correctly for the vast majority of real-world Mapping implementations. The problematic types (case-insensitive, multi-valued) produce suboptimal but still safe output — which is better than the current state of no structured diff at all.

Test plan

  • 6 new passing tests for stdlib mapping types (MappingProxyType, ChainMap, UserDict, custom Mapping)
  • 3 xfail tests documenting known-problematic external types
  • All 61 existing TestAssert_reprcompare* tests pass (no regression)
  • CI validation

Made with Cursor

)

[AI-assisted experiment — not ready for review]

Change isdict() from isinstance(x, dict) to isinstance(x, Mapping)
so that non-dict Mapping types (MappingProxyType, ChainMap, UserDict,
bidict, immutables.Map, etc.) get structured "Differing items / Omitting
N identical items / Left contains N more items" output instead of falling
through to the generic iterable diff.

Adds tests for stdlib mapping types and xfail tests documenting
known-problematic external types where set(keys) diverges from the
mapping's own key equality (case-insensitive dicts, multi-valued dicts).

Co-authored-by: Cursor AI <ai@cursor.sh>
Co-authored-by: Anthropic Claude Sonnet 4 <claude@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant