diff --git a/pyproject.toml b/pyproject.toml index 3f46a80..0f34c91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -127,8 +127,7 @@ markers = [ [tool.ruff] target-version = "py39" -include = ["src/numpydantic/**/*.py", "pyproject.toml"] -exclude = ["tests"] +include = ["src/numpydantic/**/*.py", "tests/**/*.py", "pyproject.toml"] [tool.ruff.lint] select = [ @@ -177,6 +176,10 @@ ignore = [ fixable = ["ALL"] +[tool.ruff.lint.per-file-ignores] +"src/numpydantic/testing/*" = ["D", "F722"] +"tests/*" = ["D", "F403", "F722", "ANN", ] + [tool.mypy] plugins = [ "pydantic.mypy" diff --git a/src/numpydantic/testing/cases.py b/src/numpydantic/testing/cases.py index dd9274d..26f1227 100644 --- a/src/numpydantic/testing/cases.py +++ b/src/numpydantic/testing/cases.py @@ -18,7 +18,6 @@ else: YES_PIPE = False - class BasicModel(BaseModel): x: int @@ -129,4 +128,3 @@ if YES_PIPE: "union-pipe-str", ] ) - diff --git a/src/numpydantic/testing/helpers.py b/src/numpydantic/testing/helpers.py index 4b196d0..3d0ae56 100644 --- a/src/numpydantic/testing/helpers.py +++ b/src/numpydantic/testing/helpers.py @@ -1,5 +1,6 @@ from typing import Any, Tuple, Type, Union +import numpy as np from pydantic import BaseModel, ConfigDict, computed_field from numpydantic import NDArray, Shape diff --git a/tests/conftest.py b/tests/conftest.py index cea8ea6..9e6a39a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,10 @@ -import sys - -from numpydantic.testing.cases import YES_PIPE, RGB_UNION, UNION_PIPE, DTYPE_CASES, DTYPE_IDS +import pytest +from numpydantic.testing.cases import ( + DTYPE_CASES, + DTYPE_IDS, + RGB_UNION, +) from numpydantic.testing.helpers import ValidationCase from tests.fixtures import * @@ -14,9 +17,6 @@ def pytest_addoption(parser): ) - - - @pytest.fixture( scope="module", params=[ diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py index d7efce2..f080c4f 100644 --- a/tests/fixtures/__init__.py +++ b/tests/fixtures/__init__.py @@ -1,3 +1,3 @@ -from .paths import * from .generation import * -from .models import * \ No newline at end of file +from .models import * +from .paths import * diff --git a/tests/fixtures/generation.py b/tests/fixtures/generation.py index a6dd6f6..05f68b0 100644 --- a/tests/fixtures/generation.py +++ b/tests/fixtures/generation.py @@ -24,15 +24,16 @@ def hdf5_array( compound: bool = False, ) -> H5ArrayPath: array_path = "/" + "_".join([str(s) for s in shape]) + "__" + dtype.__name__ - + generator = np.random.default_rng() + if not compound: if dtype is str: - data = np.random.random(shape).astype(bytes) + data = generator.random(shape).astype(bytes) elif dtype is datetime: data = np.empty(shape, dtype="S32") data.fill(datetime.now(timezone.utc).isoformat().encode("utf-8")) else: - data = np.random.random(shape).astype(dtype) + data = generator.random(shape).astype(dtype) h5path = H5ArrayPath(hdf5_file, array_path) else: @@ -64,7 +65,7 @@ def zarr_nested_array(tmp_output_dir_func) -> ZarrArrayPath: file = tmp_output_dir_func / "nested.zarr" path = "a/b/c" root = zarr.open(str(file), mode="w") - array = root.zeros(path, shape=(100, 100), chunks=(10, 10)) + _ = root.zeros(path, shape=(100, 100), chunks=(10, 10)) return ZarrArrayPath(file=file, path=path) diff --git a/tests/fixtures/models.py b/tests/fixtures/models.py index 088c661..08f1ac3 100644 --- a/tests/fixtures/models.py +++ b/tests/fixtures/models.py @@ -1,4 +1,4 @@ -from typing import Callable, Tuple, Union, Type, Optional, Any +from typing import Any, Callable, Optional, Tuple, Type, Union import numpy as np import pytest diff --git a/tests/fixtures/paths.py b/tests/fixtures/paths.py index c3cab21..2a6b133 100644 --- a/tests/fixtures/paths.py +++ b/tests/fixtures/paths.py @@ -20,7 +20,8 @@ def tmp_output_dir(request: pytest.FixtureRequest) -> Path: except PermissionError as e: # sporadic error on windows machines... warn( - f"Temporary directory could not be removed due to a permissions error: \n{str(e)}" + "Temporary directory could not be removed due to a permissions error: " + f"\n{str(e)}" ) diff --git a/tests/test_interface/conftest.py b/tests/test_interface/conftest.py index 5d36fa5..3917ae9 100644 --- a/tests/test_interface/conftest.py +++ b/tests/test_interface/conftest.py @@ -1,12 +1,12 @@ -import pytest - from typing import Callable, Tuple, Type -import numpy as np + import dask.array as da +import numpy as np +import pytest import zarr from pydantic import BaseModel -from numpydantic import interface, NDArray +from numpydantic import NDArray, interface @pytest.fixture( diff --git a/tests/test_interface/test_dask.py b/tests/test_interface/test_dask.py index 919455f..a7c17d4 100644 --- a/tests/test_interface/test_dask.py +++ b/tests/test_interface/test_dask.py @@ -1,12 +1,11 @@ -import pytest import json import dask.array as da +import pytest from pydantic import BaseModel, ValidationError -from numpydantic.interface import DaskInterface from numpydantic.exceptions import DtypeError, ShapeError - +from numpydantic.interface import DaskInterface from numpydantic.testing.helpers import ValidationCase pytestmark = pytest.mark.dask diff --git a/tests/test_interface/test_hdf5.py b/tests/test_interface/test_hdf5.py index 361eb90..9dec0b5 100644 --- a/tests/test_interface/test_hdf5.py +++ b/tests/test_interface/test_hdf5.py @@ -1,17 +1,16 @@ import json -from datetime import datetime, timezone +from datetime import datetime from typing import Any import h5py +import numpy as np import pytest from pydantic import BaseModel, ValidationError -import numpy as np from numpydantic import NDArray, Shape +from numpydantic.exceptions import DtypeError, ShapeError from numpydantic.interface import H5Interface from numpydantic.interface.hdf5 import H5ArrayPath, H5Proxy -from numpydantic.exceptions import DtypeError, ShapeError - from numpydantic.testing.helpers import ValidationCase pytestmark = pytest.mark.hdf5 @@ -221,10 +220,7 @@ def test_empty_dataset(dtype, tmp_path): Empty datasets shouldn't choke us during validation """ array_path = tmp_path / "test.h5" - if dtype in (str, datetime): - np_dtype = "S32" - else: - np_dtype = dtype + np_dtype = "S32" if dtype in (str, datetime) else dtype with h5py.File(array_path, "w") as h5f: _ = h5f.create_dataset(name="/data", dtype=np_dtype) diff --git a/tests/test_interface/test_interface_base.py b/tests/test_interface/test_interface_base.py index 0b99ae6..1aa17e1 100644 --- a/tests/test_interface/test_interface_base.py +++ b/tests/test_interface/test_interface_base.py @@ -6,18 +6,17 @@ for tests that should apply to all interfaces, use ``test_interfaces.py`` import gc from typing import Literal -import pytest import numpy as np +import pytest +from pydantic import ValidationError from numpydantic.interface import ( Interface, - JsonDict, InterfaceMark, - NumpyInterface, + JsonDict, MarkedJson, + NumpyInterface, ) -from pydantic import ValidationError - from numpydantic.interface.interface import V @@ -46,9 +45,7 @@ def interfaces(): @classmethod def check(cls, array): cls.checked = True - if isinstance(array, list): - return True - return False + return isinstance(array, list) @classmethod def enabled(cls) -> bool: @@ -94,7 +91,8 @@ def interfaces(): def test_interface_match_error(interfaces): """ - Test that `match` and `match_output` raises errors when no or multiple matches are found + Test that `match` and `match_output` raises errors when no or multiple matches + are found """ with pytest.raises(ValueError) as e: Interface.match([1, 2, 3]) diff --git a/tests/test_interface/test_interfaces.py b/tests/test_interface/test_interfaces.py index faec0d8..f6efc16 100644 --- a/tests/test_interface/test_interfaces.py +++ b/tests/test_interface/test_interfaces.py @@ -2,15 +2,15 @@ Tests that should be applied to all interfaces """ -import pytest -from typing import Callable -from importlib.metadata import version import json +from importlib.metadata import version +from typing import Callable -import numpy as np import dask.array as da -from zarr.core import Array as ZarrArray +import numpy as np +import pytest from pydantic import BaseModel +from zarr.core import Array as ZarrArray from numpydantic.interface import Interface, InterfaceMark, MarkedJson diff --git a/tests/test_interface/test_numpy.py b/tests/test_interface/test_numpy.py index 17bfe9b..e93bffb 100644 --- a/tests/test_interface/test_numpy.py +++ b/tests/test_interface/test_numpy.py @@ -1,8 +1,8 @@ import numpy as np import pytest -from pydantic import ValidationError, BaseModel -from numpydantic.exceptions import DtypeError, ShapeError +from pydantic import BaseModel, ValidationError +from numpydantic.exceptions import DtypeError, ShapeError from numpydantic.testing.helpers import ValidationCase pytestmark = pytest.mark.numpy diff --git a/tests/test_interface/test_video.py b/tests/test_interface/test_video.py index 5f03a57..a99300c 100644 --- a/tests/test_interface/test_video.py +++ b/tests/test_interface/test_video.py @@ -2,12 +2,10 @@ Needs to be refactored to DRY, but works for now """ -import numpy as np -import pytest - from pathlib import Path -import cv2 +import cv2 +import pytest from pydantic import BaseModel, ValidationError from numpydantic import NDArray, Shape @@ -65,7 +63,7 @@ def test_video_wrong_shape(avi_video): # should correctly validate :) with pytest.raises(ValidationError): - instance = MyModel(array=vid) + _ = MyModel(array=vid) @pytest.mark.proxy diff --git a/tests/test_interface/test_zarr.py b/tests/test_interface/test_zarr.py index 2fa086c..af0d7e9 100644 --- a/tests/test_interface/test_zarr.py +++ b/tests/test_interface/test_zarr.py @@ -2,14 +2,11 @@ import json import pytest import zarr - from pydantic import BaseModel, ValidationError -from numcodecs import Pickle +from numpydantic.exceptions import DtypeError, ShapeError from numpydantic.interface import ZarrInterface from numpydantic.interface.zarr import ZarrArrayPath -from numpydantic.exceptions import DtypeError, ShapeError - from numpydantic.testing.helpers import ValidationCase pytestmark = pytest.mark.zarr @@ -36,7 +33,7 @@ def nested_dir_array(tmp_output_dir_func) -> zarr.NestedDirectoryStore: def _zarr_array(case: ValidationCase, store) -> zarr.core.Array: if issubclass(case.dtype, BaseModel): pytest.skip( - f"Zarr can't handle objects properly at the moment, " + "Zarr can't handle objects properly at the moment, " "see https://github.com/zarr-developers/zarr-python/issues/2081" ) # return zarr.full( @@ -103,14 +100,14 @@ def test_zarr_from_tuple(array, model_blank, request): """Should be able to do the same validation logic from tuples as an input""" array = request.getfixturevalue(array) if isinstance(array, ZarrArrayPath): - instance = model_blank(array=(array.file, array.path)) + _ = model_blank(array=(array.file, array.path)) else: - instance = model_blank(array=(array,)) + _ = model_blank(array=(array,)) def test_zarr_from_path(zarr_array, model_blank): """Should be able to just pass a path""" - instance = model_blank(array=zarr_array) + _ = model_blank(array=zarr_array) def test_zarr_array_path_from_iterable(zarr_array): diff --git a/tests/test_meta.py b/tests/test_meta.py index c137633..c5d2a51 100644 --- a/tests/test_meta.py +++ b/tests/test_meta.py @@ -1,4 +1,5 @@ import sys + import pytest from numpydantic import NDArray @@ -40,4 +41,4 @@ def test_stub_revealed_type(): """ Check that the revealed type matches the stub """ - type = reveal_type(NDArray) + _ = reveal_type(NDArray) diff --git a/tests/test_ndarray.py b/tests/test_ndarray.py index cda092c..4282279 100644 --- a/tests/test_ndarray.py +++ b/tests/test_ndarray.py @@ -1,16 +1,13 @@ -import pytest - -from typing import Union, Optional, Any import json +from typing import Any, Optional, Union import numpy as np -from pydantic import BaseModel, ValidationError, Field +import pytest +from pydantic import BaseModel, Field, ValidationError - -from numpydantic import NDArray, Shape -from numpydantic.exceptions import ShapeError, DtypeError -from numpydantic import dtype +from numpydantic import NDArray, Shape, dtype from numpydantic.dtype import Number +from numpydantic.exceptions import DtypeError @pytest.mark.json_schema @@ -28,15 +25,15 @@ def test_ndarray_type(): assert schema["properties"]["array"]["minItems"] == 2 # models should instantiate correctly! - instance = Model(array=np.zeros((2, 3))) + _ = Model(array=np.zeros((2, 3))) with pytest.raises(ValidationError): - instance = Model(array=np.zeros((4, 6))) + _ = Model(array=np.zeros((4, 6))) with pytest.raises(ValidationError): - instance = Model(array=np.ones((2, 3), dtype=bool)) + _ = Model(array=np.ones((2, 3), dtype=bool)) - instance = Model(array=np.zeros((2, 3)), array_any=np.ones((3, 4, 5))) + _ = Model(array=np.zeros((2, 3)), array_any=np.ones((3, 4, 5))) @pytest.mark.dtype @@ -93,6 +90,7 @@ def test_schema_number(): def test_ndarray_union(): + generator = np.random.default_rng() class Model(BaseModel): array: Optional[ Union[ @@ -102,22 +100,22 @@ def test_ndarray_union(): ] ] = Field(None) - instance = Model() - instance = Model(array=np.random.random((5, 10))) - instance = Model(array=np.random.random((5, 10, 3))) - instance = Model(array=np.random.random((5, 10, 3, 4))) + _ = Model() + _ = Model(array=generator.random((5, 10))) + _ = Model(array=generator.random((5, 10, 3))) + _ = Model(array=generator.random((5, 10, 3, 4))) with pytest.raises(ValidationError): - instance = Model(array=np.random.random((5,))) + _ = Model(array=generator.random((5,))) with pytest.raises(ValidationError): - instance = Model(array=np.random.random((5, 10, 4))) + _ = Model(array=generator.random((5, 10, 4))) with pytest.raises(ValidationError): - instance = Model(array=np.random.random((5, 10, 3, 6))) + _ = Model(array=generator.random((5, 10, 3, 6))) with pytest.raises(ValidationError): - instance = Model(array=np.random.random((5, 10, 4, 6))) + _ = Model(array=generator.random((5, 10, 4, 6))) @pytest.mark.shape @@ -127,15 +125,16 @@ def test_ndarray_unparameterized(dtype): """ NDArray without any parameters is any shape, any type """ + generator = np.random.default_rng() class Model(BaseModel): array: NDArray # not very sophisticated fuzzing of "any shape" test_cases = 10 - for i in range(test_cases): - n_dimensions = np.random.randint(1, 8) - dim_sizes = np.random.randint(1, 7, size=n_dimensions) + for _ in range(test_cases): + n_dimensions = generator.integers(1, 8) + dim_sizes = generator.integers(1, 7, size=n_dimensions) _ = Model(array=np.zeros(dim_sizes, dtype=dtype)) @@ -144,15 +143,16 @@ def test_ndarray_any(): """ using :class:`typing.Any` in for the shape means any shape """ + generator = np.random.default_rng() class Model(BaseModel): array: NDArray[Any, np.uint8] # not very sophisticated fuzzing of "any shape" test_cases = 100 - for i in range(test_cases): - n_dimensions = np.random.randint(1, 8) - dim_sizes = np.random.randint(1, 16, size=n_dimensions) + for _ in range(test_cases): + n_dimensions = generator.integers(1, 8) + dim_sizes = generator.integers(1, 16, size=n_dimensions) _ = Model(array=np.zeros(dim_sizes, dtype=np.uint8)) @@ -191,7 +191,7 @@ def test_ndarray_serialize(): class Model(BaseModel): array: NDArray[Any, Number] - mod = Model(array=np.random.random((3, 3))) + mod = Model(array=np.random.default_rng().random((3, 3))) mod_str = mod.model_dump_json() mod_json = json.loads(mod_str) assert isinstance(mod_json["array"], list) diff --git a/tests/test_serialization.py b/tests/test_serialization.py index a42ee7c..4042e08 100644 --- a/tests/test_serialization.py +++ b/tests/test_serialization.py @@ -3,14 +3,15 @@ Test serialization-specific functionality that doesn't need to be applied across every interface (use test_interface/test_interfaces for that """ -import h5py -import pytest +import json from pathlib import Path from typing import Callable -import numpy as np -import json -from numpydantic.serialization import _walk_and_apply, _relativize_paths, relative_path +import h5py +import numpy as np +import pytest + +from numpydantic.serialization import _relativize_paths, _walk_and_apply, relative_path pytestmark = pytest.mark.serialization @@ -115,7 +116,8 @@ def test_absolute_path(hdf5_at_path, tmp_output_dir, model_blank): def test_walk_and_apply(): """ - Walk and apply should recursively apply a function to everything in a nesty structure + Walk and apply should recursively apply a function to everything in a + nesty structure """ test = { "a": 1, diff --git a/tests/test_shape.py b/tests/test_shape.py index b521054..c2ce279 100644 --- a/tests/test_shape.py +++ b/tests/test_shape.py @@ -1,9 +1,8 @@ -import pytest - from typing import Any -from pydantic import BaseModel, ValidationError import numpy as np +import pytest +from pydantic import BaseModel, ValidationError from numpydantic import NDArray, Shape