From 84f10eb55100b5161b84db168308a03107d2804d Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Fri, 14 Aug 2026 09:23:08 +0200 Subject: [PATCH 1/2] fix(zarr-metadata): make JSONValue's array arm covariant `list["JSONValue"] | tuple["JSONValue", ...]` is invariant in the element type, so a value typed with any narrower element -- a `list[str]` field on a TypedDict, a `Sequence[float]` -- was not assignable to `JSONValue`, and a TypedDict carrying such fields was not assignable to `Mapping[str, JSONValue]`. pyright's diagnostic for the failure suggests the fix verbatim: "Consider switching from list to Sequence which is covariant." The array arm is now `Sequence["JSONValue"]`. The docstring records the deliberate type-level cost (`Sequence` admits `str`/`bytes`; runtime narrowing must exclude them regardless of the alias's spelling). Found while aliasing zarr-cm's JsonValue to this type: the two aliases are structurally identical except for this arm, and with it changed, pyright unifies them across the package boundary. Assisted-by: ClaudeCode:claude-opus-5 --- packages/zarr-metadata/changes/295.bugfix.md | 9 +++++++ .../src/zarr_metadata/_common.py | 24 ++++++++++++------- 2 files changed, 24 insertions(+), 9 deletions(-) create mode 100644 packages/zarr-metadata/changes/295.bugfix.md diff --git a/packages/zarr-metadata/changes/295.bugfix.md b/packages/zarr-metadata/changes/295.bugfix.md new file mode 100644 index 0000000000..466bf5941d --- /dev/null +++ b/packages/zarr-metadata/changes/295.bugfix.md @@ -0,0 +1,9 @@ +`JSONValue`'s array arm is now the covariant `Sequence["JSONValue"]` rather +than the invariant `list["JSONValue"] | tuple["JSONValue", ...]`. Values typed +with a narrower element type — a `list[str]` field on a TypedDict, a +`Sequence[float]` — now count as JSON values, and TypedDicts whose fields +carry precise types are now assignable to `Mapping[str, JSONValue]`. +Type-level cost, accepted deliberately: `Sequence` says nothing about the +concrete container and admits `str`/`bytes`, so runtime code narrowing a JSON +array must exclude `str`/`bytes`/`bytearray` — as it already had to, since +`str` was always a union arm. diff --git a/packages/zarr-metadata/src/zarr_metadata/_common.py b/packages/zarr-metadata/src/zarr_metadata/_common.py index f3259f7b73..08c143107f 100644 --- a/packages/zarr-metadata/src/zarr_metadata/_common.py +++ b/packages/zarr-metadata/src/zarr_metadata/_common.py @@ -6,21 +6,14 @@ `zarr_metadata.v3.data_type`. """ -from collections.abc import Mapping +from collections.abc import Mapping, Sequence from typing import NotRequired from typing_extensions import TypeAliasType, TypedDict JSONValue = TypeAliasType( "JSONValue", - int - | float - | bool - | str - | list["JSONValue"] - | tuple["JSONValue", ...] - | Mapping[str, "JSONValue"] - | None, + int | float | bool | str | Sequence["JSONValue"] | Mapping[str, "JSONValue"] | None, ) """A recursive type alias for JSON-encodable values. @@ -28,6 +21,19 @@ self-reference is a named recursion point that pydantic can resolve when building a `TypeAdapter`; a bare recursive `TypeAlias` raises `PydanticUserError`/`RecursionError` at validation time. + +The array arm is the covariant `Sequence` rather than the invariant +`list["JSONValue"] | tuple["JSONValue", ...]`, so values typed with a +*narrower* element type still count as JSON values: a `list[str]` field on a +TypedDict is assignable to `JSONValue` under `Sequence` but not under +`list[JSONValue]` (`list` is invariant in its element type, and pyright's +diagnostic for that failure suggests exactly this change). This is what lets +downstream TypedDicts give their fields precise types (`Sequence[str]`, +`list[int]`, ...) while remaining assignable to `Mapping[str, JSONValue]`. +The type-level cost, accepted deliberately: `Sequence` says nothing about the +concrete container, and it admits `str`/`bytes` (`str` was already a union +arm); runtime code narrowing a JSON array must exclude `str`/`bytes`/ +`bytearray` regardless of how this alias is spelled. """ From e960b607d56f09636e6c9ca245919103a903d8cc Mon Sep 17 00:00:00 2001 From: Davis Bennett Date: Fri, 14 Aug 2026 11:45:26 +0200 Subject: [PATCH 2/2] Rename 295.bugfix.md to 4264.bugfix.md --- packages/zarr-metadata/changes/{295.bugfix.md => 4264.bugfix.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/zarr-metadata/changes/{295.bugfix.md => 4264.bugfix.md} (100%) diff --git a/packages/zarr-metadata/changes/295.bugfix.md b/packages/zarr-metadata/changes/4264.bugfix.md similarity index 100% rename from packages/zarr-metadata/changes/295.bugfix.md rename to packages/zarr-metadata/changes/4264.bugfix.md