mirror of
https://github.com/p2p-ld/numpydantic.git
synced 2024-11-14 10:44:28 +00:00
create testing module, add to docs
This commit is contained in:
parent
124024f48a
commit
f291afbbe2
11 changed files with 199 additions and 166 deletions
7
docs/api/testing/cases.md
Normal file
7
docs/api/testing/cases.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# cases
|
||||||
|
|
||||||
|
```{eval-rst}
|
||||||
|
.. automodule:: numpydantic.testing.cases
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
```
|
7
docs/api/testing/helpers.md
Normal file
7
docs/api/testing/helpers.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# helpers
|
||||||
|
|
||||||
|
```{eval-rst}
|
||||||
|
.. automodule:: numpydantic.testing.helpers
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
```
|
8
docs/api/testing/index.md
Normal file
8
docs/api/testing/index.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# testing
|
||||||
|
|
||||||
|
Utilities for testing and 3rd-party interface development.
|
||||||
|
|
||||||
|
```{toctree}
|
||||||
|
cases
|
||||||
|
helpers
|
||||||
|
```
|
0
src/numpydantic/testing/__init__.py
Normal file
0
src/numpydantic/testing/__init__.py
Normal file
132
src/numpydantic/testing/cases.py
Normal file
132
src/numpydantic/testing/cases.py
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
import sys
|
||||||
|
from typing import TypeAlias, Union
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from numpydantic import NDArray, Shape
|
||||||
|
from numpydantic.dtype import Float, Integer, Number
|
||||||
|
from numpydantic.testing.helpers import ValidationCase
|
||||||
|
|
||||||
|
if sys.version_info.minor >= 10:
|
||||||
|
from typing import TypeAlias
|
||||||
|
|
||||||
|
YES_PIPE = True
|
||||||
|
else:
|
||||||
|
from typing_extensions import TypeAlias
|
||||||
|
|
||||||
|
YES_PIPE = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BasicModel(BaseModel):
|
||||||
|
x: int
|
||||||
|
|
||||||
|
|
||||||
|
class BadModel(BaseModel):
|
||||||
|
x: int
|
||||||
|
|
||||||
|
|
||||||
|
class SubClass(BasicModel):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
RGB_UNION: TypeAlias = Union[
|
||||||
|
NDArray[Shape["* x, * y"], Number],
|
||||||
|
NDArray[Shape["* x, * y, 3 r_g_b"], Number],
|
||||||
|
NDArray[Shape["* x, * y, 3 r_g_b, 4 r_g_b_a"], Number],
|
||||||
|
]
|
||||||
|
NUMBER: TypeAlias = NDArray[Shape["*, *, *"], Number]
|
||||||
|
INTEGER: TypeAlias = NDArray[Shape["*, *, *"], Integer]
|
||||||
|
FLOAT: TypeAlias = NDArray[Shape["*, *, *"], Float]
|
||||||
|
STRING: TypeAlias = NDArray[Shape["*, *, *"], str]
|
||||||
|
MODEL: TypeAlias = NDArray[Shape["*, *, *"], BasicModel]
|
||||||
|
UNION_TYPE: TypeAlias = NDArray[Shape["*, *, *"], Union[np.uint32, np.float32]]
|
||||||
|
UNION_PIPE: TypeAlias = NDArray[Shape["*, *, *"], np.uint32 | np.float32]
|
||||||
|
DTYPE_CASES = [
|
||||||
|
ValidationCase(dtype=float, passes=True),
|
||||||
|
ValidationCase(dtype=int, passes=False),
|
||||||
|
ValidationCase(dtype=np.uint8, passes=False),
|
||||||
|
ValidationCase(annotation=NUMBER, dtype=int, passes=True),
|
||||||
|
ValidationCase(annotation=NUMBER, dtype=float, passes=True),
|
||||||
|
ValidationCase(annotation=NUMBER, dtype=np.uint8, passes=True),
|
||||||
|
ValidationCase(annotation=NUMBER, dtype=np.float16, passes=True),
|
||||||
|
ValidationCase(annotation=NUMBER, dtype=str, passes=False),
|
||||||
|
ValidationCase(annotation=INTEGER, dtype=int, passes=True),
|
||||||
|
ValidationCase(annotation=INTEGER, dtype=np.uint8, passes=True),
|
||||||
|
ValidationCase(annotation=INTEGER, dtype=float, passes=False),
|
||||||
|
ValidationCase(annotation=INTEGER, dtype=np.float32, passes=False),
|
||||||
|
ValidationCase(annotation=INTEGER, dtype=str, passes=False),
|
||||||
|
ValidationCase(annotation=FLOAT, dtype=float, passes=True),
|
||||||
|
ValidationCase(annotation=FLOAT, dtype=np.float32, passes=True),
|
||||||
|
ValidationCase(annotation=FLOAT, dtype=int, passes=False),
|
||||||
|
ValidationCase(annotation=FLOAT, dtype=np.uint8, passes=False),
|
||||||
|
ValidationCase(annotation=FLOAT, dtype=str, passes=False),
|
||||||
|
ValidationCase(annotation=STRING, dtype=str, passes=True),
|
||||||
|
ValidationCase(annotation=STRING, dtype=int, passes=False),
|
||||||
|
ValidationCase(annotation=STRING, dtype=float, passes=False),
|
||||||
|
ValidationCase(annotation=MODEL, dtype=BasicModel, passes=True),
|
||||||
|
ValidationCase(annotation=MODEL, dtype=BadModel, passes=False),
|
||||||
|
ValidationCase(annotation=MODEL, dtype=int, passes=False),
|
||||||
|
ValidationCase(annotation=MODEL, dtype=SubClass, passes=True),
|
||||||
|
ValidationCase(annotation=UNION_TYPE, dtype=np.uint32, passes=True),
|
||||||
|
ValidationCase(annotation=UNION_TYPE, dtype=np.float32, passes=True),
|
||||||
|
ValidationCase(annotation=UNION_TYPE, dtype=np.uint64, passes=False),
|
||||||
|
ValidationCase(annotation=UNION_TYPE, dtype=np.float64, passes=False),
|
||||||
|
ValidationCase(annotation=UNION_TYPE, dtype=str, passes=False),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
DTYPE_IDS = [
|
||||||
|
"float",
|
||||||
|
"int",
|
||||||
|
"uint8",
|
||||||
|
"number-int",
|
||||||
|
"number-float",
|
||||||
|
"number-uint8",
|
||||||
|
"number-float16",
|
||||||
|
"number-str",
|
||||||
|
"integer-int",
|
||||||
|
"integer-uint8",
|
||||||
|
"integer-float",
|
||||||
|
"integer-float32",
|
||||||
|
"integer-str",
|
||||||
|
"float-float",
|
||||||
|
"float-float32",
|
||||||
|
"float-int",
|
||||||
|
"float-uint8",
|
||||||
|
"float-str",
|
||||||
|
"str-str",
|
||||||
|
"str-int",
|
||||||
|
"str-float",
|
||||||
|
"model-model",
|
||||||
|
"model-badmodel",
|
||||||
|
"model-int",
|
||||||
|
"model-subclass",
|
||||||
|
"union-type-uint32",
|
||||||
|
"union-type-float32",
|
||||||
|
"union-type-uint64",
|
||||||
|
"union-type-float64",
|
||||||
|
"union-type-str",
|
||||||
|
]
|
||||||
|
|
||||||
|
if YES_PIPE:
|
||||||
|
DTYPE_CASES.extend(
|
||||||
|
[
|
||||||
|
ValidationCase(annotation=UNION_PIPE, dtype=np.uint32, passes=True),
|
||||||
|
ValidationCase(annotation=UNION_PIPE, dtype=np.float32, passes=True),
|
||||||
|
ValidationCase(annotation=UNION_PIPE, dtype=np.uint64, passes=False),
|
||||||
|
ValidationCase(annotation=UNION_PIPE, dtype=np.float64, passes=False),
|
||||||
|
ValidationCase(annotation=UNION_PIPE, dtype=str, passes=False),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
DTYPE_IDS.extend(
|
||||||
|
[
|
||||||
|
"union-pipe-uint32",
|
||||||
|
"union-pipe-float32",
|
||||||
|
"union-pipe-uint64",
|
||||||
|
"union-pipe-float64",
|
||||||
|
"union-pipe-str",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
39
src/numpydantic/testing/helpers.py
Normal file
39
src/numpydantic/testing/helpers.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
from typing import Any, Tuple, Type, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel, ConfigDict, computed_field
|
||||||
|
|
||||||
|
from numpydantic import NDArray, Shape
|
||||||
|
from numpydantic.dtype import Float
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationCase(BaseModel):
|
||||||
|
"""
|
||||||
|
Test case for validating an array.
|
||||||
|
|
||||||
|
Contains both the validating model and the parameterization for an array to
|
||||||
|
test in a given interface
|
||||||
|
"""
|
||||||
|
|
||||||
|
annotation: Any = NDArray[Shape["10, 10, *"], Float]
|
||||||
|
"""
|
||||||
|
Array annotation used in the validating model
|
||||||
|
Any typed because the types of type annotations are weird
|
||||||
|
"""
|
||||||
|
shape: Tuple[int, ...] = (10, 10, 10)
|
||||||
|
"""Shape of the array to validate"""
|
||||||
|
dtype: Union[Type, np.dtype] = float
|
||||||
|
"""Dtype of the array to validate"""
|
||||||
|
passes: bool
|
||||||
|
"""Whether the validation should pass or not"""
|
||||||
|
|
||||||
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||||
|
|
||||||
|
@computed_field()
|
||||||
|
def model(self) -> Type[BaseModel]:
|
||||||
|
"""A model with a field ``array`` with the given annotation"""
|
||||||
|
annotation = self.annotation
|
||||||
|
|
||||||
|
class Model(BaseModel):
|
||||||
|
array: annotation
|
||||||
|
|
||||||
|
return Model
|
|
@ -1,25 +1,10 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
from numpydantic.testing.cases import YES_PIPE, RGB_UNION, UNION_PIPE, DTYPE_CASES, DTYPE_IDS
|
||||||
from typing import Any, Tuple, Union, Type
|
|
||||||
|
|
||||||
from pydantic import BaseModel, computed_field, ConfigDict
|
|
||||||
from numpydantic import NDArray, Shape
|
|
||||||
from numpydantic.ndarray import NDArrayMeta
|
|
||||||
from numpydantic.dtype import Float, Number, Integer
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
|
from numpydantic.testing.helpers import ValidationCase
|
||||||
from tests.fixtures import *
|
from tests.fixtures import *
|
||||||
|
|
||||||
if sys.version_info.minor >= 10:
|
|
||||||
from typing import TypeAlias
|
|
||||||
|
|
||||||
YES_PIPE = True
|
|
||||||
else:
|
|
||||||
from typing_extensions import TypeAlias
|
|
||||||
|
|
||||||
YES_PIPE = False
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption(
|
parser.addoption(
|
||||||
|
@ -29,65 +14,7 @@ def pytest_addoption(parser):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ValidationCase(BaseModel):
|
|
||||||
"""
|
|
||||||
Test case for validating an array.
|
|
||||||
|
|
||||||
Contains both the validating model and the parameterization for an array to
|
|
||||||
test in a given interface
|
|
||||||
"""
|
|
||||||
|
|
||||||
annotation: Any = NDArray[Shape["10, 10, *"], Float]
|
|
||||||
"""
|
|
||||||
Array annotation used in the validating model
|
|
||||||
Any typed because the types of type annotations are weird
|
|
||||||
"""
|
|
||||||
shape: Tuple[int, ...] = (10, 10, 10)
|
|
||||||
"""Shape of the array to validate"""
|
|
||||||
dtype: Union[Type, np.dtype] = float
|
|
||||||
"""Dtype of the array to validate"""
|
|
||||||
passes: bool
|
|
||||||
"""Whether the validation should pass or not"""
|
|
||||||
|
|
||||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
||||||
|
|
||||||
@computed_field()
|
|
||||||
def model(self) -> Type[BaseModel]:
|
|
||||||
"""A model with a field ``array`` with the given annotation"""
|
|
||||||
annotation = self.annotation
|
|
||||||
|
|
||||||
class Model(BaseModel):
|
|
||||||
array: annotation
|
|
||||||
|
|
||||||
return Model
|
|
||||||
|
|
||||||
|
|
||||||
class BasicModel(BaseModel):
|
|
||||||
x: int
|
|
||||||
|
|
||||||
|
|
||||||
class BadModel(BaseModel):
|
|
||||||
x: int
|
|
||||||
|
|
||||||
|
|
||||||
class SubClass(BasicModel):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
RGB_UNION: TypeAlias = Union[
|
|
||||||
NDArray[Shape["* x, * y"], Number],
|
|
||||||
NDArray[Shape["* x, * y, 3 r_g_b"], Number],
|
|
||||||
NDArray[Shape["* x, * y, 3 r_g_b, 4 r_g_b_a"], Number],
|
|
||||||
]
|
|
||||||
|
|
||||||
NUMBER: TypeAlias = NDArray[Shape["*, *, *"], Number]
|
|
||||||
INTEGER: TypeAlias = NDArray[Shape["*, *, *"], Integer]
|
|
||||||
FLOAT: TypeAlias = NDArray[Shape["*, *, *"], Float]
|
|
||||||
STRING: TypeAlias = NDArray[Shape["*, *, *"], str]
|
|
||||||
MODEL: TypeAlias = NDArray[Shape["*, *, *"], BasicModel]
|
|
||||||
UNION_TYPE: TypeAlias = NDArray[Shape["*, *, *"], Union[np.uint32, np.float32]]
|
|
||||||
if YES_PIPE:
|
|
||||||
UNION_PIPE: TypeAlias = NDArray[Shape["*, *, *"], np.uint32 | np.float32]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(
|
@pytest.fixture(
|
||||||
|
@ -127,93 +54,6 @@ def shape_cases(request) -> ValidationCase:
|
||||||
return request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
DTYPE_CASES = [
|
|
||||||
ValidationCase(dtype=float, passes=True),
|
|
||||||
ValidationCase(dtype=int, passes=False),
|
|
||||||
ValidationCase(dtype=np.uint8, passes=False),
|
|
||||||
ValidationCase(annotation=NUMBER, dtype=int, passes=True),
|
|
||||||
ValidationCase(annotation=NUMBER, dtype=float, passes=True),
|
|
||||||
ValidationCase(annotation=NUMBER, dtype=np.uint8, passes=True),
|
|
||||||
ValidationCase(annotation=NUMBER, dtype=np.float16, passes=True),
|
|
||||||
ValidationCase(annotation=NUMBER, dtype=str, passes=False),
|
|
||||||
ValidationCase(annotation=INTEGER, dtype=int, passes=True),
|
|
||||||
ValidationCase(annotation=INTEGER, dtype=np.uint8, passes=True),
|
|
||||||
ValidationCase(annotation=INTEGER, dtype=float, passes=False),
|
|
||||||
ValidationCase(annotation=INTEGER, dtype=np.float32, passes=False),
|
|
||||||
ValidationCase(annotation=INTEGER, dtype=str, passes=False),
|
|
||||||
ValidationCase(annotation=FLOAT, dtype=float, passes=True),
|
|
||||||
ValidationCase(annotation=FLOAT, dtype=np.float32, passes=True),
|
|
||||||
ValidationCase(annotation=FLOAT, dtype=int, passes=False),
|
|
||||||
ValidationCase(annotation=FLOAT, dtype=np.uint8, passes=False),
|
|
||||||
ValidationCase(annotation=FLOAT, dtype=str, passes=False),
|
|
||||||
ValidationCase(annotation=STRING, dtype=str, passes=True),
|
|
||||||
ValidationCase(annotation=STRING, dtype=int, passes=False),
|
|
||||||
ValidationCase(annotation=STRING, dtype=float, passes=False),
|
|
||||||
ValidationCase(annotation=MODEL, dtype=BasicModel, passes=True),
|
|
||||||
ValidationCase(annotation=MODEL, dtype=BadModel, passes=False),
|
|
||||||
ValidationCase(annotation=MODEL, dtype=int, passes=False),
|
|
||||||
ValidationCase(annotation=MODEL, dtype=SubClass, passes=True),
|
|
||||||
ValidationCase(annotation=UNION_TYPE, dtype=np.uint32, passes=True),
|
|
||||||
ValidationCase(annotation=UNION_TYPE, dtype=np.float32, passes=True),
|
|
||||||
ValidationCase(annotation=UNION_TYPE, dtype=np.uint64, passes=False),
|
|
||||||
ValidationCase(annotation=UNION_TYPE, dtype=np.float64, passes=False),
|
|
||||||
ValidationCase(annotation=UNION_TYPE, dtype=str, passes=False),
|
|
||||||
]
|
|
||||||
|
|
||||||
DTYPE_IDS = [
|
|
||||||
"float",
|
|
||||||
"int",
|
|
||||||
"uint8",
|
|
||||||
"number-int",
|
|
||||||
"number-float",
|
|
||||||
"number-uint8",
|
|
||||||
"number-float16",
|
|
||||||
"number-str",
|
|
||||||
"integer-int",
|
|
||||||
"integer-uint8",
|
|
||||||
"integer-float",
|
|
||||||
"integer-float32",
|
|
||||||
"integer-str",
|
|
||||||
"float-float",
|
|
||||||
"float-float32",
|
|
||||||
"float-int",
|
|
||||||
"float-uint8",
|
|
||||||
"float-str",
|
|
||||||
"str-str",
|
|
||||||
"str-int",
|
|
||||||
"str-float",
|
|
||||||
"model-model",
|
|
||||||
"model-badmodel",
|
|
||||||
"model-int",
|
|
||||||
"model-subclass",
|
|
||||||
"union-type-uint32",
|
|
||||||
"union-type-float32",
|
|
||||||
"union-type-uint64",
|
|
||||||
"union-type-float64",
|
|
||||||
"union-type-str",
|
|
||||||
]
|
|
||||||
|
|
||||||
if YES_PIPE:
|
|
||||||
DTYPE_CASES.extend(
|
|
||||||
[
|
|
||||||
ValidationCase(annotation=UNION_PIPE, dtype=np.uint32, passes=True),
|
|
||||||
ValidationCase(annotation=UNION_PIPE, dtype=np.float32, passes=True),
|
|
||||||
ValidationCase(annotation=UNION_PIPE, dtype=np.uint64, passes=False),
|
|
||||||
ValidationCase(annotation=UNION_PIPE, dtype=np.float64, passes=False),
|
|
||||||
ValidationCase(annotation=UNION_PIPE, dtype=str, passes=False),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
DTYPE_IDS.extend(
|
|
||||||
[
|
|
||||||
"union-pipe-uint32",
|
|
||||||
"union-pipe-float32",
|
|
||||||
"union-pipe-uint64",
|
|
||||||
"union-pipe-float64",
|
|
||||||
"union-pipe-str",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module", params=DTYPE_CASES, ids=DTYPE_IDS)
|
@pytest.fixture(scope="module", params=DTYPE_CASES, ids=DTYPE_IDS)
|
||||||
def dtype_cases(request) -> ValidationCase:
|
def dtype_cases(request) -> ValidationCase:
|
||||||
return request.param
|
return request.param
|
||||||
|
|
|
@ -7,7 +7,7 @@ from pydantic import BaseModel, ValidationError
|
||||||
from numpydantic.interface import DaskInterface
|
from numpydantic.interface import DaskInterface
|
||||||
from numpydantic.exceptions import DtypeError, ShapeError
|
from numpydantic.exceptions import DtypeError, ShapeError
|
||||||
|
|
||||||
from tests.conftest import ValidationCase
|
from numpydantic.testing.helpers import ValidationCase
|
||||||
|
|
||||||
pytestmark = pytest.mark.dask
|
pytestmark = pytest.mark.dask
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ from numpydantic.interface import H5Interface
|
||||||
from numpydantic.interface.hdf5 import H5ArrayPath, H5Proxy
|
from numpydantic.interface.hdf5 import H5ArrayPath, H5Proxy
|
||||||
from numpydantic.exceptions import DtypeError, ShapeError
|
from numpydantic.exceptions import DtypeError, ShapeError
|
||||||
|
|
||||||
from tests.conftest import ValidationCase
|
from numpydantic.testing.helpers import ValidationCase
|
||||||
|
|
||||||
pytestmark = pytest.mark.hdf5
|
pytestmark = pytest.mark.hdf5
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import pytest
|
||||||
from pydantic import ValidationError, BaseModel
|
from pydantic import ValidationError, BaseModel
|
||||||
from numpydantic.exceptions import DtypeError, ShapeError
|
from numpydantic.exceptions import DtypeError, ShapeError
|
||||||
|
|
||||||
from tests.conftest import ValidationCase
|
from numpydantic.testing.helpers import ValidationCase
|
||||||
|
|
||||||
pytestmark = pytest.mark.numpy
|
pytestmark = pytest.mark.numpy
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ from numpydantic.interface import ZarrInterface
|
||||||
from numpydantic.interface.zarr import ZarrArrayPath
|
from numpydantic.interface.zarr import ZarrArrayPath
|
||||||
from numpydantic.exceptions import DtypeError, ShapeError
|
from numpydantic.exceptions import DtypeError, ShapeError
|
||||||
|
|
||||||
from tests.conftest import ValidationCase
|
from numpydantic.testing.helpers import ValidationCase
|
||||||
|
|
||||||
pytestmark = pytest.mark.zarr
|
pytestmark = pytest.mark.zarr
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue