mirror of
https://github.com/p2p-ld/numpydantic.git
synced 2025-01-10 05:54:26 +00:00
Merge pull request #8 from p2p-ld/constructable
Make NDArray callable as a functional validator
This commit is contained in:
commit
6a397a9aba
6 changed files with 78 additions and 5 deletions
|
@ -2,6 +2,61 @@
|
||||||
|
|
||||||
## 1.*
|
## 1.*
|
||||||
|
|
||||||
|
### 1.3.3 - 24-08-13 - Callable type annotations
|
||||||
|
|
||||||
|
Problem, when you use a numpydantic `"wrap"` validator, it gives the annotation as a `handler` function.
|
||||||
|
|
||||||
|
So this is effectively what happens
|
||||||
|
|
||||||
|
```python
|
||||||
|
@field_validator("*", mode="wrap")
|
||||||
|
@classmethod
|
||||||
|
def cast_specified_columns(
|
||||||
|
cls, val: Any, handler: ValidatorFunctionWrapHandler, info: ValidationInfo
|
||||||
|
) -> Any:
|
||||||
|
# where handler is the callable here
|
||||||
|
# so
|
||||||
|
# return handler(val)
|
||||||
|
|
||||||
|
return NDArray[Any, Any](val)
|
||||||
|
```
|
||||||
|
|
||||||
|
where `Any, Any` is whatever you had put in there.
|
||||||
|
|
||||||
|
So this makes it so you can use an annotation as a functional validator. it looks a little bit whacky but idk it makes sense as a PARAMETERIZED TYPE
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> from numpydantic import NDArray, Shape
|
||||||
|
>>> import numpy as np
|
||||||
|
|
||||||
|
>>> array = np.array([1,2,3], dtype=int)
|
||||||
|
>>> validated = NDArray[Shape["3"], int](array)
|
||||||
|
>>> assert validated is array
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> bad_array = np.array([1,2,3,4], dtype=int)
|
||||||
|
>>> _ = NDArray[Shape["3"], int](bad_array)
|
||||||
|
175 """
|
||||||
|
176 Raise a ShapeError if the shape is invalid.
|
||||||
|
177
|
||||||
|
178 Raises:
|
||||||
|
179 :class:`~numpydantic.exceptions.ShapeError`
|
||||||
|
180 """
|
||||||
|
181 if not valid:
|
||||||
|
--> 182 raise ShapeError(
|
||||||
|
183 f"Invalid shape! expected shape {self.shape.prepared_args}, "
|
||||||
|
184 f"got shape {shape}"
|
||||||
|
185 )
|
||||||
|
|
||||||
|
ShapeError: Invalid shape! expected shape ['3'], got shape (4,)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
**Performance:**
|
||||||
|
- Don't import the pandas module if we don't have to, since we are not
|
||||||
|
using it. This shaves ~600ms off import time.
|
||||||
|
|
||||||
|
|
||||||
### 1.3.2 - 24-08-12 - Allow subclasses of dtypes
|
### 1.3.2 - 24-08-12 - Allow subclasses of dtypes
|
||||||
|
|
||||||
(also when using objects for dtypes, subclasses of that object are allowed to validate)
|
(also when using objects for dtypes, subclasses of that object are allowed to validate)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "numpydantic"
|
name = "numpydantic"
|
||||||
version = "1.3.2"
|
version = "1.3.3"
|
||||||
description = "Type and shape validation and serialization for numpy arrays in pydantic models"
|
description = "Type and shape validation and serialization for numpy arrays in pydantic models"
|
||||||
authors = [
|
authors = [
|
||||||
{name = "sneakers-the-rat", email = "sneakers-the-rat@protonmail.com"},
|
{name = "sneakers-the-rat", email = "sneakers-the-rat@protonmail.com"},
|
||||||
|
|
|
@ -29,7 +29,7 @@ from numpydantic.schema import (
|
||||||
get_validate_interface,
|
get_validate_interface,
|
||||||
make_json_schema,
|
make_json_schema,
|
||||||
)
|
)
|
||||||
from numpydantic.types import DtypeType, ShapeType
|
from numpydantic.types import DtypeType, NDArrayType, ShapeType
|
||||||
from numpydantic.vendor.nptyping.error import InvalidArgumentsError
|
from numpydantic.vendor.nptyping.error import InvalidArgumentsError
|
||||||
from numpydantic.vendor.nptyping.ndarray import NDArrayMeta as _NDArrayMeta
|
from numpydantic.vendor.nptyping.ndarray import NDArrayMeta as _NDArrayMeta
|
||||||
from numpydantic.vendor.nptyping.nptyping_type import NPTypingType
|
from numpydantic.vendor.nptyping.nptyping_type import NPTypingType
|
||||||
|
@ -54,6 +54,10 @@ class NDArrayMeta(_NDArrayMeta, implementation="NDArray"):
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
__getitem__ = SubscriptableMeta.__getitem__
|
__getitem__ = SubscriptableMeta.__getitem__
|
||||||
|
|
||||||
|
def __call__(cls, val: NDArrayType) -> NDArrayType:
|
||||||
|
"""Call ndarray as a validator function"""
|
||||||
|
return get_validate_interface(cls.__args__[0], cls.__args__[1])(val)
|
||||||
|
|
||||||
def __instancecheck__(self, instance: Any):
|
def __instancecheck__(self, instance: Any):
|
||||||
"""
|
"""
|
||||||
Extended type checking that determines whether
|
Extended type checking that determines whether
|
||||||
|
|
4
src/numpydantic/vendor/nptyping/__init__.py
vendored
4
src/numpydantic/vendor/nptyping/__init__.py
vendored
|
@ -32,7 +32,9 @@ from numpydantic.vendor.nptyping.error import (
|
||||||
)
|
)
|
||||||
from numpydantic.vendor.nptyping.ndarray import NDArray
|
from numpydantic.vendor.nptyping.ndarray import NDArray
|
||||||
from numpydantic.vendor.nptyping.package_info import __version__
|
from numpydantic.vendor.nptyping.package_info import __version__
|
||||||
from numpydantic.vendor.nptyping.pandas_.dataframe import DataFrame
|
|
||||||
|
# don't import unnecessarily since we don't use it
|
||||||
|
# from numpydantic.vendor.nptyping.pandas_.dataframe import DataFrame
|
||||||
from numpydantic.vendor.nptyping.recarray import RecArray
|
from numpydantic.vendor.nptyping.recarray import RecArray
|
||||||
from numpydantic.vendor.nptyping.shape import Shape
|
from numpydantic.vendor.nptyping.shape import Shape
|
||||||
from numpydantic.vendor.nptyping.shape_expression import (
|
from numpydantic.vendor.nptyping.shape_expression import (
|
||||||
|
|
2
src/numpydantic/vendor/nptyping/ndarray.py
vendored
2
src/numpydantic/vendor/nptyping/ndarray.py
vendored
|
@ -31,7 +31,6 @@ import numpy as np
|
||||||
from numpydantic.vendor.nptyping.base_meta_classes import (
|
from numpydantic.vendor.nptyping.base_meta_classes import (
|
||||||
FinalMeta,
|
FinalMeta,
|
||||||
ImmutableMeta,
|
ImmutableMeta,
|
||||||
InconstructableMeta,
|
|
||||||
MaybeCheckableMeta,
|
MaybeCheckableMeta,
|
||||||
PrintableMeta,
|
PrintableMeta,
|
||||||
SubscriptableMeta,
|
SubscriptableMeta,
|
||||||
|
@ -54,7 +53,6 @@ from numpydantic.vendor.nptyping.typing_ import (
|
||||||
|
|
||||||
class NDArrayMeta(
|
class NDArrayMeta(
|
||||||
SubscriptableMeta,
|
SubscriptableMeta,
|
||||||
InconstructableMeta,
|
|
||||||
ImmutableMeta,
|
ImmutableMeta,
|
||||||
FinalMeta,
|
FinalMeta,
|
||||||
MaybeCheckableMeta,
|
MaybeCheckableMeta,
|
||||||
|
|
|
@ -350,3 +350,17 @@ def test_instancecheck():
|
||||||
return array
|
return array
|
||||||
|
|
||||||
my_function(np.zeros((1, 2, 3), int))
|
my_function(np.zeros((1, 2, 3), int))
|
||||||
|
|
||||||
|
|
||||||
|
def test_callable():
|
||||||
|
"""
|
||||||
|
NDArray objects are callable to validate and cast
|
||||||
|
Don't test validation here, just that we can be called
|
||||||
|
"""
|
||||||
|
annotation = NDArray[Shape["3"], int]
|
||||||
|
array = np.array([1, 2, 3], dtype=int)
|
||||||
|
validated = annotation(array)
|
||||||
|
assert validated is array
|
||||||
|
|
||||||
|
with pytest.raises(DtypeError):
|
||||||
|
_ = annotation(np.zeros((1, 2, 3)))
|
||||||
|
|
Loading…
Reference in a new issue