diff --git a/changes/3886.feature.md b/changes/3886.feature.md new file mode 100644 index 0000000000..faf7d968bd --- /dev/null +++ b/changes/3886.feature.md @@ -0,0 +1 @@ +Export public type aliases from ``zarr.types``, including ``JSON``, ``ZarrFormat``, ``ShapeLike``, ``CompressorLike``, ``FiltersLike``, ``Selection``, and other types used in the public API. diff --git a/docs/api/zarr/types.md b/docs/api/zarr/types.md new file mode 100644 index 0000000000..04f12a2d96 --- /dev/null +++ b/docs/api/zarr/types.md @@ -0,0 +1,5 @@ +--- +title: types +--- + +::: zarr.types diff --git a/docs/contributing.md b/docs/contributing.md index e62ce54c35..35d3cdb3d5 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -183,6 +183,19 @@ prek list If you would like to skip the failing checks and push the code for further discussion, use the `--no-verify` option with `git commit`. +### Public and private API + +Zarr follows standard Python conventions for distinguishing public from private API: + +- **Public modules** are top-level or one level deep: `zarr`, `zarr.dtype`, `zarr.types`, `zarr.codecs`, `zarr.storage`, etc. Users should only need to import from these modules. +- **Private modules** use a leading underscore or are within `zarr.core` (e.g. `zarr.core.dtype.npy.common`, `zarr.core._info`). These are implementation details and may change without notice. +- **`__all__`** declares the public API of a module. Every public module should define `__all__`. If a name is not in `__all__`, it is not part of the public API. +- **Type aliases** used across the codebase (e.g. `JSON`, `ZarrFormat`, `ShapeLike`) are re-exported from [`zarr.types`][zarr.types]. External users and tests should import from `zarr.types`. Internal code may import from the defining module (e.g. `zarr.core.common`). +- **Extension points** (custom codecs, data types) should be fully usable by importing only from public modules. If a user needs to reach into `zarr.core.*` to implement an extension, that is a gap in the public API. +- **Entry points** (e.g. `zarr.codecs`, `zarr.data_type`) are part of the public plugin interface and should be documented in the [extending guide](user-guide/extending.md). + +When adding new functionality, consider whether it belongs in the public API. If so, export it from the appropriate top-level module and add it to `__all__`. If it is an internal helper, place it in a private module or use a leading underscore. + ### Test coverage > **Note:** Test coverage for Zarr-Python 3 is currently not at 100%. This is a known issue and help is welcome to bring test coverage back to 100%. See issue #2613 for more details. diff --git a/mkdocs.yml b/mkdocs.yml index ce39fd0f2e..a6db637679 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -49,6 +49,7 @@ nav: - api/zarr/registry.md - api/zarr/storage.md - api/zarr/experimental.md + - api/zarr/types.md - ABC: - api/zarr/abc/index.md - api/zarr/abc/buffer.md diff --git a/src/zarr/types.py b/src/zarr/types.py index c159d5d5f2..3e0ea940bc 100644 --- a/src/zarr/types.py +++ b/src/zarr/types.py @@ -1,9 +1,73 @@ from typing import Any -from zarr.core.array import Array, AsyncArray +from zarr.core.array import ( + Array, + AsyncArray, + CompressorLike, + CompressorsLike, + FiltersLike, + SerializerLike, + ShardsLike, +) +from zarr.core.array_spec import ArrayConfigLike +from zarr.core.buffer import NDArrayLikeOrScalar +from zarr.core.chunk_key_encodings import ChunkKeyEncodingLike +from zarr.core.common import ( + JSON, + AccessModeLiteral, + BytesLike, + ChunksLike, + DimensionNamesLike, + MemoryOrder, + NodeType, + ShapeLike, + ZarrFormat, +) +from zarr.core.dtype import ZDTypeLike +from zarr.core.indexing import ( + BasicSelection, + CoordinateSelection, + Fields, + MaskSelection, + OrthogonalSelection, + Selection, +) from zarr.core.metadata.v2 import ArrayV2Metadata from zarr.core.metadata.v3 import ArrayV3Metadata +__all__ = [ + "JSON", + "AccessModeLiteral", + "AnyArray", + "AnyAsyncArray", + "ArrayConfigLike", + "ArrayV2", + "ArrayV3", + "AsyncArrayV2", + "AsyncArrayV3", + "BasicSelection", + "BytesLike", + "ChunkKeyEncodingLike", + "ChunksLike", + "CompressorLike", + "CompressorsLike", + "CoordinateSelection", + "DimensionNamesLike", + "Fields", + "FiltersLike", + "MaskSelection", + "MemoryOrder", + "NDArrayLikeOrScalar", + "NodeType", + "OrthogonalSelection", + "Selection", + "SerializerLike", + "ShapeLike", + "ShardsLike", + "ZDTypeLike", + "ZarrFormat", +] + type AnyAsyncArray = AsyncArray[Any] """A Zarr format 2 or 3 `AsyncArray`""" diff --git a/tests/package_with_entrypoint/__init__.py b/tests/package_with_entrypoint/__init__.py index 7b5dfb5a1e..48277f5a75 100644 --- a/tests/package_with_entrypoint/__init__.py +++ b/tests/package_with_entrypoint/__init__.py @@ -17,7 +17,7 @@ from typing import Any, ClassVar, Literal, Self from zarr.core.array_spec import ArraySpec - from zarr.core.common import ZarrFormat + from zarr.types import ZarrFormat class TestEntrypointCodec(ArrayBytesCodec): diff --git a/tests/test_api.py b/tests/test_api.py index 4198d56c78..5b5b5025fd 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -15,8 +15,7 @@ from pathlib import Path from zarr.abc.store import Store - from zarr.core.common import JSON, MemoryOrder, ZarrFormat - from zarr.types import AnyArray + from zarr.types import JSON, AnyArray, MemoryOrder, ZarrFormat import contextlib from typing import Literal diff --git a/tests/test_array.py b/tests/test_array.py index f7f564f30e..022552d879 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -48,7 +48,7 @@ from zarr.core.buffer import NDArrayLike, NDArrayLikeOrScalar, default_buffer_prototype from zarr.core.chunk_grids import _auto_partition from zarr.core.chunk_key_encodings import ChunkKeyEncodingParams -from zarr.core.common import JSON, ZarrFormat, ceildiv +from zarr.core.common import ceildiv from zarr.core.dtype import ( DateTime64, Float32, @@ -76,7 +76,7 @@ ) from zarr.storage import LocalStore, MemoryStore, StorePath from zarr.storage._logging import LoggingStore -from zarr.types import AnyArray, AnyAsyncArray +from zarr.types import JSON, AnyArray, AnyAsyncArray, ZarrFormat from .test_dtype.conftest import zdtype_examples diff --git a/tests/test_attributes.py b/tests/test_attributes.py index 269704d2a0..906160a63b 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -8,7 +8,7 @@ import zarr.core.attributes import zarr.storage from tests.conftest import deep_nan_equal -from zarr.core.common import ZarrFormat +from zarr.types import ZarrFormat if TYPE_CHECKING: from zarr.types import AnyArray diff --git a/tests/test_cli/conftest.py b/tests/test_cli/conftest.py index 4f95f47b5e..ac39b7adb1 100644 --- a/tests/test_cli/conftest.py +++ b/tests/test_cli/conftest.py @@ -5,7 +5,7 @@ import zarr from zarr.abc.store import Store -from zarr.core.common import ZarrFormat +from zarr.types import ZarrFormat def create_nested_zarr( diff --git a/tests/test_cli/test_migrate_v3.py b/tests/test_cli/test_migrate_v3.py index 7213aada12..6e35287bcd 100644 --- a/tests/test_cli/test_migrate_v3.py +++ b/tests/test_cli/test_migrate_v3.py @@ -17,12 +17,11 @@ from zarr.codecs.transpose import TransposeCodec from zarr.codecs.zstd import ZstdCodec from zarr.core.chunk_key_encodings import V2ChunkKeyEncoding -from zarr.core.common import JSON, ZarrFormat from zarr.core.dtype.npy.int import UInt8, UInt16 from zarr.core.group import Group, GroupMetadata from zarr.core.metadata.v3 import ArrayV3Metadata from zarr.storage._local import LocalStore -from zarr.types import AnyArray +from zarr.types import JSON, AnyArray, ZarrFormat typer_testing = pytest.importorskip( "typer.testing", reason="optional cli dependencies aren't installed" diff --git a/tests/test_codecs/test_codecs.py b/tests/test_codecs/test_codecs.py index 6e3e3f6d28..97efc96bff 100644 --- a/tests/test_codecs/test_codecs.py +++ b/tests/test_codecs/test_codecs.py @@ -28,8 +28,7 @@ from zarr.abc.codec import Codec from zarr.abc.store import Store from zarr.core.buffer.core import NDArrayLikeOrScalar - from zarr.core.common import MemoryOrder - from zarr.types import AnyAsyncArray + from zarr.types import AnyAsyncArray, MemoryOrder @dataclass(frozen=True) diff --git a/tests/test_codecs/test_numcodecs.py b/tests/test_codecs/test_numcodecs.py index eec0ecacae..20a6798e4d 100644 --- a/tests/test_codecs/test_numcodecs.py +++ b/tests/test_codecs/test_numcodecs.py @@ -54,7 +54,7 @@ def codec_conf() -> Iterator[Any]: if TYPE_CHECKING: - from zarr.core.common import JSON + from zarr.types import JSON def test_get_numcodec() -> None: diff --git a/tests/test_codecs/test_transpose.py b/tests/test_codecs/test_transpose.py index 949bb72a62..983c264ce5 100644 --- a/tests/test_codecs/test_transpose.py +++ b/tests/test_codecs/test_transpose.py @@ -8,9 +8,9 @@ from zarr.codecs import TransposeCodec from zarr.core.array_spec import ArrayConfig, ArraySpec from zarr.core.buffer import NDBuffer, default_buffer_prototype -from zarr.core.common import MemoryOrder from zarr.core.dtype import get_data_type_from_native_dtype from zarr.storage import StorePath +from zarr.types import MemoryOrder from .test_codecs import _AsyncArrayProxy diff --git a/tests/test_dtype/test_npy/test_common.py b/tests/test_dtype/test_npy/test_common.py index d8912a70ec..e44c345471 100644 --- a/tests/test_dtype/test_npy/test_common.py +++ b/tests/test_dtype/test_npy/test_common.py @@ -33,7 +33,7 @@ ) if TYPE_CHECKING: - from zarr.core.common import JSON, ZarrFormat + from zarr.types import JSON, ZarrFormat json_float_v2_roundtrip_cases: tuple[tuple[JSONFloatV2, float | np.floating[Any]], ...] = ( diff --git a/tests/test_dtype_registry.py b/tests/test_dtype_registry.py index b7ceb502b7..c1141c52c1 100644 --- a/tests/test_dtype_registry.py +++ b/tests/test_dtype_registry.py @@ -27,7 +27,7 @@ ) if TYPE_CHECKING: - from zarr.core.common import ZarrFormat + from zarr.types import ZarrFormat from .test_dtype.conftest import zdtype_examples diff --git a/tests/test_group.py b/tests/test_group.py index e53b0b9ea0..799cd067df 100644 --- a/tests/test_group.py +++ b/tests/test_group.py @@ -60,7 +60,7 @@ from _pytest.compat import LEGACY_PATH from zarr.core.buffer.core import Buffer - from zarr.core.common import JSON, ZarrFormat + from zarr.types import JSON, ZarrFormat @pytest.fixture(params=["local", "memory", "zip"]) diff --git a/tests/test_info.py b/tests/test_info.py index 28c8803c83..5a81b338f9 100644 --- a/tests/test_info.py +++ b/tests/test_info.py @@ -4,8 +4,8 @@ from zarr.codecs.bytes import BytesCodec from zarr.core._info import ArrayInfo, GroupInfo, human_readable_size -from zarr.core.common import ZarrFormat from zarr.core.dtype.npy.int import Int32 +from zarr.types import ZarrFormat ZARR_FORMATS = [2, 3] diff --git a/tests/test_metadata/test_consolidated.py b/tests/test_metadata/test_consolidated.py index 9e8b763ef7..8477a54be0 100644 --- a/tests/test_metadata/test_consolidated.py +++ b/tests/test_metadata/test_consolidated.py @@ -27,7 +27,7 @@ if TYPE_CHECKING: from zarr.abc.store import Store - from zarr.core.common import JSON, ZarrFormat + from zarr.types import JSON, ZarrFormat @pytest.fixture diff --git a/tests/test_metadata/test_v2.py b/tests/test_metadata/test_v2.py index 8c3082e924..feb0702ada 100644 --- a/tests/test_metadata/test_v2.py +++ b/tests/test_metadata/test_v2.py @@ -22,7 +22,7 @@ from typing import Any from zarr.abc.codec import Codec - from zarr.core.common import JSON + from zarr.types import JSON def test_parse_zarr_format_valid() -> None: diff --git a/tests/test_store/test_core.py b/tests/test_store/test_core.py index e673bfd40b..ddbcd6e6ac 100644 --- a/tests/test_store/test_core.py +++ b/tests/test_store/test_core.py @@ -8,7 +8,6 @@ import zarr from zarr import Group -from zarr.core.common import AccessModeLiteral, ZarrFormat from zarr.storage import FsspecStore, LocalStore, MemoryStore, StoreLike, StorePath, ZipStore from zarr.storage._common import contains_array, contains_group, make_store_path from zarr.storage._utils import ( @@ -18,6 +17,7 @@ _relativize_path, normalize_path, ) +from zarr.types import AccessModeLiteral, ZarrFormat @pytest.fixture( diff --git a/tests/test_store/test_fsspec.py b/tests/test_store/test_fsspec.py index 5e9e33f0e4..29b93706ca 100644 --- a/tests/test_store/test_fsspec.py +++ b/tests/test_store/test_fsspec.py @@ -27,7 +27,7 @@ import botocore.client import s3fs - from zarr.core.common import JSON + from zarr.types import JSON # Warning filter due to https://github.com/boto/boto3/issues/3889 diff --git a/tests/test_store/test_memory.py b/tests/test_store/test_memory.py index 03c8b24271..edbd234be0 100644 --- a/tests/test_store/test_memory.py +++ b/tests/test_store/test_memory.py @@ -18,7 +18,7 @@ if TYPE_CHECKING: from zarr.core.buffer import BufferPrototype - from zarr.core.common import ZarrFormat + from zarr.types import ZarrFormat # TODO: work out where this warning is coming from and fix it