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
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Generic
|
|
4
|
+
|
|
5
|
+
from valarray.core.types import ArrayIndicesT, ArrayT, DTypeLikeT, DTypeT
|
|
6
|
+
|
|
7
|
+
from .validation_errors.dtype import CannotCoerceDTypeError, IncorrectDTypeError
|
|
8
|
+
from .validation_errors.values import InvalidArrayValuesError, InvalidFieldValuesError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class GenericErrors(ABC, Generic[ArrayT, DTypeLikeT, DTypeT, ArrayIndicesT]):
|
|
13
|
+
"""Generic `ValidationError` classes to be used in `ArrayTypeAdapter`.
|
|
14
|
+
Subclass these for each array type with specific types and assign them to
|
|
15
|
+
a 'GenericErrors' subclass.
|
|
16
|
+
|
|
17
|
+
## Example:
|
|
18
|
+
```
|
|
19
|
+
class ChildIncorrectDTypeError(IncorrectDTypeError[DTypeLike, Dtype]): ...
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class ChildErrors(GenericErrors[DTypeLike, Dtype]):
|
|
23
|
+
incorrect_dtype: type[ChildIncorrectDTypeError] = ChildIncorrectDTypeError
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Necessary for correct type inference of errors accessed through a raised `ValidationException`.
|
|
28
|
+
|
|
29
|
+
### Good:
|
|
30
|
+
```
|
|
31
|
+
try:
|
|
32
|
+
...
|
|
33
|
+
except ValidationException as exc:
|
|
34
|
+
for err in exc.errs:
|
|
35
|
+
if isinstance(err, ChildIncorrectDTypeError):
|
|
36
|
+
# dtypes => Dtype, DTypeLike
|
|
37
|
+
print(err.actual_dtype, err.expected_dtypelike)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Bad:
|
|
41
|
+
```
|
|
42
|
+
try:
|
|
43
|
+
...
|
|
44
|
+
except ValidationException as exc:
|
|
45
|
+
for err in exc.errs:
|
|
46
|
+
if isinstance(err, IncorrectDTypeError):
|
|
47
|
+
# dtypes => Unknown
|
|
48
|
+
print(err.actual_dtype, err.expected_dtypelike)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
(introduced ***v0.2.4***)
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
incorrect_dtype: type[IncorrectDTypeError[DTypeLikeT, DTypeT]]
|
|
55
|
+
cannot_coerce_dtype: type[CannotCoerceDTypeError[DTypeLikeT, DTypeT]]
|
|
56
|
+
invalid_array_values: type[InvalidArrayValuesError[ArrayT, ArrayIndicesT]]
|
|
57
|
+
invalid_field_values: type[InvalidFieldValuesError[ArrayT, ArrayIndicesT]]
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .base import ValidationError
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# [?] What information should this error contain, if any?
|
|
5
|
+
class CannotCreateArrayError(ValidationError):
|
|
6
|
+
"""Error caused by array type adapter not being able to create an array of specified data type
|
|
7
|
+
from supplied object.
|
|
8
|
+
|
|
9
|
+
(introduced ***v0.1***)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def msg(self) -> str:
|
|
14
|
+
return "Cannot create array."
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from valarray.core.types import AxSizes
|
|
4
|
+
|
|
5
|
+
from .base import ValidationError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class IncorrectAxNumberError(ValidationError):
|
|
10
|
+
"""***Validation error:*** Array has incorrect number of axes.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
actual_axis_number (int): Number of axes of the actual array.
|
|
14
|
+
expected_axis_number (int): Number of axes inferred from schema.
|
|
15
|
+
|
|
16
|
+
(introduced ***v0.2***)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
actual_axis_number: int
|
|
20
|
+
expected_axis_number: int
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def msg(self) -> str:
|
|
24
|
+
act = self.actual_axis_number
|
|
25
|
+
exp = self.expected_axis_number
|
|
26
|
+
return f"Incorrect number of axes: {act}, expected {exp}."
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class IncorrectAxSizesError(ValidationError):
|
|
31
|
+
"""***Validation error:*** Array has ax(es) with incorrect size(s).
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
actual_sizes (tuple[int, ...]): Ax sizes of the actual array.
|
|
35
|
+
expected_sizes (AxSizes):
|
|
36
|
+
Expected ax sizes (or None for variable sized axis) inferred from schema.
|
|
37
|
+
incorrect_size_indices (list[int]): Indices of which axes have incorrect sizes.
|
|
38
|
+
|
|
39
|
+
Examples:
|
|
40
|
+
```
|
|
41
|
+
err = IncorrectAxSizesError(
|
|
42
|
+
(1, 2, 3, 4), [2, None, 1, 4], incorrect_size_indices=[0, 2]
|
|
43
|
+
),
|
|
44
|
+
print(err.msg)
|
|
45
|
+
>>> "Incorrect axis sizes: (*1*, 2, *3*, 4), expected (2, any, 1, 4)."
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
(introduced ***v0.2***, last_modified ***v0.3***)
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
actual_sizes: tuple[int, ...]
|
|
52
|
+
expected_sizes: AxSizes
|
|
53
|
+
incorrect_size_indices: list[int]
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def msg(self) -> str:
|
|
57
|
+
act_sizes_str_list = [
|
|
58
|
+
f"*{s}*" if i in self.incorrect_size_indices else str(s)
|
|
59
|
+
for i, s in enumerate(self.actual_sizes)
|
|
60
|
+
]
|
|
61
|
+
act_size_str = "(" + ", ".join(act_sizes_str_list) + ")"
|
|
62
|
+
|
|
63
|
+
exp_sizes_str_list = [
|
|
64
|
+
str(a) if a is not None else "any" for a in self.expected_sizes
|
|
65
|
+
]
|
|
66
|
+
exp_size_str = "(" + ", ".join(exp_sizes_str_list) + ")"
|
|
67
|
+
|
|
68
|
+
return f"Incorrect axis sizes: {act_size_str}, expected {exp_size_str}."
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import TypeVar
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ValidationError(ABC):
|
|
6
|
+
"""Base validation error. Subclasses need to implement:
|
|
7
|
+
|
|
8
|
+
**abstract property**
|
|
9
|
+
- `msg` -
|
|
10
|
+
Human readeble description of error.
|
|
11
|
+
|
|
12
|
+
(introduced ***v0.1***)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def msg(self) -> str: ...
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
ValidationErrorT = TypeVar("ValidationErrorT", bound=ValidationError)
|
|
21
|
+
"""Validation error types contained in the error list.
|
|
22
|
+
(introduced ***v0.4***)"""
|
|
23
|
+
|
|
24
|
+
SelectedValidationErrorT = TypeVar("SelectedValidationErrorT", bound=ValidationError)
|
|
25
|
+
"""Validation error types to be selected from the error list.
|
|
26
|
+
(introduced ***v0.4***)"""
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Generic
|
|
3
|
+
|
|
4
|
+
from valarray.core.types import DTypeLikeT, DTypeT
|
|
5
|
+
|
|
6
|
+
from .base import ValidationError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _dtype_str(dtype, dtypelike):
|
|
10
|
+
"""`"dtype"`` OR `"dtypelike (dtype)"``"""
|
|
11
|
+
return str(dtype) if dtype == dtypelike else f"{dtypelike} ({dtype})"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class IncorrectDTypeError(ValidationError, Generic[DTypeLikeT, DTypeT]):
|
|
16
|
+
"""***Validation error:*** Array has the wrong data type.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
actual_dtype (DTypeT): Data type of the actual array.
|
|
20
|
+
expected_dtype (DTypeT): Expected data type (as a data type object).
|
|
21
|
+
expected_dtypelike (DTypeLikeT):
|
|
22
|
+
Expected data type specification from `ValidatedArray.dtype`.
|
|
23
|
+
|
|
24
|
+
(introduced ***v0.1***, last_modified ***v0.2.4***)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
actual_dtype: DTypeT
|
|
28
|
+
expected_dtype: DTypeT
|
|
29
|
+
expected_dtypelike: DTypeLikeT
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def msg(self) -> str:
|
|
33
|
+
e = _dtype_str(self.expected_dtype, self.expected_dtypelike)
|
|
34
|
+
|
|
35
|
+
return f"Incorrect DType: '{self.actual_dtype}', expected '{e}'."
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class CannotCoerceDTypeError(ValidationError, Generic[DTypeLikeT, DTypeT]):
|
|
40
|
+
"""***Validation error:*** Array type adapter is not able to convert an existing array
|
|
41
|
+
to specified data type when creating an instance of ValidatedArray.
|
|
42
|
+
|
|
43
|
+
Attributes:
|
|
44
|
+
actual_dtype (DTypeT): Data type of array passed to `ValidatedArray.__init__`.
|
|
45
|
+
target_dtype (DTypeT): Data type to convert to (as a data type object).
|
|
46
|
+
target_dtypelike (DTypeLikeT):
|
|
47
|
+
Expected data type specification from `ValidatedArray.dtype`.
|
|
48
|
+
|
|
49
|
+
(introduced ***v0.1***, last_modified ***v0.2.4***)
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
actual_dtype: DTypeT
|
|
53
|
+
target_dtype: DTypeT
|
|
54
|
+
target_dtypelike: DTypeLikeT
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def msg(self) -> str:
|
|
58
|
+
e = _dtype_str(self.target_dtype, self.target_dtypelike)
|
|
59
|
+
|
|
60
|
+
return f"Cannot coerce data type from: '{self.actual_dtype}' to '{e}'."
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, Generic, Optional
|
|
3
|
+
|
|
4
|
+
from valarray.core.axes_and_fields import AxisNameAndIdx, FieldNameAndIdx
|
|
5
|
+
from valarray.core.types import ArrayIndicesT, ArrayT
|
|
6
|
+
from valarray.core.validators.base import Validator
|
|
7
|
+
|
|
8
|
+
from .base import ValidationError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class _InvalidValuesError(ValidationError, Generic[ArrayT, ArrayIndicesT]):
|
|
13
|
+
validator: Validator[ArrayT, ArrayIndicesT]
|
|
14
|
+
# TODO: add extra
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class InvalidArrayValuesError(_InvalidValuesError[ArrayT, ArrayIndicesT]):
|
|
19
|
+
"""***ValidationError:*** Array has wrong values.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
validator: (Validator[ArrayT, ArrayIndicesT]): Validator applied to the whole array.
|
|
23
|
+
invalid_values (Optional[list[Any]]):
|
|
24
|
+
Values causing the validation to fail. Defaults to None.
|
|
25
|
+
invalid_indices (Optional[ArrayIndicesT]): Indices of invalid values.
|
|
26
|
+
result_msg (Optional[str]): Optional message returned by validator.
|
|
27
|
+
|
|
28
|
+
(introduced ***v0.3***, last_modified ***v0.4***)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
invalid_values: Optional[list[Any]] = None
|
|
32
|
+
invalid_indices: Optional[ArrayIndicesT] = None
|
|
33
|
+
result_msg: Optional[str] = None
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def msg(self) -> str:
|
|
37
|
+
validator_desc = str(self.validator)
|
|
38
|
+
|
|
39
|
+
header = f"Invalid Array Values ({validator_desc})"
|
|
40
|
+
|
|
41
|
+
if self.invalid_values is None and self.result_msg is None:
|
|
42
|
+
return f"{header}."
|
|
43
|
+
|
|
44
|
+
lines = []
|
|
45
|
+
|
|
46
|
+
if self.invalid_values is not None:
|
|
47
|
+
lines.append(f"\t{self.invalid_values}")
|
|
48
|
+
|
|
49
|
+
if self.result_msg is not None:
|
|
50
|
+
lines.append(f"\t{self.result_msg}")
|
|
51
|
+
|
|
52
|
+
return header + ":\n" + "\n".join(lines)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
type InvalidFieldValues = dict[AxisNameAndIdx, dict[FieldNameAndIdx, Any]]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _invalid_field_values_to_lines(ifv: InvalidFieldValues) -> list[str]:
|
|
59
|
+
lines: list[str] = []
|
|
60
|
+
for ax, fld_dict in ifv.items():
|
|
61
|
+
ax_name_str = f": '{ax.name}'" if ax.name is not None else ""
|
|
62
|
+
|
|
63
|
+
lines.append(f"\tAxis < {ax.idx} >{ax_name_str}")
|
|
64
|
+
|
|
65
|
+
for fld, invalid_values in fld_dict.items():
|
|
66
|
+
fld_name_str = f": '{fld.name}'" if fld.name is not None else ""
|
|
67
|
+
lines.append(f"\t\tField < {fld.idx} >{fld_name_str}: {invalid_values}")
|
|
68
|
+
|
|
69
|
+
return lines
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class InvalidFieldValuesError(_InvalidValuesError[ArrayT, ArrayIndicesT]):
|
|
74
|
+
"""***ValidationError:*** Field or fields have wrong values.
|
|
75
|
+
|
|
76
|
+
Attributes:
|
|
77
|
+
validator: (Validator[ArrayT, ArrayIndicesT]): Validator applied to array field(s).
|
|
78
|
+
invalid_values (Optional[InvalidFieldValues]):
|
|
79
|
+
Values causing the validation to fail, organized by axis and field. Defaults to None.
|
|
80
|
+
|
|
81
|
+
(introduced ***v0.4***)
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
invalid_values: Optional[InvalidFieldValues] = None
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def msg(self) -> str:
|
|
88
|
+
validator_desc = str(self.validator)
|
|
89
|
+
|
|
90
|
+
header = f"Invalid Field Values ({validator_desc})"
|
|
91
|
+
|
|
92
|
+
if self.invalid_values is None:
|
|
93
|
+
return f"{header}."
|
|
94
|
+
|
|
95
|
+
lines = []
|
|
96
|
+
|
|
97
|
+
lines.extend(_invalid_field_values_to_lines(self.invalid_values))
|
|
98
|
+
|
|
99
|
+
return header + ":\n" + "\n".join(lines)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
## Generics
|
|
3
|
+
*import* `DTypeLikeT` **TypeVar**
|
|
4
|
+
|
|
5
|
+
*import* `DTypeT` **TypeVar**
|
|
6
|
+
|
|
7
|
+
*import* `ArrayT` **TypeVar**
|
|
8
|
+
|
|
9
|
+
*import* `ArrayIndicesT` **TypeVar**
|
|
10
|
+
|
|
11
|
+
*import* `ComparableValueT` **TypeVar**
|
|
12
|
+
|
|
13
|
+
## Other
|
|
14
|
+
*import* `ArrayP` **Protocol**
|
|
15
|
+
|
|
16
|
+
*import* `ComparisonOperator` **type**
|
|
17
|
+
|
|
18
|
+
*import* `AxSizes` **type**
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from .generics import ArrayIndicesT, ArrayT, ComparableValueT, DTypeLikeT, DTypeT
|
|
22
|
+
from .other import ArrayP, AxSizes, ComparisonOperator
|
|
23
|
+
|
|
24
|
+
# pyright: reportUnusedImport=false
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import TypeVar
|
|
2
|
+
|
|
3
|
+
from .other import ArrayP
|
|
4
|
+
|
|
5
|
+
# NOTE: using old-school generics because I'm not familiar with the new syntax
|
|
6
|
+
# and I don't know how to customize syntax highligting for it in VSCode.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
DTypeLikeT = TypeVar("DTypeLikeT")
|
|
10
|
+
"""Data type object or object that can be used to construct a data type object.
|
|
11
|
+
```
|
|
12
|
+
dtypelike = "float32"
|
|
13
|
+
dtypelike = float
|
|
14
|
+
dtypelike = Float32()
|
|
15
|
+
|
|
16
|
+
dt = dtype(dtypelike)
|
|
17
|
+
dt == Float32()
|
|
18
|
+
>>> True
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
(introduced ***v0.1***)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
DTypeT = TypeVar("DTypeT")
|
|
25
|
+
"""(introduced ***v0.1***)"""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
ArrayT = TypeVar("ArrayT", bound=ArrayP)
|
|
29
|
+
"""Generic array object conforming to `ArrayP` protocol.
|
|
30
|
+
(introduced ***v0.1***)"""
|
|
31
|
+
|
|
32
|
+
ArrayIndicesT = TypeVar("ArrayIndicesT")
|
|
33
|
+
"""Object that can select values from array using `__getitem__` method.
|
|
34
|
+
(introduced ***v0.3***)
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
ComparableValueT = TypeVar("ComparableValueT")
|
|
38
|
+
"""Value or object an array can be compared with using `<`, `<=`, `==`, `=>`, `>` operators.
|
|
39
|
+
(introduced ***v0.3***)
|
|
40
|
+
"""
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from typing import Any, Literal, Protocol
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ArrayP(Protocol):
|
|
6
|
+
"""(introduced ***v0.1***, last_modified ***v0.2***)"""
|
|
7
|
+
|
|
8
|
+
@property
|
|
9
|
+
def dtype(self) -> Any: ...
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def shape(self) -> tuple[int, ...]: ...
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
type ComparisonOperator = Literal["lt", "le", "ge", "gt", "eq"]
|
|
16
|
+
"""(introduced ***v0.3***)"""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
type AxSizes = Sequence[int | None]
|
|
20
|
+
"""Array sizes from schema or 'None' if unknown.
|
|
21
|
+
|
|
22
|
+
(introduced ***v0.1.1***, last_modified ***v0.4***)
|
|
23
|
+
"""
|
valarray/core/utils.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""
|
|
2
|
+
*import* `setup_array` **function**
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from types import EllipsisType
|
|
6
|
+
from typing import Any, Optional
|
|
7
|
+
|
|
8
|
+
from .array_type_adapter import ArrayTypeAdapter
|
|
9
|
+
from .axes_and_fields import AxesTuple
|
|
10
|
+
from .comparisons import Comparisons
|
|
11
|
+
from .errors_exceptions import CoerceDTypeException, CreateArrayException
|
|
12
|
+
from .types import ArrayIndicesT, ArrayT, AxSizes, ComparableValueT, DTypeLikeT
|
|
13
|
+
from .validators import ComparisonValidator
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def ax_sizes_from_schema(schema: AxesTuple[Any, Any, Any]) -> AxSizes:
|
|
17
|
+
"""(introduced ***v0.1.1***)"""
|
|
18
|
+
sizes: AxSizes = []
|
|
19
|
+
|
|
20
|
+
for ax in schema:
|
|
21
|
+
if isinstance(ax, int):
|
|
22
|
+
sizes.append(ax)
|
|
23
|
+
elif isinstance(ax, str):
|
|
24
|
+
sizes.append(None)
|
|
25
|
+
else:
|
|
26
|
+
sizes.append(len(ax))
|
|
27
|
+
|
|
28
|
+
return sizes
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def constraints_to_validators(
|
|
32
|
+
lt: Optional[ComparableValueT],
|
|
33
|
+
le: Optional[ComparableValueT],
|
|
34
|
+
ge: Optional[ComparableValueT],
|
|
35
|
+
gt: Optional[ComparableValueT],
|
|
36
|
+
eq: Optional[ComparableValueT],
|
|
37
|
+
comparisons: Comparisons[ArrayT, ArrayIndicesT, ComparableValueT],
|
|
38
|
+
) -> list[ComparisonValidator[ArrayT, ArrayIndicesT, ComparableValueT]]:
|
|
39
|
+
"""(introduced ***v0.3***)"""
|
|
40
|
+
validators = []
|
|
41
|
+
|
|
42
|
+
for val, operator in zip((lt, le, ge, gt, eq), ("lt", "le", "ge", "gt", "eq")):
|
|
43
|
+
if val is not None:
|
|
44
|
+
validators.append(ComparisonValidator(operator, val, comparisons))
|
|
45
|
+
|
|
46
|
+
return validators
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def setup_array(
|
|
50
|
+
obj: Any,
|
|
51
|
+
dtypelike: DTypeLikeT | EllipsisType,
|
|
52
|
+
coerce_dtype: Optional[bool],
|
|
53
|
+
schema: AxesTuple[Any, Any, Any] | EllipsisType,
|
|
54
|
+
ata: ArrayTypeAdapter[ArrayT, DTypeLikeT, Any, Any, Any],
|
|
55
|
+
cls_name: str,
|
|
56
|
+
) -> ArrayT:
|
|
57
|
+
"""Creates array if it already doesn't exist. Speciffically if:
|
|
58
|
+
- `obj` is an array AND
|
|
59
|
+
- `coerce_dtype` is `False` OR `dtypelike == ...` -> return `obj`
|
|
60
|
+
- `coerce_dtype` is `True` -> try to convert dtype,
|
|
61
|
+
throw `CannotCoerceDTypeException` on fail.
|
|
62
|
+
- `obj` is `None` -> create an empty array
|
|
63
|
+
- `obj` is any other object -> try to create array, throw `CreateArrayException` on fail.
|
|
64
|
+
|
|
65
|
+
(introduced ***v0.1***, last_modified ***v0.2.4***)
|
|
66
|
+
"""
|
|
67
|
+
if isinstance(obj, ata.array_type):
|
|
68
|
+
if coerce_dtype is False or dtypelike == ...:
|
|
69
|
+
arr = obj
|
|
70
|
+
else:
|
|
71
|
+
try:
|
|
72
|
+
arr = ata.change_dtype(obj, dtypelike)
|
|
73
|
+
except Exception as exc:
|
|
74
|
+
raise CoerceDTypeException(
|
|
75
|
+
cls_name,
|
|
76
|
+
ata.errors.cannot_coerce_dtype(
|
|
77
|
+
obj.dtype, ata.dtype_from_dtypelike(dtypelike), dtypelike
|
|
78
|
+
),
|
|
79
|
+
) from exc
|
|
80
|
+
elif obj is None:
|
|
81
|
+
ax_sizes = ax_sizes_from_schema(schema if schema != ... else tuple())
|
|
82
|
+
arr = ata.create_empty_array(ax_sizes, dtypelike)
|
|
83
|
+
else:
|
|
84
|
+
try:
|
|
85
|
+
arr = ata.create_array(obj, dtypelike)
|
|
86
|
+
except Exception as exc:
|
|
87
|
+
raise CreateArrayException(cls_name) from exc
|
|
88
|
+
|
|
89
|
+
return arr
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
*import* `validate_array` **function**
|
|
3
|
+
|
|
4
|
+
*import* `validate_dtype` **function**
|
|
5
|
+
|
|
6
|
+
*import* `validate_shape` **function**
|
|
7
|
+
|
|
8
|
+
*import* `validate_array_values` **function**
|
|
9
|
+
|
|
10
|
+
*import* `validate_field_values` **function**
|
|
11
|
+
|
|
12
|
+
`.utils` ***module***
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# pyright: reportUnusedImport=false
|
|
16
|
+
from .array import validate_array
|
|
17
|
+
from .array_values import validate_array_values
|
|
18
|
+
from .dtype import validate_dtype
|
|
19
|
+
from .field_values.core import validate_field_values
|
|
20
|
+
from .shape import validate_shape
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from types import EllipsisType
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from valarray.core.array_type_adapter import ArrayTypeAdapter
|
|
6
|
+
from valarray.core.axes_and_fields import AxesTuple
|
|
7
|
+
from valarray.core.errors_exceptions import ValidationError, ValidationErrorList
|
|
8
|
+
from valarray.core.types import (
|
|
9
|
+
ArrayIndicesT,
|
|
10
|
+
ArrayT,
|
|
11
|
+
ComparableValueT,
|
|
12
|
+
DTypeLikeT,
|
|
13
|
+
DTypeT,
|
|
14
|
+
)
|
|
15
|
+
from valarray.core.validators import Validator
|
|
16
|
+
|
|
17
|
+
from .array_values import validate_array_values
|
|
18
|
+
from .dtype import validate_dtype
|
|
19
|
+
from .field_values.core import validate_field_values
|
|
20
|
+
from .shape import validate_shape
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def validate_array(
|
|
24
|
+
arr: ArrayT,
|
|
25
|
+
dtypelike: DTypeLikeT | EllipsisType,
|
|
26
|
+
schema: AxesTuple[Any, Any, Any] | EllipsisType,
|
|
27
|
+
validators: Sequence[Validator[ArrayT, ArrayIndicesT]],
|
|
28
|
+
ata: ArrayTypeAdapter[ArrayT, DTypeLikeT, DTypeT, ArrayIndicesT, ComparableValueT],
|
|
29
|
+
) -> ValidationErrorList[ValidationError]:
|
|
30
|
+
"""Validate array:
|
|
31
|
+
- data type
|
|
32
|
+
- shape (based on array schema)
|
|
33
|
+
- array values (using a sequence of validators)
|
|
34
|
+
- field values (based on validators defined in array schema)
|
|
35
|
+
|
|
36
|
+
First validate data type and shape. If those are correct, validate field and array values.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
arr (ArrayT): Array to be validated.
|
|
40
|
+
dtypelike (DTypeLikeT | EllipsisType): Expected datatype (if `...` , do not validate).
|
|
41
|
+
schema (AxesTuple | EllipsisType): Expected array schema (if `...` , do not validate).
|
|
42
|
+
validators (Sequence[Validator[ArrayT]]): Validators to apply.
|
|
43
|
+
ata (ArrayTypeAdapter[ArrayT, DTypeLikeT, DTypeT, ArrayIndicesT, ComparableValueT]):
|
|
44
|
+
Adapter for specific array type.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
ValidationErrorList[ValidationError]: Validation errors.
|
|
48
|
+
|
|
49
|
+
(introduced ***v0.4***)
|
|
50
|
+
"""
|
|
51
|
+
errs: ValidationErrorList[ValidationError] = ValidationErrorList([])
|
|
52
|
+
|
|
53
|
+
errs += validate_dtype(arr, dtypelike, ata)
|
|
54
|
+
errs += validate_shape(arr, schema)
|
|
55
|
+
|
|
56
|
+
if errs:
|
|
57
|
+
return errs
|
|
58
|
+
|
|
59
|
+
errs += validate_array_values(arr, validators, ata)
|
|
60
|
+
errs += validate_field_values(arr, schema, ata)
|
|
61
|
+
|
|
62
|
+
return errs
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from valarray.core.array_type_adapter import ArrayTypeAdapter
|
|
5
|
+
from valarray.core.errors_exceptions import InvalidArrayValuesError, ValidationErrorList
|
|
6
|
+
from valarray.core.types import ArrayIndicesT, ArrayT
|
|
7
|
+
from valarray.core.validation_functions.utils import apply_validator
|
|
8
|
+
from valarray.core.validators.base import Validator
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def validate_array_values(
|
|
12
|
+
arr: ArrayT,
|
|
13
|
+
validators: Sequence[Validator[ArrayT, ArrayIndicesT]],
|
|
14
|
+
ata: ArrayTypeAdapter[ArrayT, Any, Any, ArrayIndicesT, Any],
|
|
15
|
+
) -> ValidationErrorList[InvalidArrayValuesError[ArrayT, ArrayIndicesT]]:
|
|
16
|
+
"""Validate array with a sequence of validators applied to the whole array
|
|
17
|
+
and return potential errors.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
arr (ArrayT): Array to be validated.
|
|
21
|
+
validators (Sequence[Validator[ArrayT]]): Validators to apply.
|
|
22
|
+
ata (ArrayTypeAdapter[ArrayT, Any, Any, ArrayIndicesT, Any]):
|
|
23
|
+
Adapter for specific array type.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
errs (ValidationErrorList[InvalidArrayValuesError[ArrayT, ArrayIndicesT]]):
|
|
27
|
+
List containing at most 1 error per validator.
|
|
28
|
+
|
|
29
|
+
(introduced ***v0.3***, last_modified ***v0.4***)
|
|
30
|
+
"""
|
|
31
|
+
errs: ValidationErrorList[InvalidArrayValuesError[ArrayT, ArrayIndicesT]] = (
|
|
32
|
+
ValidationErrorList([])
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
for validator in validators:
|
|
36
|
+
res = apply_validator(validator, arr)
|
|
37
|
+
|
|
38
|
+
if res.status == "FAIL":
|
|
39
|
+
invalid_values = ata.get_values(arr, res.indices_invalid)
|
|
40
|
+
|
|
41
|
+
errs.append(
|
|
42
|
+
ata.errors.invalid_array_values(
|
|
43
|
+
validator, invalid_values, res.indices_invalid, res.msg
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return errs
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from types import EllipsisType
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from valarray.core.array_type_adapter import ArrayTypeAdapter
|
|
5
|
+
from valarray.core.errors_exceptions import IncorrectDTypeError, ValidationErrorList
|
|
6
|
+
from valarray.core.types import ArrayT, DTypeLikeT, DTypeT
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def validate_dtype(
|
|
10
|
+
arr: ArrayT,
|
|
11
|
+
dtypelike: DTypeLikeT | EllipsisType,
|
|
12
|
+
ata: ArrayTypeAdapter[ArrayT, DTypeLikeT, DTypeT, Any, Any],
|
|
13
|
+
) -> ValidationErrorList[IncorrectDTypeError[DTypeLikeT, DTypeT]]:
|
|
14
|
+
"""Validate that array has the correct dtype.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
arr (ArrayT): Array to be validated.
|
|
18
|
+
dtypelike (DTypeLikeT | EllipsisType): Expected datatype (if `...` , do not validate).
|
|
19
|
+
ata (ArrayTypeAdapter[ArrayT, DTypeLikeT, DTypeT, ...]): Adapter for specific array type.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
errs (ValidationErrorList[IncorrectDTypeError]):
|
|
23
|
+
list containing a single dtype error or an empty list.
|
|
24
|
+
|
|
25
|
+
(introduced ***v0.1***, last_modified ***v0.4***)
|
|
26
|
+
"""
|
|
27
|
+
errs: ValidationErrorList[IncorrectDTypeError[DTypeLikeT, DTypeT]] = (
|
|
28
|
+
ValidationErrorList([])
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if dtypelike == ...:
|
|
32
|
+
return errs
|
|
33
|
+
|
|
34
|
+
expected_dtype = ata.dtype_from_dtypelike(dtypelike)
|
|
35
|
+
if not ata.dtypes_equal(arr.dtype, expected_dtype):
|
|
36
|
+
errs.append(
|
|
37
|
+
ata.errors.incorrect_dtype(
|
|
38
|
+
actual_dtype=arr.dtype,
|
|
39
|
+
expected_dtype=expected_dtype,
|
|
40
|
+
expected_dtypelike=dtypelike,
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return errs
|
|
File without changes
|