From 5c427ac65dae7fab090476677a0d00cf08ca06d5 Mon Sep 17 00:00:00 2001 From: nstarman Date: Sat, 10 Oct 2026 19:45:14 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20docs:=20document=20that=20Immuta?= =?UTF-8?q?bleMap=20|=20merges=20any=20Mapping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since #82, frozendict | other merges only a dict or frozendict, matching the Python 3.15 built-in. ImmutableMap.__or__ intentionally stays looser and merges any Mapping. Document that in the docstring and README, and pin it (and that the reflected | is not loosened) with a test. Co-Authored-By: Claude Opus 5.5 --- README.md | 5 +++++ src/xmmutablemap/_core.py | 6 ++++++ tests/test_immutablemap.py | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/README.md b/README.md index 2c069bd..251c84b 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,11 @@ print(jax.tree.structure(frozendict(a=1, b=2))) > switching, check anything that depends on leaf position or tree structure, > such as indexing `jax.tree.leaves`, saved optimizer states, or checkpoints. +One deliberate difference: `ImmutableMap | other` merges **any** `Mapping`, +while `frozendict | other` merges only a `dict` or `frozendict` (so, for +example, a custom `Mapping` with no `|` of its own raises `TypeError`). Convert +such mappings with `dict(...)` when switching to the built-in. + ### JAX Integration One of the key benefits of `ImmutableMap` is its compatibility with JAX. Since diff --git a/src/xmmutablemap/_core.py b/src/xmmutablemap/_core.py index d65bd03..06bbb23 100644 --- a/src/xmmutablemap/_core.py +++ b/src/xmmutablemap/_core.py @@ -74,6 +74,12 @@ def __or__(self, other: Any, /) -> "ImmutableMap[K, V]": `frozendict.__or__` hardcodes its result type, so a subclass would otherwise get a plain `frozendict` back. + Deliberately looser than `frozendict`: ``ImmutableMap | other`` + merges *any* `Mapping`, whereas ``frozendict | other`` merges only a + `dict` or `frozendict` and raises `TypeError` for other mappings that + bring no ``|`` of their own. The reflected ``other | ImmutableMap`` + is inherited from `frozendict` and is not loosened. + Examples -------- >>> from xmmutablemap import ImmutableMap diff --git a/tests/test_immutablemap.py b/tests/test_immutablemap.py index e2e6d60..d61f97c 100644 --- a/tests/test_immutablemap.py +++ b/tests/test_immutablemap.py @@ -154,6 +154,14 @@ def test_or(self, d: ImmutableMap[str, Any]) -> None: assert d | OrderedDict([("c", 3)]) == ImmutableMap(a=1, b=2, c=3) assert d | MappingProxyType({"c": 3}) == ImmutableMap(a=1, b=2, c=3) + # Looser than `frozendict`: any Mapping merges, even one with no `|`. + assert d | Plain({"c": 3}) == ImmutableMap(a=1, b=2, c=3) + with pytest.raises(TypeError, match="unsupported operand type"): + _ = frozendict(a=1) | Plain({"c": 3}) + # ...but only on the left; the reflected `|` is `frozendict`'s. + with pytest.raises(TypeError, match="unsupported operand type"): + _ = Plain({"c": 3}) | d + # Should raise TypeError if not a mapping. with pytest.raises(TypeError, match="unsupported operand type"): _ = d | 1