valarray 0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- valarray/__init__.py +6 -0
- valarray/core/__init__.py +27 -0
- valarray/core/array.py +121 -0
- valarray/core/array_type_adapter.py +109 -0
- valarray/core/axes_and_fields.py +127 -0
- valarray/core/comparisons.py +71 -0
- valarray/core/errors_exceptions/__init__.py +41 -0
- valarray/core/errors_exceptions/error_list.py +87 -0
- valarray/core/errors_exceptions/exceptions.py +75 -0
- valarray/core/errors_exceptions/generic.py +57 -0
- valarray/core/errors_exceptions/validation_errors/__init__.py +0 -0
- valarray/core/errors_exceptions/validation_errors/array_creation.py +14 -0
- valarray/core/errors_exceptions/validation_errors/axes.py +68 -0
- valarray/core/errors_exceptions/validation_errors/base.py +26 -0
- valarray/core/errors_exceptions/validation_errors/dtype.py +60 -0
- valarray/core/errors_exceptions/validation_errors/values.py +99 -0
- valarray/core/types/__init__.py +24 -0
- valarray/core/types/generics.py +40 -0
- valarray/core/types/other.py +23 -0
- valarray/core/utils.py +89 -0
- valarray/core/validation_functions/__init__.py +20 -0
- valarray/core/validation_functions/array.py +62 -0
- valarray/core/validation_functions/array_values.py +47 -0
- valarray/core/validation_functions/dtype.py +44 -0
- valarray/core/validation_functions/field_values/__init__.py +0 -0
- valarray/core/validation_functions/field_values/core.py +106 -0
- valarray/core/validation_functions/field_values/types_and_data_structures.py +90 -0
- valarray/core/validation_functions/field_values/utils.py +143 -0
- valarray/core/validation_functions/shape.py +67 -0
- valarray/core/validation_functions/utils.py +24 -0
- valarray/core/validators/__init__.py +13 -0
- valarray/core/validators/base.py +72 -0
- valarray/core/validators/value_comparisons.py +107 -0
- valarray/numpy/__init__.py +24 -0
- valarray/numpy/array.py +63 -0
- valarray/numpy/array_type_adapter.py +133 -0
- valarray/numpy/axes_and_fields.py +83 -0
- valarray/numpy/comparisons.py +135 -0
- valarray/numpy/errors_exceptions.py +172 -0
- valarray/numpy/types.py +32 -0
- valarray/numpy/validation_functions.py +170 -0
- valarray/numpy/validators.py +35 -0
- valarray-0.4.dist-info/METADATA +698 -0
- valarray-0.4.dist-info/RECORD +45 -0
- valarray-0.4.dist-info/WHEEL +4 -0
valarray/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
*import* `ValidatedArray` **ABC**
|
|
3
|
+
|
|
4
|
+
*import* `ArrayTypeAdapter` **ABC**
|
|
5
|
+
|
|
6
|
+
*import* `Comparisons` **ABC**
|
|
7
|
+
|
|
8
|
+
*import* `Field` **dataclass**
|
|
9
|
+
|
|
10
|
+
`.validation_functions` ***module***
|
|
11
|
+
|
|
12
|
+
`.validators` ***module***
|
|
13
|
+
|
|
14
|
+
`.errors_exceptions` ***module***
|
|
15
|
+
|
|
16
|
+
`.types` ***module***
|
|
17
|
+
|
|
18
|
+
`.axes_and_fields` ***module***
|
|
19
|
+
|
|
20
|
+
`.utils` ***module***
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# pyright: reportUnusedImport=false
|
|
24
|
+
from .array import ValidatedArray
|
|
25
|
+
from .array_type_adapter import ArrayTypeAdapter
|
|
26
|
+
from .axes_and_fields import Field
|
|
27
|
+
from .comparisons import Comparisons
|
valarray/core/array.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from types import EllipsisType
|
|
3
|
+
from typing import Any, Final, Generic, Optional, Self
|
|
4
|
+
|
|
5
|
+
from valarray.core.axes_and_fields import AxesTuple
|
|
6
|
+
|
|
7
|
+
from .array_type_adapter import ArrayTypeAdapter
|
|
8
|
+
from .axes_and_fields import AxesTuple
|
|
9
|
+
from .errors_exceptions import ValidationException
|
|
10
|
+
from .types import ArrayIndicesT, ArrayT, ComparableValueT, DTypeLikeT, DTypeT
|
|
11
|
+
from .utils import constraints_to_validators, setup_array
|
|
12
|
+
from .validation_functions import validate_array
|
|
13
|
+
from .validators.base import Validator
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ValidatedArray(
|
|
17
|
+
ABC, Generic[ArrayT, DTypeLikeT, DTypeT, ArrayIndicesT, ComparableValueT]
|
|
18
|
+
):
|
|
19
|
+
"""Abstract base class for Validated Array implementations.
|
|
20
|
+
Subclasses need to implement:
|
|
21
|
+
|
|
22
|
+
**abstract property**
|
|
23
|
+
- `ata` -> returns a subclass of `ArrayTypeAdapter`
|
|
24
|
+
|
|
25
|
+
You should also specify generic type variables:
|
|
26
|
+
- `ArrayT` - type of array object
|
|
27
|
+
- `DTypeLikeT` - data type specification
|
|
28
|
+
- `DTypeT` - data type object
|
|
29
|
+
- `ArrayIndexT` - object(s) that can select values from array using `__getitem__` method.
|
|
30
|
+
- `ComparableValueT` - type of object an array can be compared with.
|
|
31
|
+
|
|
32
|
+
(introduced ***v0.1***, last_modified ***v0.3***)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
dtype: DTypeLikeT | EllipsisType = ...
|
|
36
|
+
schema: AxesTuple[ArrayT, ArrayIndicesT, ComparableValueT] | EllipsisType = ...
|
|
37
|
+
validators: tuple[Validator[ArrayT, ArrayIndicesT], ...] = ()
|
|
38
|
+
|
|
39
|
+
lt: Optional[ComparableValueT] = None
|
|
40
|
+
le: Optional[ComparableValueT] = None
|
|
41
|
+
ge: Optional[ComparableValueT] = None
|
|
42
|
+
gt: Optional[ComparableValueT] = None
|
|
43
|
+
eq: Optional[ComparableValueT] = None
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def ata(
|
|
48
|
+
self,
|
|
49
|
+
) -> ArrayTypeAdapter[ArrayT, DTypeLikeT, DTypeT, ArrayIndicesT, ComparableValueT]:
|
|
50
|
+
"""Interface for operations that have different implementations for each array type."""
|
|
51
|
+
|
|
52
|
+
def __init__(self, obj: Any, validate=True, coerce_dtype=False) -> None:
|
|
53
|
+
self.is_validated = False
|
|
54
|
+
|
|
55
|
+
self._array: Final[ArrayT] = setup_array(
|
|
56
|
+
obj,
|
|
57
|
+
self.dtype,
|
|
58
|
+
coerce_dtype,
|
|
59
|
+
self.schema,
|
|
60
|
+
self.ata,
|
|
61
|
+
self.__class__.__name__,
|
|
62
|
+
) # [!] CreateArrayException, CoerceDTypeException
|
|
63
|
+
|
|
64
|
+
if validate:
|
|
65
|
+
self.validate_array()
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def wrap(cls, array: ArrayT) -> Self:
|
|
69
|
+
"""Creates `ValidatedArray` without validation.
|
|
70
|
+
|
|
71
|
+
(introduced ***v0.1***, last_modified ***v0.1.1***)
|
|
72
|
+
"""
|
|
73
|
+
return cls(array, validate=False)
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def validate(cls, array: ArrayT) -> Self:
|
|
77
|
+
"""Creates `ValidatedArray` by validating an existing array.
|
|
78
|
+
|
|
79
|
+
(introduced ***v0.1***, last_modified ***v0.1.1***)
|
|
80
|
+
"""
|
|
81
|
+
return cls(array, validate=True)
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def empty(cls) -> Self:
|
|
85
|
+
"""Creates empty `ValidatedArray` by validating an existing array.
|
|
86
|
+
|
|
87
|
+
(introduced ***v0.1.1***)
|
|
88
|
+
"""
|
|
89
|
+
return cls(None)
|
|
90
|
+
|
|
91
|
+
def validate_array(self):
|
|
92
|
+
"""Validate array if it isn't already validated.
|
|
93
|
+
|
|
94
|
+
(introduced ***v0.1***, last_modified ***v.0.4***)"""
|
|
95
|
+
if self.is_validated:
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
# TODO: do not validate dtype if not necessary
|
|
99
|
+
|
|
100
|
+
validators = constraints_to_validators(
|
|
101
|
+
self.lt, self.le, self.ge, self.gt, self.eq, self.ata.comparisons
|
|
102
|
+
) + list(self.validators)
|
|
103
|
+
|
|
104
|
+
errs = validate_array(
|
|
105
|
+
self._array, self.dtype, self.schema, validators, self.ata
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if errs:
|
|
109
|
+
raise ValidationException(self.__class__.__name__, errs)
|
|
110
|
+
|
|
111
|
+
self.is_validated = True
|
|
112
|
+
|
|
113
|
+
def __repr__(self) -> str:
|
|
114
|
+
"""(introduced ***v0.1***, last_modified ***v0.2.2***)"""
|
|
115
|
+
cls_name = self.__class__.__name__
|
|
116
|
+
|
|
117
|
+
if self.ata.is_array_empty((self._array)):
|
|
118
|
+
rep = f"{cls_name}.empty()"
|
|
119
|
+
else:
|
|
120
|
+
rep = f"{cls_name}({repr(self._array)})"
|
|
121
|
+
return rep
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from types import EllipsisType
|
|
4
|
+
from typing import Any, Generic, Optional
|
|
5
|
+
|
|
6
|
+
from .axes_and_fields import AxisNameAndIdx, FieldNameAndIdx
|
|
7
|
+
from .comparisons import Comparisons
|
|
8
|
+
from .errors_exceptions import GenericErrors
|
|
9
|
+
from .types import ArrayT, AxSizes, ComparableValueT, DTypeLikeT, DTypeT
|
|
10
|
+
from .types.generics import ArrayIndicesT
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ArrayTypeAdapter(
|
|
14
|
+
ABC, Generic[ArrayT, DTypeLikeT, DTypeT, ArrayIndicesT, ComparableValueT]
|
|
15
|
+
):
|
|
16
|
+
"""Generic base array type adapter.
|
|
17
|
+
|
|
18
|
+
Type variables:
|
|
19
|
+
- `ArrayT` - type of array object
|
|
20
|
+
- `DTypeLikeT` - data type specification
|
|
21
|
+
- `DTypeT` - data type object
|
|
22
|
+
- `ArrayIndexT` - object(s) that can select values from array using `__getitem__` method.
|
|
23
|
+
- `ComparableValueT` - value array can be compared with
|
|
24
|
+
using `<`, `<=`, `==`, `=>`, `>` operators
|
|
25
|
+
|
|
26
|
+
Subclasses need to implement:
|
|
27
|
+
**abstract property**
|
|
28
|
+
- `errors` -
|
|
29
|
+
Subtypes of generic `ValidationError` classes.
|
|
30
|
+
- `array_type` -
|
|
31
|
+
Array type (to be used in `isinstance(obj, adapter.array_type)` check)
|
|
32
|
+
- `comparisons` -
|
|
33
|
+
Subtype of generic `Comparisons` class.
|
|
34
|
+
|
|
35
|
+
**abstract method**
|
|
36
|
+
- `create_array` -
|
|
37
|
+
Create an array object from data with specified dtype(like).
|
|
38
|
+
- `create_empty_array` -
|
|
39
|
+
Create an empty array object with specified dtype(like).
|
|
40
|
+
- `is_array_empty` -
|
|
41
|
+
Check if array is empty.
|
|
42
|
+
- `change_dtype` -
|
|
43
|
+
Change array data type.
|
|
44
|
+
- `dtype_from_dtypelike` -
|
|
45
|
+
Create dtype object from dtypelike.
|
|
46
|
+
- `dtypes_equal` -
|
|
47
|
+
Check if dtypes are equal
|
|
48
|
+
- `get_values` -
|
|
49
|
+
Get a list of values from array
|
|
50
|
+
|
|
51
|
+
(`dtypes_equal` and `get_values` are neccessary because `__eq__` and `__getitem__`
|
|
52
|
+
methods respectively can have different parameter names)
|
|
53
|
+
|
|
54
|
+
(introduced ***v0.1***, last_modified ***v0.4***)
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def errors(self) -> GenericErrors[ArrayT, DTypeLikeT, DTypeT, ArrayIndicesT]: ...
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def array_type(self) -> type[ArrayT]: ...
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
@abstractmethod
|
|
67
|
+
def comparisons(self) -> Comparisons[ArrayT, ArrayIndicesT, ComparableValueT]:
|
|
68
|
+
"""Methods used by `ComparisonValidator`"""
|
|
69
|
+
|
|
70
|
+
@abstractmethod
|
|
71
|
+
def change_dtype(self, arr: ArrayT, dtypelike: DTypeLikeT) -> ArrayT: ...
|
|
72
|
+
|
|
73
|
+
@abstractmethod
|
|
74
|
+
def create_array(
|
|
75
|
+
self, obj: Any, dtypelike: DTypeLikeT | EllipsisType
|
|
76
|
+
) -> ArrayT: ...
|
|
77
|
+
|
|
78
|
+
@abstractmethod
|
|
79
|
+
def is_array_empty(self, arr: ArrayT) -> bool: ...
|
|
80
|
+
|
|
81
|
+
@abstractmethod
|
|
82
|
+
def create_empty_array(
|
|
83
|
+
self, ax_sizes: AxSizes, dtypelike: DTypeLikeT | EllipsisType
|
|
84
|
+
) -> ArrayT: ...
|
|
85
|
+
|
|
86
|
+
@abstractmethod
|
|
87
|
+
def dtype_from_dtypelike(self, dtypelike: DTypeLikeT) -> DTypeT: ...
|
|
88
|
+
|
|
89
|
+
@abstractmethod
|
|
90
|
+
def dtypes_equal(self, actual_dtype: DTypeT, expected_dtype: DTypeT) -> bool: ...
|
|
91
|
+
|
|
92
|
+
@abstractmethod
|
|
93
|
+
def get_values(
|
|
94
|
+
self, arr: ArrayT, indices: Optional[ArrayIndicesT]
|
|
95
|
+
) -> Optional[list[Any]]: ...
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
def select_subset(
|
|
99
|
+
self, arr: ArrayT, ax: AxisNameAndIdx, fields: Sequence[FieldNameAndIdx]
|
|
100
|
+
) -> ArrayT: ...
|
|
101
|
+
|
|
102
|
+
@abstractmethod
|
|
103
|
+
def get_values_from_subset(
|
|
104
|
+
self,
|
|
105
|
+
arr: ArrayT,
|
|
106
|
+
indices: Optional[ArrayIndicesT],
|
|
107
|
+
ax: AxisNameAndIdx,
|
|
108
|
+
fields: Sequence[FieldNameAndIdx],
|
|
109
|
+
) -> Optional[dict[FieldNameAndIdx, Any]]: ...
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""
|
|
2
|
+
*import* `AxesTuple` **TypeAlias** - defines axes of schema
|
|
3
|
+
|
|
4
|
+
*import* `FieldsTuple` **TypeAlias** - defines fields of schema axis
|
|
5
|
+
|
|
6
|
+
*import* `Field` **dataclass** - defines constraints on field
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import ClassVar, Generic, Optional, TypeAlias
|
|
11
|
+
|
|
12
|
+
from valarray.core.validators import Validator
|
|
13
|
+
|
|
14
|
+
from .types.generics import ArrayIndicesT, ArrayT, ComparableValueT
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class Field(Generic[ArrayT, ArrayIndicesT, ComparableValueT]):
|
|
19
|
+
"""Field of an array axis.
|
|
20
|
+
|
|
21
|
+
Arguments:
|
|
22
|
+
name (Optional[str]): Optional descriptive name of field. Defaults to `None`.
|
|
23
|
+
validators (tuple[Validator[ArrayT, ArrayIndicesT], ...]):
|
|
24
|
+
optional list of validators applied to field values
|
|
25
|
+
lt (Optional[ComparableValueT]):
|
|
26
|
+
Optional constraint -> field values less than. Defaults to None.
|
|
27
|
+
le (Optional[ComparableValueT]):
|
|
28
|
+
Optional constraint -> field values less or equal than. Defaults to None.
|
|
29
|
+
ge (Optional[ComparableValueT]):
|
|
30
|
+
Optional constraint -> field values greater than. Defaults to None.
|
|
31
|
+
gt (Optional[ComparableValueT]):
|
|
32
|
+
Optional constraint -> field values greater or equal than. Defaults to None.
|
|
33
|
+
eq (Optional[ComparableValueT]):
|
|
34
|
+
Optional constraint -> field values equal to. Defaults to None.
|
|
35
|
+
|
|
36
|
+
(introduced ***v0.4***)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
name: Optional[str] = None
|
|
40
|
+
validators: tuple[Validator[ArrayT, ArrayIndicesT], ...] = ()
|
|
41
|
+
|
|
42
|
+
lt: Optional[ComparableValueT] = None
|
|
43
|
+
le: Optional[ComparableValueT] = None
|
|
44
|
+
ge: Optional[ComparableValueT] = None
|
|
45
|
+
gt: Optional[ComparableValueT] = None
|
|
46
|
+
eq: Optional[ComparableValueT] = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(unsafe_hash=True)
|
|
50
|
+
class _NameAndIdx:
|
|
51
|
+
_specifier: ClassVar[str] = "Field/Axis"
|
|
52
|
+
idx: int
|
|
53
|
+
name: Optional[str] = None
|
|
54
|
+
|
|
55
|
+
def __str__(self) -> str:
|
|
56
|
+
name_str = f": '{self.name}'" if self.name is not None else ""
|
|
57
|
+
return f"{self._specifier} < {self.idx} >{name_str}"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(unsafe_hash=True)
|
|
61
|
+
class AxisNameAndIdx(_NameAndIdx):
|
|
62
|
+
"""Name and index of axis.
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
```
|
|
66
|
+
a_named = AxisNameAndIdx(name="example_axis", idx=3)
|
|
67
|
+
a = AxisNameAndIdx(idx=3)
|
|
68
|
+
|
|
69
|
+
print(a_named)
|
|
70
|
+
>>> Axis < 3 >: 'example_axis'
|
|
71
|
+
|
|
72
|
+
print(a)
|
|
73
|
+
>>> Axis < 3 >
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
(introduced ***v0.4***)
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
_specifier: ClassVar[str] = "Axis"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass(unsafe_hash=True)
|
|
83
|
+
class FieldNameAndIdx(_NameAndIdx):
|
|
84
|
+
"""Name and index of field.
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
```
|
|
88
|
+
f_named = FieldNameAndIdx(name="example_field", idx=3)
|
|
89
|
+
f = FieldNameAndIdx(idx=3)
|
|
90
|
+
|
|
91
|
+
print(f_named)
|
|
92
|
+
>>> Field < 3 >: 'example_field'
|
|
93
|
+
|
|
94
|
+
print(f)
|
|
95
|
+
>>> Field < 3 >
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
(introduced ***v0.4***)
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
_specifier: ClassVar[str] = "Field"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
FieldsTuple: TypeAlias = tuple[
|
|
105
|
+
str | Field[ArrayT, ArrayIndicesT, ComparableValueT], ...
|
|
106
|
+
]
|
|
107
|
+
"""Tuple of array fields.
|
|
108
|
+
|
|
109
|
+
(introduced ***v0.1.1***, last_modified ***v0.4***)
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
AxesTuple: TypeAlias = tuple[
|
|
113
|
+
str | int | FieldsTuple[ArrayT, ArrayIndicesT, ComparableValueT], ...
|
|
114
|
+
]
|
|
115
|
+
"""Tuple of array axes. Can be either name, size or a tuple of fields.
|
|
116
|
+
```
|
|
117
|
+
import valarray.core
|
|
118
|
+
|
|
119
|
+
axes = (
|
|
120
|
+
16,
|
|
121
|
+
'items',
|
|
122
|
+
('x','y', valarray.core.Field()),
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
(introduced ***v0.1.1***, last_modified ***v0.4***)
|
|
127
|
+
"""
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any, Generic
|
|
3
|
+
|
|
4
|
+
from valarray.core.types import ArrayIndicesT, ArrayT, ComparableValueT
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Comparisons(ABC, Generic[ArrayT, ArrayIndicesT, ComparableValueT]):
|
|
8
|
+
"""Generic comparison functions.
|
|
9
|
+
|
|
10
|
+
Type variables:
|
|
11
|
+
- `ArrayT` - type of array object
|
|
12
|
+
- `ArrayIndexT` - object(s) that can select values from array using `__getitem__` method.
|
|
13
|
+
- `ComparableValueT` - value array can be compared with
|
|
14
|
+
using `<`, `<=`, `==`, `=>`, `>` operators
|
|
15
|
+
|
|
16
|
+
Subclasses need to implement:
|
|
17
|
+
**abstract method**
|
|
18
|
+
- `where_not_lt` -
|
|
19
|
+
Returns indices of values which are not less than value.
|
|
20
|
+
- `where_not_le` -
|
|
21
|
+
Returns indices of values which are not less than or equal value.
|
|
22
|
+
- `where_not_ge` -
|
|
23
|
+
Returns indices of values which are not greater than or equal value.
|
|
24
|
+
- `where_not_gt` -
|
|
25
|
+
Returns indices of values which are not greater than value.
|
|
26
|
+
- `where_not_eq` -
|
|
27
|
+
Returns indices of values which are not equal value.
|
|
28
|
+
- `n_indices` -
|
|
29
|
+
Returns number of indices.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
(introduced ***v0.3***, last_modified ***v0.4***)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def where_not_lt(self, arr: ArrayT, value: ComparableValueT) -> ArrayIndicesT:
|
|
37
|
+
"""(introduced ***v0.3***)"""
|
|
38
|
+
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def where_not_le(self, arr: ArrayT, value: ComparableValueT) -> ArrayIndicesT:
|
|
41
|
+
"""(introduced ***v0.3***)"""
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def where_not_ge(self, arr: ArrayT, value: ComparableValueT) -> ArrayIndicesT:
|
|
45
|
+
"""(introduced ***v0.3***)"""
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def where_not_gt(self, arr: ArrayT, value: ComparableValueT) -> ArrayIndicesT:
|
|
49
|
+
"""(introduced ***v0.3***)"""
|
|
50
|
+
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def where_not_eq(self, arr: ArrayT, value: ComparableValueT) -> ArrayIndicesT:
|
|
53
|
+
"""(introduced ***v0.3***)"""
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def n_indices(self, indices: ArrayIndicesT) -> int:
|
|
57
|
+
"""(introduced ***v0.3***)"""
|
|
58
|
+
|
|
59
|
+
def __hash__(self) -> int:
|
|
60
|
+
# HACK: For ComparisonValidator to be hashable, *Comparisons must be hashable.
|
|
61
|
+
# Because all instances of each type of *Comparisons class are the same
|
|
62
|
+
# I just convert class name into integer and use it as a hash
|
|
63
|
+
# using: https://stackoverflow.com/questions/61848516/convert-whole-ascii-string-into-a-int
|
|
64
|
+
cls_name = self.__class__.__name__
|
|
65
|
+
return int.from_bytes(cls_name.encode("utf-8"), byteorder="big", signed=False)
|
|
66
|
+
|
|
67
|
+
def __eq__(self, other: Any) -> bool:
|
|
68
|
+
try:
|
|
69
|
+
return hash(self) == hash(other)
|
|
70
|
+
except TypeError:
|
|
71
|
+
return False
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# Errors
|
|
3
|
+
*import* `ValidationError` **ABC**
|
|
4
|
+
|
|
5
|
+
*import* `CannotCreateArrayError` **ValidationError**
|
|
6
|
+
|
|
7
|
+
*import* `IncorrectDTypeError` **ValidationError**
|
|
8
|
+
|
|
9
|
+
*import* `CannotCoerceDTypeError` **ValidationError**
|
|
10
|
+
|
|
11
|
+
*import* `IncorrectAxNumberError` **ValidationError**
|
|
12
|
+
|
|
13
|
+
*import* `IncorrectAxSizesError` **ValidationError**
|
|
14
|
+
|
|
15
|
+
*import* `InvalidArrayValuesError` **ValidationError**
|
|
16
|
+
|
|
17
|
+
*import* `InvalidFieldValuesError` **ValidationError**
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Exceptions
|
|
21
|
+
*import* `ValidationException` **Exception**
|
|
22
|
+
|
|
23
|
+
*import* `CreateArrayException` **Exception**
|
|
24
|
+
|
|
25
|
+
*import* `CoerceDTypeException` **Exception**
|
|
26
|
+
|
|
27
|
+
# Other
|
|
28
|
+
*import* `ValidationErrorList` **UserList**
|
|
29
|
+
|
|
30
|
+
*import* `GenericErrors` **dataclass**
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
# pyright: reportUnusedImport=false
|
|
34
|
+
from .error_list import ValidationErrorList
|
|
35
|
+
from .exceptions import CoerceDTypeException, CreateArrayException, ValidationException
|
|
36
|
+
from .generic import GenericErrors
|
|
37
|
+
from .validation_errors.array_creation import CannotCreateArrayError
|
|
38
|
+
from .validation_errors.axes import IncorrectAxNumberError, IncorrectAxSizesError
|
|
39
|
+
from .validation_errors.base import ValidationError
|
|
40
|
+
from .validation_errors.dtype import CannotCoerceDTypeError, IncorrectDTypeError
|
|
41
|
+
from .validation_errors.values import InvalidArrayValuesError, InvalidFieldValuesError
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import UserList
|
|
4
|
+
from typing import Generic, Sequence, overload
|
|
5
|
+
|
|
6
|
+
from .validation_errors.base import SelectedValidationErrorT, ValidationErrorT
|
|
7
|
+
|
|
8
|
+
_ErrorTypes = (
|
|
9
|
+
type[SelectedValidationErrorT] | tuple[type[SelectedValidationErrorT], ...]
|
|
10
|
+
)
|
|
11
|
+
"""Error type or a tuple of error types.
|
|
12
|
+
For example `IncorrectAxSizesError` or `(IncorrectAxSizesError, IncorrectDTypeError)`"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ValidationErrorList(UserList[ValidationErrorT], Generic[ValidationErrorT]):
|
|
16
|
+
"""List of `ValidationError` objects.
|
|
17
|
+
|
|
18
|
+
Compared to built-in list, implements two methods:
|
|
19
|
+
- `raise_`/`raise_on_errors` - raises `ValidationException` if not empty.
|
|
20
|
+
- `__getitem__` - in addition to indexing by integer or slice can be filtered by type
|
|
21
|
+
or tuple of types of `ValidationError`
|
|
22
|
+
|
|
23
|
+
### Examples:
|
|
24
|
+
```
|
|
25
|
+
errs = ValidationErrorList(
|
|
26
|
+
[IncorrectAxSizesError(...), IncorrectDTypeError(...), InvalidFieldValuesError(...)]
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
err = errs[0]
|
|
30
|
+
>>> IncorrectAxSizesError(...)
|
|
31
|
+
|
|
32
|
+
sliced_errs = errs[:2]
|
|
33
|
+
>>> [IncorrectAxSizesError(...), IncorrectDTypeError(...)]
|
|
34
|
+
|
|
35
|
+
filtered_errs_type = errs[IncorrectAxSizesError]
|
|
36
|
+
>>> [IncorrectAxSizesError(...)]
|
|
37
|
+
|
|
38
|
+
filtered_errs_tuple = errs[(IncorrectAxSizesError, InvalidFieldValuesError)]
|
|
39
|
+
>>> [IncorrectAxSizesError(...), InvalidFieldValuesError(...)]
|
|
40
|
+
```
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, errs: Sequence[ValidationErrorT]):
|
|
44
|
+
super().__init__(errs)
|
|
45
|
+
|
|
46
|
+
def raise_(self):
|
|
47
|
+
"""Raises `ValidationException` if there are errors."""
|
|
48
|
+
# NOTE: scoped import to prevent circular import
|
|
49
|
+
# ValidationException -> ValidationErrorList -> ValidationException
|
|
50
|
+
from .exceptions import ValidationException
|
|
51
|
+
|
|
52
|
+
if self.data:
|
|
53
|
+
raise ValidationException(None, ValidationErrorList(self.data))
|
|
54
|
+
|
|
55
|
+
raise_on_errors = raise_
|
|
56
|
+
|
|
57
|
+
@overload
|
|
58
|
+
def __getitem__(self, i: int) -> ValidationErrorT: ...
|
|
59
|
+
|
|
60
|
+
@overload
|
|
61
|
+
def __getitem__(self, i: slice) -> ValidationErrorList[ValidationErrorT]: ...
|
|
62
|
+
|
|
63
|
+
@overload
|
|
64
|
+
def __getitem__(
|
|
65
|
+
self,
|
|
66
|
+
i: _ErrorTypes[SelectedValidationErrorT],
|
|
67
|
+
) -> ValidationErrorList[SelectedValidationErrorT]: ...
|
|
68
|
+
|
|
69
|
+
def __getitem__(
|
|
70
|
+
self,
|
|
71
|
+
i: int | slice | _ErrorTypes[SelectedValidationErrorT],
|
|
72
|
+
) -> (
|
|
73
|
+
ValidationErrorList[SelectedValidationErrorT]
|
|
74
|
+
| ValidationErrorList[ValidationErrorT]
|
|
75
|
+
| ValidationErrorT
|
|
76
|
+
):
|
|
77
|
+
if isinstance(i, (type, tuple)):
|
|
78
|
+
filtered_errs: list[SelectedValidationErrorT] = [
|
|
79
|
+
err for err in self.data if isinstance(err, i)
|
|
80
|
+
] # pyright: ignore[reportAssignmentType] | I don't know why pyright complains
|
|
81
|
+
items = ValidationErrorList(filtered_errs)
|
|
82
|
+
elif isinstance(i, slice):
|
|
83
|
+
items = type(self)(self.data[i])
|
|
84
|
+
else:
|
|
85
|
+
items = self.data[i]
|
|
86
|
+
|
|
87
|
+
return items
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from typing import Generic, Optional
|
|
2
|
+
|
|
3
|
+
from .error_list import ValidationErrorList
|
|
4
|
+
from .validation_errors.array_creation import CannotCreateArrayError
|
|
5
|
+
from .validation_errors.base import ValidationErrorT
|
|
6
|
+
from .validation_errors.dtype import CannotCoerceDTypeError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# TODO: ugh, typing issues, pls fix ->
|
|
10
|
+
# except ValidationException as exc:
|
|
11
|
+
# exc.errs -> 'Unknown'
|
|
12
|
+
class ValidationException(ValueError, Generic[ValidationErrorT]):
|
|
13
|
+
"""Exception caused by failed array validation.
|
|
14
|
+
|
|
15
|
+
Attributes:
|
|
16
|
+
cls_name (Optional[str]):
|
|
17
|
+
Optional name of ValidatedArray subclass that caused this exception.
|
|
18
|
+
errs (ValidationErrorList[ValidationError):
|
|
19
|
+
List of validation errors responsible.
|
|
20
|
+
|
|
21
|
+
(introduced ***v0.1***, last_modified ***v0.4***)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
cls_name: Optional[str],
|
|
27
|
+
errs: ValidationErrorList[ValidationErrorT],
|
|
28
|
+
*args: object,
|
|
29
|
+
) -> None:
|
|
30
|
+
|
|
31
|
+
super().__init__(*args)
|
|
32
|
+
|
|
33
|
+
self.cls_name = cls_name
|
|
34
|
+
self.errs = errs
|
|
35
|
+
"""List of validation errors."""
|
|
36
|
+
|
|
37
|
+
def __str__(self) -> str:
|
|
38
|
+
prefix = f"'{self.cls_name}' " if self.cls_name is not None else ""
|
|
39
|
+
err_msgs = "\n".join([e.msg for e in self.errs])
|
|
40
|
+
return f"{prefix}validation failed:\n" + err_msgs
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CreateArrayException(ValidationException):
|
|
44
|
+
"""Special case of `ValidationException`
|
|
45
|
+
on failure to create a validated array from generic data.
|
|
46
|
+
|
|
47
|
+
Attributes:
|
|
48
|
+
cls_name (str):
|
|
49
|
+
Name of ValidatedArray subclass that caused this exception.
|
|
50
|
+
errs (ValidationErrorList[CannotCreateArrayError]): List containing error responsible.
|
|
51
|
+
|
|
52
|
+
(introduced ***v0.1***, last_modified ***v0.4***)
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, cls_name: str) -> None:
|
|
56
|
+
super().__init__(
|
|
57
|
+
cls_name=cls_name, errs=ValidationErrorList([CannotCreateArrayError()])
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class CoerceDTypeException(ValidationException):
|
|
62
|
+
"""Special case of `ValidationException`
|
|
63
|
+
on failure to coerce the data type of an existing array.
|
|
64
|
+
|
|
65
|
+
Attributes:
|
|
66
|
+
cls_name (str):
|
|
67
|
+
Name of ValidatedArray subclass that caused this exception.
|
|
68
|
+
errs (ValidationErrorList[CannotCoerceDTypeError]):
|
|
69
|
+
List containing error with actual and target data type information.
|
|
70
|
+
|
|
71
|
+
(introduced ***v0.1***, last_modified ***v0.4***)
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(self, cls_name: str, err: CannotCoerceDTypeError) -> None:
|
|
75
|
+
super().__init__(cls_name=cls_name, errs=ValidationErrorList([err]))
|