Skip to content

Compare read-only containers recursively in test operations - #257

Open
liugedragon wants to merge 2 commits into
stefankoegl:masterfrom
liugedragon:fix/readonly-mapping-tests
Open

liugedragon wants to merge 2 commits into
stefankoegl:masterfrom
liugedragon:fix/readonly-mapping-tests

Conversation

@liugedragon

@liugedragon liugedragon commented Nov 9, 2026 •

Copy link
Copy Markdown

Whole read-only containers can fall back to Python equality instead of applying JSON equality recursively. For example, MappingProxyType({'a': True}) passes a root test against {'a': 1}, and (True,) passes against (1,). Equivalent list and tuple values can also compare unequal.

Use Mapping and non-text Sequence in _json_equal. Both are already accepted by the module and documented in its stubs; immutable sequences are also handled by the existing restoration code. Objects and arrays are compared recursively under the RFC 6902 rules. Strings and bytes remain scalar values, and mutation requirements are unchanged.

Regression coverage includes root and nested read-only mappings, tuple/list comparisons in both directions, a generic read-only sequence, Boolean/number rejection, numeric 1/1.0 equivalence, length/order differences, text/bytes exclusions, and preservation of input identity and content. The sequence follow-up adds 95 subcases; 33 fail on the previous PR revision and pass after the fix.

Validation on Windows / Python 3.13.4:

  • python tests.py: 322 tests run, OK (1 skipped).
  • Standard-profile python property_tests.py: 26 tests, OK.
  • CI's fatal Flake8 checks: passed with Flake8 7.3.0.
  • Additional bounded local RFC oracle: three properties at 1,000 examples each, mixing native and read-only mappings/sequences; all passed.
  • git diff --check: passed.

Earlier validation: a 5,000-example single-process stress run hit a Hypothesis MemoryError and replay error; the affected two properties each passed at the same budget in fresh processes. On the previous CI revision, all unit-test and property-test matrix steps passed; coverage-upload jobs failed while downloading the Coveralls binary/checksum.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Copilot was unable to run its full agentic suite in this review.

Copilot review overview

2 open issues
What changed in this PR

This PR updates JSON Patch test operation equality semantics so read-only mapping types (e.g., MappingProxyType) are compared structurally instead of via Python’s default equality, preventing True/1 and False/0 from being treated as equal.

Changes:

  • Broaden _json_equal mapping comparison from MutableMapping to Mapping.
  • Add regression tests ensuring test fails for bool-vs-int mismatches on read-only mappings and does not mutate input documents on success/failure.
File Description
jsonpatch.py Fixes _json_equal to structurally compare read-only mappings, preserving RFC6902 bool/number distinction.
tests.py Adds regression coverage for MappingProxyType and nested read-only mapping scenarios (success and failure).

Review effort: Lite

Comment thread jsonpatch.py
Comment on lines 1459 to 1465
if isinstance(first, bool) or isinstance(second, bool):
return isinstance(first, bool) and isinstance(second, bool) and \
first == second
if isinstance(first, MutableMapping) and \
isinstance(second, MutableMapping):
if isinstance(first, Mapping) and isinstance(second, Mapping):
return len(first) == len(second) and all(
key in second and _json_equal(first[key], second[key])
for key in first)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 51b32f1. The equality check now accepts non-text Sequence implementations, consistent with the stubs and the immutable-container handling in _restore; strings and bytes stay scalar, and write operations still require mutable containers.

The new regressions cover tuple/list comparisons in both directions, a generic read-only sequence, nested containers, Boolean/number distinctions, numeric equivalence, and unchanged inputs. Of the 95 new subcases, 33 fail on the previous PR revision. The updated suite passes locally: 322 unit tests (one skipped) and 26 standard-profile property tests.

Comment thread tests.py
Comment on lines +449 to +466
def test_test_read_only_mapping_preserves_boolean_types(self):
for mapping_type in (dict, MappingProxyType):
for nested in (False, True):
for actual, expected in ((True, 1), (False, 0),
(1, True), (0, False)):
with self.subTest(mapping_type=mapping_type, nested=nested,
actual=actual, expected=expected):
mapping = mapping_type({'a': actual})
doc = {'nested': mapping} if nested else mapping
path = '/nested' if nested else ''
original = json.dumps(doc, default=dict)
patch = [{'op': 'test', 'path': path,
'value': {'a': expected}}]

with self.assertRaises(jsonpatch.JsonPatchTestFailed):
jsonpatch.apply_patch(doc, patch, in_place=True)

self.assertEqual(json.dumps(doc, default=dict), original)
@liugedragon liugedragon changed the title Compare read-only mappings recursively in test operations Compare read-only containers recursively in test operations Nov 10, 2026

This branch has not been deployed

No deployments
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.

2 participants