mirror of
https://github.com/p2p-ld/numpydantic.git
synced 2024-11-14 02:34: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 pytest
|
||||
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.cases import YES_PIPE, RGB_UNION, UNION_PIPE, DTYPE_CASES, DTYPE_IDS
|
||||
|
||||
from numpydantic.testing.helpers import ValidationCase
|
||||
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):
|
||||
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(
|
||||
|
@ -127,93 +54,6 @@ def shape_cases(request) -> ValidationCase:
|
|||
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)
|
||||
def dtype_cases(request) -> ValidationCase:
|
||||
return request.param
|
||||
|
|
|
@ -7,7 +7,7 @@ from pydantic import BaseModel, ValidationError
|
|||
from numpydantic.interface import DaskInterface
|
||||
from numpydantic.exceptions import DtypeError, ShapeError
|
||||
|
||||
from tests.conftest import ValidationCase
|
||||
from numpydantic.testing.helpers import ValidationCase
|
||||
|
||||
pytestmark = pytest.mark.dask
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ from numpydantic.interface import H5Interface
|
|||
from numpydantic.interface.hdf5 import H5ArrayPath, H5Proxy
|
||||
from numpydantic.exceptions import DtypeError, ShapeError
|
||||
|
||||
from tests.conftest import ValidationCase
|
||||
from numpydantic.testing.helpers import ValidationCase
|
||||
|
||||
pytestmark = pytest.mark.hdf5
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import pytest
|
|||
from pydantic import ValidationError, BaseModel
|
||||
from numpydantic.exceptions import DtypeError, ShapeError
|
||||
|
||||
from tests.conftest import ValidationCase
|
||||
from numpydantic.testing.helpers import ValidationCase
|
||||
|
||||
pytestmark = pytest.mark.numpy
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ from numpydantic.interface import ZarrInterface
|
|||
from numpydantic.interface.zarr import ZarrArrayPath
|
||||
from numpydantic.exceptions import DtypeError, ShapeError
|
||||
|
||||
from tests.conftest import ValidationCase
|
||||
from numpydantic.testing.helpers import ValidationCase
|
||||
|
||||
pytestmark = pytest.mark.zarr
|
||||
|
||||
|
|
Loading…
Reference in a new issue