diff --git a/pyproject.toml b/pyproject.toml index e0ca815d95..00c6333aa5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -160,6 +160,7 @@ strict_concatenate = true check_untyped_defs = true disallow_untyped_decorators = true +disallow_any_generics = true [[tool.mypy.overrides]] module = [ @@ -176,6 +177,21 @@ module = [ ] check_untyped_defs = false +[[tool.mypy.overrides]] +module = [ + "zarr.v3.abc.codec", + "zarr.v3.codecs.bytes", + "zarr.v3.codecs.pipeline", + "zarr.v3.codecs.sharding", + "zarr.v3.codecs.transpose", + "zarr.v3.array_v2", + "zarr.v3.array", + "zarr.v3.sync", + "zarr.convenience", + "zarr.meta", +] +disallow_any_generics = false + [tool.pytest.ini_options] doctest_optionflags = [ diff --git a/src/zarr/_storage/store.py b/src/zarr/_storage/store.py index 911af20fda..6e13b08cc7 100644 --- a/src/zarr/_storage/store.py +++ b/src/zarr/_storage/store.py @@ -14,7 +14,7 @@ DEFAULT_ZARR_VERSION = 2 -class BaseStore(MutableMapping): +class BaseStore(MutableMapping[str, Any]): """Abstract base class for store implementations. This is a thin wrapper over MutableMapping that provides methods to check @@ -165,7 +165,7 @@ def rmdir(self, path: str = "") -> None: # allow MutableMapping for backwards compatibility -StoreLike = Union[BaseStore, MutableMapping] +StoreLike = Union[BaseStore, MutableMapping[str, Any]] def _path_to_prefix(path: Optional[str]) -> str: diff --git a/src/zarr/attrs.py b/src/zarr/attrs.py index 65f0423ecd..89cfefc22e 100644 --- a/src/zarr/attrs.py +++ b/src/zarr/attrs.py @@ -6,7 +6,7 @@ from zarr.util import json_dumps -class Attributes(MutableMapping): +class Attributes(MutableMapping[str, Any]): """Class providing access to user attributes on an array or group. Should not be instantiated directly, will be available via the `.attrs` property of an array or group. diff --git a/src/zarr/convenience.py b/src/zarr/convenience.py index 615a019dc3..b357c26c55 100644 --- a/src/zarr/convenience.py +++ b/src/zarr/convenience.py @@ -20,9 +20,9 @@ ) from zarr.util import TreeViewer, buffer_size, normalize_storage_path -from typing import Union +from typing import Any, Union -StoreLike = Union[BaseStore, MutableMapping, str, None] +StoreLike = Union[BaseStore, MutableMapping[str, Any], str, None] _builtin_open = open # builtin open is later shadowed by a local open function diff --git a/src/zarr/hierarchy.py b/src/zarr/hierarchy.py index e30d2d7996..9044c1681e 100644 --- a/src/zarr/hierarchy.py +++ b/src/zarr/hierarchy.py @@ -1,5 +1,6 @@ from collections.abc import MutableMapping from itertools import islice +from typing import Any import numpy as np @@ -48,7 +49,7 @@ ) -class Group(MutableMapping): +class Group(MutableMapping[str, Any]): """Instantiate a group from an initialized store. Parameters diff --git a/src/zarr/storage.py b/src/zarr/storage.py index a7bd22a6b9..b98cee99dd 100644 --- a/src/zarr/storage.py +++ b/src/zarr/storage.py @@ -99,7 +99,7 @@ Path = Union[str, bytes, None] # allow MutableMapping for backwards compatibility -StoreLike = Union[BaseStore, MutableMapping] +StoreLike = Union[BaseStore, MutableMapping[str, Any]] def contains_array(store: StoreLike, path: Path = None) -> bool: @@ -202,7 +202,7 @@ def listdir(store: BaseStore, path: Path = None): def _getsize(store: BaseStore, path: Path = None) -> int: # compute from size of values - if path and path in store: + if isinstance(path, str) and path in store: v = store[path] size = buffer_size(v) else: @@ -584,7 +584,7 @@ def _init_group_metadata( store[key] = encode_group_metadata(meta) -def _dict_store_keys(d: Dict, prefix="", cls=dict): +def _dict_store_keys(d: dict[str, Any], prefix="", cls=dict): for k in d.keys(): v = d[k] if isinstance(v, cls): diff --git a/src/zarr/util.py b/src/zarr/util.py index 35ecc64bba..0588e1a558 100644 --- a/src/zarr/util.py +++ b/src/zarr/util.py @@ -20,6 +20,7 @@ ) import numpy as np +import numpy.typing as npt from asciitree import BoxStyle, LeftAligned from asciitree.traversal import Traversal from numcodecs.compat import ( @@ -36,7 +37,7 @@ ValueType = TypeVar("ValueType") -def flatten(arg: Iterable) -> Iterable: +def flatten(arg: Iterable[Any]) -> Iterable[Any]: for element in arg: if isinstance(element, Iterable) and not isinstance(element, (str, bytes)): yield from flatten(element) @@ -179,7 +180,7 @@ def normalize_chunks(chunks: Any, shape: Tuple[int, ...], typesize: int) -> Tupl return chunks -def normalize_dtype(dtype: Union[str, np.dtype], object_codec) -> Tuple[np.dtype, Any]: +def normalize_dtype(dtype: Union[str, npt.DTypeLike], object_codec) -> Tuple[np.dtype[Any], Any]: # convenience API for object arrays if inspect.isclass(dtype): dtype = dtype.__name__ @@ -291,7 +292,7 @@ def normalize_dimension_separator(sep: Optional[str]) -> Optional[str]: raise ValueError("dimension_separator must be either '.' or '/', found: %r" % sep) -def normalize_fill_value(fill_value, dtype: np.dtype): +def normalize_fill_value(fill_value, dtype: np.dtype[Any]): if fill_value is None or dtype.hasobject: # no fill value pass @@ -668,7 +669,7 @@ def read_full(self): def retry_call( - callabl: Callable, + callabl: Callable[..., Any], args=None, kwargs=None, exceptions: Tuple[Any, ...] = (), diff --git a/src/zarr/v3/common.py b/src/zarr/v3/common.py index 1caf83a764..9f0a5fdb63 100644 --- a/src/zarr/v3/common.py +++ b/src/zarr/v3/common.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Union, Tuple, Iterable, Dict, List, TypeVar, overload +from typing import TYPE_CHECKING, Union, Tuple, Iterable, Dict, List, TypeVar, overload, Any import asyncio import contextvars from dataclasses import dataclass @@ -28,7 +28,7 @@ def product(tup: ChunkCoords) -> int: return functools.reduce(lambda x, y: x * y, tup, 1) -T = TypeVar("T", bound=Tuple) +T = TypeVar("T", bound=Tuple[Any, ...]) V = TypeVar("V") @@ -76,7 +76,7 @@ def parse_enum(data: JSON, cls: Type[E]) -> E: @dataclass(frozen=True) class ArraySpec: shape: ChunkCoords - dtype: np.dtype + dtype: np.dtype[Any] fill_value: Any def __init__(self, shape, dtype, fill_value): @@ -102,7 +102,7 @@ def parse_name(data: JSON, expected: Optional[str] = None) -> str: raise TypeError(f"Expected a string, got an instance of {type(data)}.") -def parse_configuration(data: JSON) -> dict: +def parse_configuration(data: JSON) -> JSON: if not isinstance(data, dict): raise TypeError(f"Expected dict, got {type(data)}") return data @@ -153,7 +153,7 @@ def parse_shapelike(data: Any) -> Tuple[int, ...]: return data_tuple -def parse_dtype(data: Any) -> np.dtype: +def parse_dtype(data: Any) -> np.dtype[Any]: # todo: real validation return np.dtype(data) diff --git a/src/zarr/v3/metadata.py b/src/zarr/v3/metadata.py index a5e8927311..573b8484f0 100644 --- a/src/zarr/v3/metadata.py +++ b/src/zarr/v3/metadata.py @@ -4,6 +4,7 @@ from dataclasses import dataclass, field import json import numpy as np +import numpy.typing as npt from zarr.v3.chunk_grids import ChunkGrid, RegularChunkGrid from zarr.v3.chunk_key_encodings import ChunkKeyEncoding, parse_separator @@ -90,7 +91,7 @@ def to_numpy_shortname(self) -> str: return data_type_to_numpy[self] @classmethod - def from_dtype(cls, dtype: np.dtype) -> DataType: + def from_dtype(cls, dtype: np.dtype[Any]) -> DataType: dtype_to_data_type = { "|b1": "bool", "bool": "bool", @@ -111,7 +112,7 @@ def from_dtype(cls, dtype: np.dtype) -> DataType: @dataclass(frozen=True) class ArrayMetadata(Metadata): shape: ChunkCoords - data_type: np.dtype + data_type: np.dtype[Any] chunk_grid: ChunkGrid chunk_key_encoding: ChunkKeyEncoding fill_value: Any @@ -176,7 +177,7 @@ def _validate_metadata(self) -> None: self.codecs.validate(self) @property - def dtype(self) -> np.dtype: + def dtype(self) -> np.dtype[Any]: return self.data_type @property @@ -238,7 +239,7 @@ def to_dict(self) -> Dict[str, Any]: class ArrayV2Metadata(Metadata): shape: ChunkCoords chunks: ChunkCoords - dtype: np.dtype + dtype: np.dtype[Any] fill_value: Union[None, int, float] = 0 order: Literal["C", "F"] = "C" filters: Optional[List[Dict[str, Any]]] = None @@ -251,7 +252,7 @@ def __init__( self, *, shape: ChunkCoords, - dtype: np.dtype, + dtype: npt.DTypeLike, chunks: ChunkCoords, fill_value: Any, order: Literal["C", "F"],