diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a75974f6c9..8b1f0dc88e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -201,3 +201,22 @@ jobs: run: exit 1 - name: Success run: echo Success! + + pylint: + name: Pylint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.*' + - name: Install Pylint + run: | + python -m pip install --upgrade pip + pip install pylint + - name: Analyse the code with Pylint + run: | + pylint . diff --git a/packages/zarr-metadata/tests/v3/data_type/test_dtype_names.py b/packages/zarr-metadata/tests/v3/data_type/test_dtype_names.py index 152c1b5fdc..243e9e6919 100644 --- a/packages/zarr-metadata/tests/v3/data_type/test_dtype_names.py +++ b/packages/zarr-metadata/tests/v3/data_type/test_dtype_names.py @@ -49,7 +49,7 @@ ] -@pytest.mark.parametrize(("name", "literal_type"), PRIMITIVE_DTYPES, ids=lambda x: str(x)) +@pytest.mark.parametrize(("name", "literal_type"), PRIMITIVE_DTYPES, ids=str) def test_primitive_against_literal(name: str, literal_type: object) -> None: """The dtype name validates against its declared Literal type.""" TypeAdapter(literal_type).validate_python(name) diff --git a/pyproject.toml b/pyproject.toml index 6a7238ff8f..b3e93b5799 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -501,3 +501,61 @@ ignore-words-list = "astroid" [project.entry-points.pytest11] zarr = "zarr.testing" + +[tool.pylint] +disable = [ + "E0203", # access-member-before-definition + "E0401", # import-error + "E0606", # possibly-used-before-assignment # FIXME + "E1101", # no-member + "E1123", # unexpected-keyword-arg # FIXME + "E1125", # missing-kwoa # FIXME + "E1136", # unsubscriptable-object + "W0101", # unreachable + "W0104", # pointless-statement + "W0105", # pointless-string-statement + "W0106", # expression-not-assigned + "W0201", # attribute-defined-outside-init # FIXME + "W0212", # protected-access + "W0223", # abstract-method # FIXME + "W0511", # fixme + "W0602", # global-variable-not-assigned + "W0603", # global-statement + "W0611", # unused-import + "W0613", # unused-argument + "W0621", # redefined-outer-name + "W0622", # redefined-builtin + "W0718", # broad-exception-caught + "W1510", # subprocess-run-check + "C0103", # invalid-name + "C0104", # disallowed-name + "C0105", # typevar-name-incorrect-variance + "C0114", # missing-module-docstring + "C0115", # missing-class-docstring + "C0116", # missing-function-docstring + "C0123", # unidiomatic-typecheck + "C0206", # consider-using-dict-items + "C0301", # line-too-long + "C0302", # too-many-lines + "C0411", # wrong-import-order + "C0412", # ungrouped-imports + "C0413", # wrong-import-position + "C0415", # import-outside-toplevel + "C1803", # use-implicit-booleaness-not-comparison + "R0124", # comparison-with-itself + "R0801", # duplicate-code + "R0902", # too-many-instance-attributes + "R0903", # too-few-public-methods + "R0904", # too-many-public-methods + "R0911", # too-many-return-statements + "R0912", # too-many-branches + "R0913", # too-many-arguments + "R0914", # too-many-locals + "R0915", # too-many-statements + "R0917", # too-many-positional-arguments + "R1702", # too-many-nested-blocks + "R1705", # no-else-return + "R1720", # no-else-raise + "R1728", # consider-using-generator + "R1732", # consider-using-with # FIXME +] diff --git a/src/zarr/abc/codec.py b/src/zarr/abc/codec.py index eed2119aff..6b1ef396e5 100644 --- a/src/zarr/abc/codec.py +++ b/src/zarr/abc/codec.py @@ -109,7 +109,6 @@ def compute_encoded_size(self, input_byte_length: int, chunk_spec: ArraySpec) -> ------- int """ - ... def resolve_metadata(self, chunk_spec: ArraySpec) -> ArraySpec: """Computed the spec of the chunk after it has been encoded by the codec. @@ -310,7 +309,6 @@ def evolve_from_array_spec(self, array_spec: ArraySpec) -> Self: ------- Self """ - ... @classmethod @abstractmethod @@ -325,7 +323,6 @@ def from_codecs(cls, codecs: Iterable[Codec]) -> Self: ------- Self """ - ... @classmethod def from_array_metadata_and_store(cls, array_metadata: ArrayMetadata, store: Store) -> Self: @@ -374,7 +371,6 @@ def validate( chunk_grid : ChunkGridMetadata The array chunk grid metadata """ - ... @abstractmethod def compute_encoded_size(self, byte_length: int, array_spec: ArraySpec) -> int: @@ -390,7 +386,6 @@ def compute_encoded_size(self, byte_length: int, array_spec: ArraySpec) -> int: ------- int """ - ... @abstractmethod async def decode( @@ -409,7 +404,6 @@ async def decode( ------- Iterable[NDBuffer | None] """ - ... @abstractmethod async def encode( @@ -428,7 +422,6 @@ async def encode( ------- Iterable[Buffer | None] """ - ... @abstractmethod async def read( @@ -460,7 +453,6 @@ async def read( tuple[GetResult, ...] One result per chunk in ``batch_info``. """ - ... @abstractmethod async def write( @@ -483,7 +475,6 @@ async def write( The chunk spec contains information about the chunk. value : NDBuffer """ - ... async def _batching_helper[CI: CodecInput, CO: CodecOutput]( diff --git a/src/zarr/abc/numcodec.py b/src/zarr/abc/numcodec.py index d60422209a..d72ad5323e 100644 --- a/src/zarr/abc/numcodec.py +++ b/src/zarr/abc/numcodec.py @@ -24,7 +24,6 @@ def encode(self, buf: Any) -> Any: enc: Any Encoded data. """ - ... def decode(self, buf: Any, out: Any | None = None) -> Any: """ @@ -43,14 +42,12 @@ def decode(self, buf: Any, out: Any | None = None) -> Any: dec : Any Decoded data. """ - ... def get_config(self) -> Any: """ Return a JSON-serializable configuration dictionary for this codec. Must include an ``'id'`` field with the codec identifier. """ - ... @classmethod def from_config(cls, config: Any) -> Self: @@ -62,7 +59,6 @@ def from_config(cls, config: Any) -> Self: config : Any A configuration dictionary for this codec. """ - ... def _is_numcodec_cls(obj: object) -> TypeGuard[type[Numcodec]]: diff --git a/src/zarr/abc/store.py b/src/zarr/abc/store.py index 7c187594df..a3676e9d3b 100644 --- a/src/zarr/abc/store.py +++ b/src/zarr/abc/store.py @@ -188,7 +188,6 @@ def _check_writable(self) -> None: @abstractmethod def __eq__(self, value: object) -> bool: """Equality comparison.""" - ... @abstractmethod async def get( @@ -214,7 +213,6 @@ async def get( ------- Buffer """ - ... @abstractmethod async def get_partial_values( @@ -235,7 +233,6 @@ async def get_partial_values( ------- list of values, in the order of the key_ranges, may contain null/none for missing keys """ - ... @abstractmethod async def exists(self, key: str) -> bool: @@ -249,13 +246,11 @@ async def exists(self, key: str) -> bool: ------- bool """ - ... @property @abstractmethod def supports_writes(self) -> bool: """Does the store support writes?""" - ... @abstractmethod async def set(self, key: str, value: Buffer) -> None: @@ -266,7 +261,6 @@ async def set(self, key: str, value: Buffer) -> None: key : str value : Buffer """ - ... async def set_if_not_exists(self, key: str, value: Buffer) -> None: """ @@ -306,7 +300,6 @@ def supports_consolidated_metadata(self) -> bool: @abstractmethod def supports_deletes(self) -> bool: """Does the store support deletes?""" - ... @abstractmethod async def delete(self, key: str) -> None: @@ -316,7 +309,6 @@ async def delete(self, key: str) -> None: ---------- key : str """ - ... @property def supports_partial_writes(self) -> Literal[False]: @@ -330,7 +322,6 @@ def supports_partial_writes(self) -> Literal[False]: @abstractmethod def supports_listing(self) -> bool: """Does the store support listing?""" - ... @abstractmethod def list(self) -> AsyncIterator[str]: diff --git a/src/zarr/codecs/cast_value.py b/src/zarr/codecs/cast_value.py index eb8a4de248..bd0f0437e7 100644 --- a/src/zarr/codecs/cast_value.py +++ b/src/zarr/codecs/cast_value.py @@ -244,7 +244,7 @@ def to_dict(self) -> dict[str, JSON]: json_map: dict[str, list[tuple[object, object]]] = {} for direction in ("encode", "decode"): if direction in self.scalar_map: - json_map[direction] = [(k, v) for k, v in self.scalar_map[direction].items()] + json_map[direction] = list(self.scalar_map[direction].items()) config["scalar_map"] = cast("JSON", json_map) return {"name": "cast_value", "configuration": config} diff --git a/src/zarr/codecs/numcodecs/_codecs.py b/src/zarr/codecs/numcodecs/_codecs.py index 1be1381a08..f8a5a3ba25 100644 --- a/src/zarr/codecs/numcodecs/_codecs.py +++ b/src/zarr/codecs/numcodecs/_codecs.py @@ -131,9 +131,6 @@ def __repr__(self) -> str: class _NumcodecsBytesBytesCodec(_NumcodecsCodec, BytesBytesCodec): - def __init__(self, **codec_config: JSON) -> None: - super().__init__(**codec_config) - async def _decode_single(self, chunk_data: Buffer, chunk_spec: ArraySpec) -> Buffer: return await asyncio.to_thread( as_numpy_array_wrapper, @@ -153,9 +150,6 @@ async def _encode_single(self, chunk_data: Buffer, chunk_spec: ArraySpec) -> Buf class _NumcodecsArrayArrayCodec(_NumcodecsCodec, ArrayArrayCodec): - def __init__(self, **codec_config: JSON) -> None: - super().__init__(**codec_config) - async def _decode_single(self, chunk_data: NDBuffer, chunk_spec: ArraySpec) -> NDBuffer: chunk_ndarray = chunk_data.as_ndarray_like() out = await asyncio.to_thread(self._codec.decode, chunk_ndarray) @@ -168,9 +162,6 @@ async def _encode_single(self, chunk_data: NDBuffer, chunk_spec: ArraySpec) -> N class _NumcodecsArrayBytesCodec(_NumcodecsCodec, ArrayBytesCodec): - def __init__(self, **codec_config: JSON) -> None: - super().__init__(**codec_config) - async def _decode_single(self, chunk_data: Buffer, chunk_spec: ArraySpec) -> NDBuffer: chunk_bytes = chunk_data.to_bytes() out = await asyncio.to_thread(self._codec.decode, chunk_bytes) @@ -247,9 +238,6 @@ def evolve_from_array_spec(self, array_spec: ArraySpec) -> FixedScaleOffset: class Quantize(_NumcodecsArrayArrayCodec, codec_name="quantize"): - def __init__(self, **codec_config: JSON) -> None: - super().__init__(**codec_config) - def evolve_from_array_spec(self, array_spec: ArraySpec) -> Quantize: if self.codec_config.get("dtype") is None: dtype = array_spec.dtype.to_native_dtype() diff --git a/src/zarr/core/buffer/core.py b/src/zarr/core/buffer/core.py index 497543f88f..60ad64514e 100644 --- a/src/zarr/core/buffer/core.py +++ b/src/zarr/core/buffer/core.py @@ -2,7 +2,6 @@ import sys from abc import ABC, abstractmethod -from collections.abc import Iterable from typing import ( TYPE_CHECKING, Any, @@ -259,7 +258,6 @@ def as_numpy_array(self) -> npt.NDArray[Any]: ------- NumPy array of this buffer (might be a data copy) """ - ... def as_buffer_like(self) -> BytesLike: """Returns the buffer as an object that implements the Python buffer protocol. @@ -302,7 +300,6 @@ def __len__(self) -> int: @abstractmethod def combine(self, others: Iterable[Buffer]) -> Self: """Concatenate many buffers""" - ... def __add__(self, other: Buffer) -> Self: """Concatenate two buffers""" @@ -479,7 +476,6 @@ def as_numpy_array(self) -> npt.NDArray[Any]: ------- NumPy array of this buffer (might be a data copy) """ - ... def as_scalar(self) -> ScalarType: """Returns the buffer as a scalar value""" diff --git a/src/zarr/core/buffer/cpu.py b/src/zarr/core/buffer/cpu.py index 8994281b58..e29aa5a57c 100644 --- a/src/zarr/core/buffer/cpu.py +++ b/src/zarr/core/buffer/cpu.py @@ -19,7 +19,6 @@ from collections.abc import Callable, Iterable from typing import Self - from zarr.core.buffer.core import ArrayLike, NDArrayLike from zarr.core.common import BytesLike @@ -37,16 +36,8 @@ class Buffer(core.Buffer): Notes ----- This buffer is untyped, so all indexing and sizes are in bytes. - - Parameters - ---------- - array_like - array-like object that must be 1-dim, contiguous, and byte dtype. """ - def __init__(self, array_like: ArrayLike) -> None: - super().__init__(array_like) - @classmethod def create_zero_length(cls) -> Self: return cls(np.array([], dtype="B")) @@ -134,16 +125,8 @@ class NDBuffer(core.NDBuffer): in order to use Python's type system to differentiate between the contiguous Buffer and the n-dim (non-contiguous) NDBuffer, we keep the definition of the two classes separate. - - Parameters - ---------- - array - ndarray-like object that is convertible to a regular Numpy array. """ - def __init__(self, array: NDArrayLike) -> None: - super().__init__(array) - @classmethod def create( cls, diff --git a/src/zarr/core/chunk_grids.py b/src/zarr/core/chunk_grids.py index 2cb9762775..488d7ac102 100644 --- a/src/zarr/core/chunk_grids.py +++ b/src/zarr/core/chunk_grids.py @@ -921,7 +921,7 @@ def resolve_outer_and_inner_chunks( max_bytes=target_shard_size_bytes, array_shape=array_shape, ) - if (has_auto_shard := (target_shard_size_bytes is not None)) + if (has_auto_shard := target_shard_size_bytes is not None) else 2 ) for a_shape, c_shape in zip(array_shape, chunk_shape_flat, strict=True): diff --git a/src/zarr/core/dtype/common.py b/src/zarr/core/dtype/common.py index 76d763d267..af4f0971cd 100644 --- a/src/zarr/core/dtype/common.py +++ b/src/zarr/core/dtype/common.py @@ -75,7 +75,7 @@ def check_structured_dtype_v2_inner(data: object) -> TypeGuard[StructuredName_V2 return False if len(data) != 2: return False - if not (isinstance(data[0], str)): + if not isinstance(data[0], str): return False if isinstance(data[-1], str): return True @@ -148,8 +148,6 @@ def unpack_dtype_json(data: DTypeSpec_V2 | DTypeSpec_V3) -> DTypeJSON: def __getattr__(name: str) -> object: if name == "DataTypeValidationError": - import warnings - from zarr.errors import DataTypeValidationError, ZarrDeprecationWarning warnings.warn( diff --git a/src/zarr/core/group.py b/src/zarr/core/group.py index 52eaa3e144..1c30cf2eb8 100644 --- a/src/zarr/core/group.py +++ b/src/zarr/core/group.py @@ -2936,11 +2936,13 @@ async def create_hierarchy( nodes_parsed = _parse_hierarchy_dict(data=nodes_normed_keys) redundant_implicit_groups = [] - # empty hierarchies should be a no-op - if len(nodes_parsed) > 0: + try: # figure out which zarr format we are using zarr_format = next(iter(nodes_parsed.values())).zarr_format - + except StopIteration: + # empty hierarchies should be a no-op + pass + else: # check which implicit groups will require materialization implicit_group_keys = tuple( filter(lambda k: isinstance(nodes_parsed[k], ImplicitGroupMarker), nodes_parsed) diff --git a/src/zarr/core/metadata/v2.py b/src/zarr/core/metadata/v2.py index ac32521239..f95093a657 100644 --- a/src/zarr/core/metadata/v2.py +++ b/src/zarr/core/metadata/v2.py @@ -287,11 +287,10 @@ def parse_filters(data: object) -> tuple[Numcodec, ...] | None: """ Parse a potential tuple of filters """ - out: list[Numcodec] = [] - if data is None: return data if isinstance(data, Iterable): + out: list[Numcodec] = [] for idx, val in enumerate(data): if _is_numcodec(val): out.append(val) diff --git a/src/zarr/storage/_memory.py b/src/zarr/storage/_memory.py index e867706155..3efa4dbb62 100644 --- a/src/zarr/storage/_memory.py +++ b/src/zarr/storage/_memory.py @@ -459,6 +459,7 @@ class ManagedMemoryStore(MemoryStore): path: str def __init__(self, name: str | None = None, *, path: str = "", read_only: bool = False) -> None: + # pylint: disable=super-init-not-called # Skip MemoryStore.__init__ and call Store.__init__ directly # because we manage _store_dict via the registry, not via a user-supplied # MutableMapping. If MemoryStore.__init__ ever adds logic beyond setting @@ -499,7 +500,7 @@ def _from_managed_dict( ) -> ManagedMemoryStore: """Internal: create a store from an existing managed dict.""" store = object.__new__(cls) - Store.__init__(store, read_only=read_only) + Store.__init__(store, read_only=read_only) # pylint: disable=unnecessary-dunder-call store._store_dict = managed_dict store._name = name store.path = normalize_path(path) diff --git a/src/zarr/storage/_zip.py b/src/zarr/storage/_zip.py index 430b0c3e2a..e0341e8117 100644 --- a/src/zarr/storage/_zip.py +++ b/src/zarr/storage/_zip.py @@ -84,6 +84,7 @@ def __init__( assert isinstance(path, Path) self.path = path # root? + self._is_open = False self._zmode = mode self.compression = compression self.allowZip64 = allowZip64 @@ -125,6 +126,7 @@ def close(self) -> None: super().close() with self._lock: self._zf.close() + self._is_open = False async def clear(self) -> None: # docstring inherited diff --git a/src/zarr/testing/stateful.py b/src/zarr/testing/stateful.py index 9105b8234e..e161255da9 100644 --- a/src/zarr/testing/stateful.py +++ b/src/zarr/testing/stateful.py @@ -183,7 +183,7 @@ def add_array(self, data: DataObject, name: str) -> None: @with_frequency(0.25) def clear(self) -> None: note("clearing") - import zarr + import zarr # pylint: disable=reimported self._sync(self.store.clear()) self._sync(self.model.clear()) diff --git a/src/zarr/testing/store.py b/src/zarr/testing/store.py index fb87a69a09..521862660a 100644 --- a/src/zarr/testing/store.py +++ b/src/zarr/testing/store.py @@ -64,7 +64,6 @@ async def set(self, store: S, key: str, value: Buffer) -> None: This should not use any store methods. Bypassing the store methods allows them to be tested. """ - ... @abstractmethod async def get(self, store: S, key: str) -> Buffer: @@ -73,13 +72,11 @@ async def get(self, store: S, key: str) -> Buffer: This should not use any store methods. Bypassing the store methods allows them to be tested. """ - ... @abstractmethod @pytest.fixture def store_kwargs(self, *args: Any, **kwargs: Any) -> dict[str, Any]: """Kwargs for instantiating a store""" - ... @abstractmethod def test_store_repr(self, store: S) -> None: ... diff --git a/src/zarr/testing/strategies.py b/src/zarr/testing/strategies.py index f2c83677cc..0524abbc45 100644 --- a/src/zarr/testing/strategies.py +++ b/src/zarr/testing/strategies.py @@ -315,7 +315,7 @@ def arrays( else: chunks_param = draw(chunk_shapes(shape=nparray.shape), label="chunk shape") - if all(s > c and c > 1 for s, c in zip(nparray.shape, chunks_param, strict=True)): + if all(s > c > 1 for s, c in zip(nparray.shape, chunks_param, strict=True)): shard_shape = draw( st.none() | shard_shapes(shape=nparray.shape, chunk_shape=chunks_param), label="shard shape", diff --git a/tests/test_array.py b/tests/test_array.py index 0d6d2d5906..3d5d047c87 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -2311,7 +2311,7 @@ def test_with_config(config: ArrayConfigParams) -> None: new_async_array_config_dict = source_array._async_array.with_config(config).config.to_dict() new_array_config_dict = source_array.with_config(config).config.to_dict() - for key in source_config: + for key in source_config: # pylint: disable=consider-using-dict-items if key in config: assert new_async_array_config_dict[key] == config[key] # type: ignore[literal-required] assert new_array_config_dict[key] == config[key] # type: ignore[literal-required] diff --git a/tests/test_codecs/test_blosc.py b/tests/test_codecs/test_blosc.py index f5f13f4d05..2813345293 100644 --- a/tests/test_codecs/test_blosc.py +++ b/tests/test_codecs/test_blosc.py @@ -224,8 +224,12 @@ def test_blosc_enum_classes_import_silently() -> None: """ with warnings.catch_warnings(): warnings.simplefilter("error") - from zarr.codecs.blosc import BloscCname as _BloscCname # noqa: F401 - from zarr.codecs.blosc import BloscShuffle as _BloscShuffle # noqa: F401 + from zarr.codecs.blosc import ( # pylint: disable=reimported + BloscCname as _BloscCname, # noqa: F401 + ) + from zarr.codecs.blosc import ( # pylint: disable=reimported + BloscShuffle as _BloscShuffle, # noqa: F401 + ) def test_blosc_codec_init_with_enum_instance_warns() -> None: diff --git a/tests/test_codecs/test_bytes.py b/tests/test_codecs/test_bytes.py index 03dd0b40c6..4641cf28d3 100644 --- a/tests/test_codecs/test_bytes.py +++ b/tests/test_codecs/test_bytes.py @@ -183,7 +183,7 @@ def test_endian_class_imports_silently() -> None: """ with warnings.catch_warnings(): warnings.simplefilter("error") - from zarr.codecs.bytes import Endian as _Endian # noqa: F401 + from zarr.codecs.bytes import Endian as _Endian # noqa: F401 # pylint: disable=reimported def test_bytes_codec_init_with_enum_instance_warns() -> None: diff --git a/tests/test_codecs/test_cast_value.py b/tests/test_codecs/test_cast_value.py index c43edb76e8..9776779b86 100644 --- a/tests/test_codecs/test_cast_value.py +++ b/tests/test_codecs/test_cast_value.py @@ -235,8 +235,6 @@ def test_encode_decode_roundtrip( case: Expect[tuple[str, str], np.ndarray[Any, np.dtype[Any]]], ) -> None: """Small integer data survives encode → decode for each dtype pair.""" - import zarr - source_dtype, target_dtype = case.input arr = zarr.create_array( store={}, @@ -267,8 +265,6 @@ def test_float_to_int_rounding( case: Expect[np.ndarray[Any, np.dtype[Any]], np.ndarray[Any, np.dtype[Any]]], ) -> None: """Fractional float values are truncated towards zero when cast to int32.""" - import zarr - arr = zarr.create_array( store={}, shape=case.input.shape, @@ -298,8 +294,6 @@ def test_out_of_range_clamp( case: Expect[np.ndarray[Any, np.dtype[Any]], np.ndarray[Any, np.dtype[Any]]], ) -> None: """Values outside the int8 range are clamped to [-128, 127].""" - import zarr - arr = zarr.create_array( store={}, shape=case.input.shape, @@ -334,8 +328,6 @@ def test_compute_encoded_size() -> None: @requires_cast_value_rs def test_scalar_map_encode_decode_roundtrip() -> None: """Scalar map entries are applied during encode and decode.""" - import zarr - data = np.array([1.0, float("nan"), 3.0], dtype="float64") arr = zarr.create_array( store={}, @@ -408,8 +400,6 @@ def test_scalar_map_encode_decode_roundtrip() -> None: ) def test_scalar_map_validation_rejects_invalid(case: ExpectFail[dict[str, Any]]) -> None: """Invalid scalar_map entries are rejected at array creation.""" - import zarr - with case.raises(): zarr.create_array( store={}, @@ -431,7 +421,6 @@ def test_scalar_map_validation_rejects_invalid(case: ExpectFail[dict[str, Any]]) @requires_cast_value_rs def test_combined_with_scale_offset() -> None: """scale_offset followed by cast_value compresses float64 into int16 and round-trips.""" - import zarr from zarr.codecs.scale_offset import ScaleOffset arr = zarr.create_array( diff --git a/tests/test_codecs/test_scale_offset.py b/tests/test_codecs/test_scale_offset.py index 513caf463a..a392285f56 100644 --- a/tests/test_codecs/test_scale_offset.py +++ b/tests/test_codecs/test_scale_offset.py @@ -170,8 +170,6 @@ def test_fill_value_transformed() -> None: def test_identity_is_noop() -> None: """Default codec (offset=0, scale=1) is a no-op.""" - import zarr - arr = zarr.create_array( store={}, shape=(50,), diff --git a/tests/test_codecs/test_sharding.py b/tests/test_codecs/test_sharding.py index e62334f5d3..504ea0354f 100644 --- a/tests/test_codecs/test_sharding.py +++ b/tests/test_codecs/test_sharding.py @@ -789,8 +789,6 @@ def test_sharding_mixed_integer_list_indexing(store: Store) -> None: Mixed integer/list indexing on sharded arrays should return the same shape and data as on equivalent chunked arrays. """ - import numpy as np - data = np.arange(200 * 100 * 10, dtype=np.uint8).reshape(200, 100, 10) chunked = zarr.create_array( @@ -984,7 +982,7 @@ def test_sharding_index_location_class_imports_silently() -> None: """ with warnings.catch_warnings(): warnings.simplefilter("error") - from zarr.codecs.sharding import ( # noqa: F401 + from zarr.codecs.sharding import ( # noqa: F401 # pylint: disable=reimported ShardingCodecIndexLocation as _SCIL, ) diff --git a/tests/test_store/test_fsspec.py b/tests/test_store/test_fsspec.py index 9efee40d7a..23e6b262de 100644 --- a/tests/test_store/test_fsspec.py +++ b/tests/test_store/test_fsspec.py @@ -409,8 +409,6 @@ async def test_fsspec_store_open_group_via_reference_filesystem() -> None: ``GroupNotFoundError``. This test pins ``path="/"`` explicitly to keep coverage even if the default value changes later. """ - import json - from fsspec.implementations.reference import ReferenceFileSystem group_json = json.dumps({"zarr_format": 3, "node_type": "group", "attributes": {}}) @@ -434,9 +432,6 @@ async def test_fsspec_store_read_array_chunk_via_reference_filesystem() -> None: fetch path (used by kerchunk-style virtualization) would surface here rather than at metadata-open time. """ - import json - - import numpy as np from fsspec.implementations.reference import ReferenceFileSystem # Construct a minimal v3 zarr: a single 1-D uint8 array of length 4 with diff --git a/tests/test_unified_chunk_grid.py b/tests/test_unified_chunk_grid.py index 0df5a5d9fd..51f2b5f824 100644 --- a/tests/test_unified_chunk_grid.py +++ b/tests/test_unified_chunk_grid.py @@ -952,11 +952,7 @@ def test_e2e_chunk_grid_serializes( tmp_path: Path, shape: tuple[int, ...], chunks: Any, grid_type_name: str, grid_name: str ) -> None: """Array metadata serializes chunk_grid with the correct type and name""" - from zarr.core.metadata.v3 import ( - ArrayV3Metadata, - RectilinearChunkGridMetadata, - RegularChunkGridMetadata, - ) + from zarr.core.metadata.v3 import ArrayV3Metadata grid_type = ( RegularChunkGridMetadata @@ -979,7 +975,7 @@ def test_e2e_chunk_grid_serializes( def test_e2e_chunk_grid_name_roundtrip_preserves_rectilinear(tmp_path: Path) -> None: """A rectilinear grid with uniform edges stays 'rectilinear' through to_dict/from_dict.""" - from zarr.core.metadata.v3 import ArrayV3Metadata, RectilinearChunkGridMetadata + from zarr.core.metadata.v3 import ArrayV3Metadata meta_dict: dict[str, Any] = { "zarr_format": 3, @@ -1004,7 +1000,7 @@ def test_e2e_chunk_grid_name_roundtrip_preserves_rectilinear(tmp_path: Path) -> def test_e2e_chunk_grid_name_regular_from_dict(tmp_path: Path) -> None: """A 'regular' chunk grid name is preserved through from_dict.""" - from zarr.core.metadata.v3 import ArrayV3Metadata, RegularChunkGridMetadata + from zarr.core.metadata.v3 import ArrayV3Metadata meta_dict: dict[str, Any] = { "zarr_format": 3, @@ -1036,7 +1032,6 @@ def test_sharding_accepts_rectilinear_outer_grid() -> None: """ShardingCodec.validate should not reject rectilinear outer grids.""" from zarr.codecs.sharding import ShardingCodec from zarr.core.dtype import Float32 - from zarr.core.metadata.v3 import RectilinearChunkGridMetadata codec = ShardingCodec(chunk_shape=(5, 5)) grid_meta = RectilinearChunkGridMetadata(chunk_shapes=((10, 20, 30), (50, 50))) @@ -1052,7 +1047,6 @@ def test_sharding_rejects_non_divisible_rectilinear() -> None: """Rectilinear shard sizes not divisible by inner chunk_shape should raise.""" from zarr.codecs.sharding import ShardingCodec from zarr.core.dtype import Float32 - from zarr.core.metadata.v3 import RectilinearChunkGridMetadata codec = ShardingCodec(chunk_shape=(5, 5)) grid_meta = RectilinearChunkGridMetadata(chunk_shapes=((10, 20, 17), (50, 50))) @@ -1069,7 +1063,6 @@ def test_sharding_accepts_divisible_rectilinear() -> None: """Rectilinear shard sizes all divisible by inner chunk_shape should pass.""" from zarr.codecs.sharding import ShardingCodec from zarr.core.dtype import Float32 - from zarr.core.metadata.v3 import RectilinearChunkGridMetadata codec = ShardingCodec(chunk_shape=(5, 5)) grid_meta = RectilinearChunkGridMetadata(chunk_shapes=((10, 20, 30), (50, 50))) @@ -1085,7 +1078,6 @@ def test_sharding_rejects_non_divisible_among_repeated_edges() -> None: """Shard validation catches a non-divisible edge even among many repeated valid ones.""" from zarr.codecs.sharding import ShardingCodec from zarr.core.dtype import Float32 - from zarr.core.metadata.v3 import RectilinearChunkGridMetadata # edges (10, 10, 7) — 7 is not divisible by 5 codec = ShardingCodec(chunk_shape=(5,)) @@ -1098,7 +1090,6 @@ def test_sharding_accepts_all_repeated_divisible_edges() -> None: """Shard validation passes when all distinct edges are divisible by inner chunk size.""" from zarr.codecs.sharding import ShardingCodec from zarr.core.dtype import Float32 - from zarr.core.metadata.v3 import RectilinearChunkGridMetadata # edges (10, 10, 20, 10) — unique values {10, 20}, both divisible by 5 codec = ShardingCodec(chunk_shape=(5,)) @@ -2658,7 +2649,7 @@ def test_nchunks_rectilinear( shape: tuple[int, ...], chunks: list[list[int]], expected: int ) -> None: """Array.nchunks reports correct total chunk count for rectilinear arrays""" - store = MemoryStore() + store = zarr.storage.MemoryStore() a = zarr.create_array(store, shape=shape, chunks=chunks, dtype="int32") assert a.nchunks == expected @@ -2672,7 +2663,7 @@ def test_iter_chunk_regions_rectilinear() -> None: """_iter_chunk_regions should work for rectilinear arrays.""" from zarr.core.array import _iter_chunk_regions - store = MemoryStore() + store = zarr.storage.MemoryStore() a = zarr.create_array(store, shape=(30,), chunks=[[10, 20]], dtype="int32") regions = list(_iter_chunk_regions(a)) assert len(regions) == 2 @@ -2818,15 +2809,13 @@ def rectilinear_chunks_st(draw: st.DrawFn, *, shape: tuple[int, ...]) -> list[li @st.composite def rectilinear_arrays_st(draw: st.DrawFn) -> tuple[zarr.Array[Any], np.ndarray[Any, Any]]: """Generate a rectilinear zarr array with random data, shape, and chunks.""" - from zarr.storage import MemoryStore - ndim = draw(st.integers(min_value=1, max_value=3)) shape = draw(st.tuples(*[st.integers(min_value=2, max_value=20) for _ in range(ndim)])) chunk_shapes = draw(rectilinear_chunks_st(shape=shape)) event(f"ndim={ndim}, shape={shape}") a = np.arange(int(np.prod(shape)), dtype="int32").reshape(shape) - store = MemoryStore() + store = zarr.storage.MemoryStore() z = zarr.create_array(store=store, shape=shape, chunks=chunk_shapes, dtype="int32") z[:] = a return z, a diff --git a/tests/test_v2.py b/tests/test_v2.py index 3a063ac509..4b70580e0a 100644 --- a/tests/test_v2.py +++ b/tests/test_v2.py @@ -145,13 +145,13 @@ def test_create_array_defaults(store: Store) -> None: assert isinstance(g, Group) arr = g.create_array("one", dtype="i8", shape=(1,), chunks=(1,), compressor=None) assert arr.async_array.compressor is None - assert not (arr.filters) + assert not arr.filters arr = g.create_array("two", dtype="i8", shape=(1,), chunks=(1,)) assert arr.async_array.compressor is not None - assert not (arr.filters) + assert not arr.filters arr = g.create_array("three", dtype="i8", shape=(1,), chunks=(1,), compressor=Zstd()) assert arr.async_array.compressor is not None - assert not (arr.filters) + assert not arr.filters with pytest.raises(ValueError): g.create_array( "four", dtype="i8", shape=(1,), chunks=(1,), compressor=None, compressors=None