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