typingkit 0.2.2__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.
@@ -0,0 +1,248 @@
1
+ """
2
+ Helpers for TypedNDArray
3
+ =======
4
+ """
5
+ # src/typingkit/_typed/helpers.py
6
+
7
+ from typing import Literal, TypeAlias, TypeVar
8
+
9
+ import numpy as np
10
+
11
+ from typingkit._typed.ndarray import TypedNDArray
12
+
13
+ ## Helpers
14
+
15
+ # Literal type aliases for small integers
16
+ ZERO: TypeAlias = Literal[0]
17
+ """Literal type for the integer `0`."""
18
+ ONE: TypeAlias = Literal[1]
19
+ """Literal type for the integer `1`."""
20
+ TWO: TypeAlias = Literal[2]
21
+ """Literal type for the integer `2`."""
22
+ THREE: TypeAlias = Literal[3]
23
+ """Literal type for the integer `3`."""
24
+ FOUR: TypeAlias = Literal[4]
25
+ """Literal type for the integer `4`."""
26
+ FIVE: TypeAlias = Literal[5]
27
+ """Literal type for the integer `5`."""
28
+ SIX: TypeAlias = Literal[6]
29
+ """Literal type for the integer `6`."""
30
+ SEVEN: TypeAlias = Literal[7]
31
+ """Literal type for the integer `7`."""
32
+ EIGHT: TypeAlias = Literal[8]
33
+ """Literal type for the integer `8`."""
34
+ NINE: TypeAlias = Literal[9]
35
+ """Literal type for the integer `9`."""
36
+ TEN: TypeAlias = Literal[10]
37
+ """Literal type for the integer `10`."""
38
+
39
+ # TypeVars
40
+ Dim1 = TypeVar("Dim1", bound=int, default=int)
41
+ Dim2 = TypeVar("Dim2", bound=int, default=int)
42
+ Dim3 = TypeVar("Dim3", bound=int, default=int)
43
+ Dim4 = TypeVar("Dim4", bound=int, default=int)
44
+ K = TypeVar("K", bound=int, default=int)
45
+ L = TypeVar("L", bound=int, default=int)
46
+ M = TypeVar("M", bound=int, default=int)
47
+ N = TypeVar("N", bound=int, default=int)
48
+
49
+ # Shape type aliases
50
+ Shape0D: TypeAlias = tuple[()]
51
+ """A tuple representing a 0D shape, i.e., `()`."""
52
+ Shape1D: TypeAlias = tuple[Dim1]
53
+ """A tuple representing a 1D shape, i.e., `(N,)`."""
54
+ Shape2D: TypeAlias = tuple[Dim1, Dim2]
55
+ """A tuple representing a 2D shape, i.e., `(M, N)`."""
56
+ Shape3D: TypeAlias = tuple[Dim1, Dim2, Dim3]
57
+ """A tuple representing a 3D shape, i.e., shape `(L, M, N)`."""
58
+ Shape4D: TypeAlias = tuple[Dim1, Dim2, Dim3, Dim4]
59
+ """A tuple representing a 4D shape, i.e., shape `(K, L, M, N)`."""
60
+ ShapeND: TypeAlias = tuple[int, ...]
61
+ """A tuple representing a ND shape, i.e., shape `(N, ...)`."""
62
+
63
+ # DType aliases
64
+ DType = TypeVar("DType", bound=np.dtype, default=np.dtype, covariant=True)
65
+
66
+
67
+ ## Array type aliases
68
+
69
+ # Arrays based on dimensionality
70
+ Array0D: TypeAlias = TypedNDArray[Shape0D, DType]
71
+ """A `numpy.ndarray` of shape `()` and dtype `DType`."""
72
+ Array1D: TypeAlias = TypedNDArray[Shape1D[Dim1], DType]
73
+ """A `numpy.ndarray` of shape `(N,)` and dtype `DType`."""
74
+ Array2D: TypeAlias = TypedNDArray[Shape2D[Dim1, Dim2], DType]
75
+ """A `numpy.ndarray` of shape `(M, N)` and dtype `DType`."""
76
+ Array3D: TypeAlias = TypedNDArray[Shape3D[Dim1, Dim2, Dim3], DType]
77
+ """A `numpy.ndarray` of shape `(L, M, N)` and dtype `DType`."""
78
+ Array4D: TypeAlias = TypedNDArray[Shape4D[Dim1, Dim2, Dim3, Dim4], DType]
79
+ """A `numpy.ndarray` of shape `(K, L, M, N)` and dtype `DType`."""
80
+ ArrayND: TypeAlias = TypedNDArray[ShapeND, DType]
81
+ """A `numpy.ndarray` of shape `(N, ...)` and dtype `DType`."""
82
+
83
+ # 1D Arrays with specific small sizes
84
+ Array2: TypeAlias = TypedNDArray[tuple[TWO], DType]
85
+ """A `numpy.ndarray` of shape `(2,)` and dtype `DType`."""
86
+ Array3: TypeAlias = TypedNDArray[tuple[THREE], DType]
87
+ """A `numpy.ndarray` of shape `(3,)` and dtype `DType`."""
88
+ Array4: TypeAlias = TypedNDArray[tuple[FOUR], DType]
89
+ """A `numpy.ndarray` of shape `(4,)` and dtype `DType`."""
90
+ ArrayN: TypeAlias = TypedNDArray[tuple[N], DType] # Same as Array1D
91
+ """A `numpy.ndarray` of shape `(N,)` and dtype `DType`."""
92
+
93
+ # 2D Arrays with specific small sizes
94
+ Array2x2: TypeAlias = TypedNDArray[tuple[TWO, TWO], DType]
95
+ """A `numpy.ndarray` of shape `(2, 2)` and dtype `DType`."""
96
+ Array2x3: TypeAlias = TypedNDArray[tuple[TWO, THREE], DType]
97
+ """A `numpy.ndarray` of shape `(2, 3)` and dtype `DType`."""
98
+ Array2x4: TypeAlias = TypedNDArray[tuple[TWO, FOUR], DType]
99
+ """A `numpy.ndarray` of shape `(2, 4)` and dtype `DType`."""
100
+ Array2xN: TypeAlias = TypedNDArray[tuple[TWO, N], DType]
101
+ """A `numpy.ndarray` of shape `(2, N)` and dtype `DType`."""
102
+
103
+ Array3x2: TypeAlias = TypedNDArray[tuple[THREE, TWO], DType]
104
+ """A `numpy.ndarray` of shape `(3, 2)` and dtype `DType`."""
105
+ Array3x3: TypeAlias = TypedNDArray[tuple[THREE, THREE], DType]
106
+ """A `numpy.ndarray` of shape `(3, 3)` and dtype `DType`."""
107
+ Array3x4: TypeAlias = TypedNDArray[tuple[THREE, FOUR], DType]
108
+ """A `numpy.ndarray` of shape `(3, 4)` with the default `dtype."""
109
+ Array3xN: TypeAlias = TypedNDArray[tuple[THREE, N], DType]
110
+ """A `numpy.ndarray` of shape `(3, N)` and dtype `DType`."""
111
+
112
+ Array4x2: TypeAlias = TypedNDArray[tuple[FOUR, TWO], DType]
113
+ """A `numpy.ndarray` of shape `(4, 2)` and dtype `DType`."""
114
+ Array4x3: TypeAlias = TypedNDArray[tuple[FOUR, THREE], DType]
115
+ """A `numpy.ndarray` of shape `(4, 3)` and dtype `DType`."""
116
+ Array4x4: TypeAlias = TypedNDArray[tuple[FOUR, FOUR], DType]
117
+ """A `numpy.ndarray` of shape `(4, 4)` with the default `dtype."""
118
+ Array4xN: TypeAlias = TypedNDArray[tuple[FOUR, N], DType]
119
+ """A `numpy.ndarray` of shape `(4, N)` and dtype `DType`."""
120
+
121
+ ArrayNx2: TypeAlias = TypedNDArray[tuple[N, TWO], DType]
122
+ """A `numpy.ndarray` of shape `(N, 2)` and dtype `DType`."""
123
+ ArrayNx3: TypeAlias = TypedNDArray[tuple[N, THREE], DType]
124
+ """A `numpy.ndarray` of shape `(N, 3)` and dtype `DType`."""
125
+ ArrayNx4: TypeAlias = TypedNDArray[tuple[N, FOUR], DType]
126
+ """A `numpy.ndarray` of shape `(N, 4)` and dtype `DType`."""
127
+
128
+ # 3D Arrays with specific small sizes
129
+ Array2x2x2: TypeAlias = TypedNDArray[tuple[TWO, TWO, TWO], DType]
130
+ """A `numpy.ndarray` of shape `(2, 2, 2)` and dtype `DType`."""
131
+ Array2x2x3: TypeAlias = TypedNDArray[tuple[TWO, TWO, THREE], DType]
132
+ """A `numpy.ndarray` of shape `(2, 2, 3)` and dtype `DType`."""
133
+ Array2x2x4: TypeAlias = TypedNDArray[tuple[TWO, TWO, FOUR], DType]
134
+ """A `numpy.ndarray` of shape `(2, 2, 4)` and dtype `DType`."""
135
+ Array2x2xN: TypeAlias = TypedNDArray[tuple[TWO, TWO, N], DType]
136
+ """A `numpy.ndarray` of shape `(2, 2, N)` and dtype `DType`."""
137
+
138
+ Array2x3x2: TypeAlias = TypedNDArray[tuple[TWO, THREE, TWO], DType]
139
+ """A `numpy.ndarray` of shape `(2, 3, 2)` and dtype `DType`."""
140
+ Array2x3x3: TypeAlias = TypedNDArray[tuple[TWO, THREE, THREE], DType]
141
+ """A `numpy.ndarray` of shape `(2, 3, 3)` and dtype `DType`."""
142
+ Array2x3x4: TypeAlias = TypedNDArray[tuple[TWO, THREE, FOUR], DType]
143
+ """A `numpy.ndarray` of shape `(2, 3, 4)` and dtype `DType`."""
144
+ Array2x3xN: TypeAlias = TypedNDArray[tuple[TWO, THREE, N], DType]
145
+ """A `numpy.ndarray` of shape `(2, 3, N)` and dtype `DType`."""
146
+
147
+ Array2x4x2: TypeAlias = TypedNDArray[tuple[TWO, FOUR, TWO], DType]
148
+ """A `numpy.ndarray` of shape `(2, 4, 2)` and dtype `DType`."""
149
+ Array2x4x3: TypeAlias = TypedNDArray[tuple[TWO, FOUR, THREE], DType]
150
+ """A `numpy.ndarray` of shape `(2, 4, 3)` and dtype `DType`."""
151
+ Array2x4x4: TypeAlias = TypedNDArray[tuple[TWO, FOUR, FOUR], DType]
152
+ """A `numpy.ndarray` of shape `(2, 4, 4)` and dtype `DType`."""
153
+ Array2x4xN: TypeAlias = TypedNDArray[tuple[TWO, FOUR, N], DType]
154
+ """A `numpy.ndarray` of shape `(2, 4, N)` and dtype `DType`."""
155
+
156
+ Array2xNx2: TypeAlias = TypedNDArray[tuple[TWO, N, TWO], DType]
157
+ """A `numpy.ndarray` of shape `(2, N, 2)` and dtype `DType`."""
158
+ Array2xNx3: TypeAlias = TypedNDArray[tuple[TWO, N, THREE], DType]
159
+ """A `numpy.ndarray` of shape `(2, N, 3)` and dtype `DType`."""
160
+ Array2xNx4: TypeAlias = TypedNDArray[tuple[TWO, N, FOUR], DType]
161
+ """A `numpy.ndarray` of shape `(2, N, 4)` and dtype `DType`."""
162
+
163
+ Array3x2x2: TypeAlias = TypedNDArray[tuple[THREE, TWO, TWO], DType]
164
+ """A `numpy.ndarray` of shape `(3, 2, 2)` and dtype `DType`."""
165
+ Array3x2x3: TypeAlias = TypedNDArray[tuple[THREE, TWO, THREE], DType]
166
+ """A `numpy.ndarray` of shape `(3, 2, 3)` and dtype `DType`."""
167
+ Array3x2x4: TypeAlias = TypedNDArray[tuple[THREE, TWO, FOUR], DType]
168
+ """A `numpy.ndarray` of shape `(3, 2, 4)` and dtype `DType`."""
169
+ Array3x2xN: TypeAlias = TypedNDArray[tuple[THREE, TWO, N], DType]
170
+ """A `numpy.ndarray` of shape `(3, 2, N)` and dtype `DType`."""
171
+
172
+ Array3x3x2: TypeAlias = TypedNDArray[tuple[THREE, THREE, TWO], DType]
173
+ """A `numpy.ndarray` of shape `(3, 3, 2)` and dtype `DType`."""
174
+ Array3x3x3: TypeAlias = TypedNDArray[tuple[THREE, THREE, THREE], DType]
175
+ """A `numpy.ndarray` of shape `(3, 3, 3)` and dtype `DType`."""
176
+ Array3x3x4: TypeAlias = TypedNDArray[tuple[THREE, THREE, FOUR], DType]
177
+ """A `numpy.ndarray` of shape `(3, 3, 4)` and dtype `DType`."""
178
+ Array3x3xN: TypeAlias = TypedNDArray[tuple[THREE, THREE, N], DType]
179
+ """A `numpy.ndarray` of shape `(3, 3, N)` and dtype `DType`."""
180
+
181
+ Array3x4x2: TypeAlias = TypedNDArray[tuple[THREE, FOUR, TWO], DType]
182
+ """A `numpy.ndarray` of shape `(3, 4, 2)` and dtype `DType`."""
183
+ Array3x4x3: TypeAlias = TypedNDArray[tuple[THREE, FOUR, THREE], DType]
184
+ """A `numpy.ndarray` of shape `(3, 4, 3)` and dtype `DType`."""
185
+ Array3x4x4: TypeAlias = TypedNDArray[tuple[THREE, FOUR, FOUR], DType]
186
+ """A `numpy.ndarray` of shape `(3, 4, 4)` and dtype `DType`."""
187
+ Array3x4xN: TypeAlias = TypedNDArray[tuple[THREE, FOUR, N], DType]
188
+ """A `numpy.ndarray` of shape `(3, 4, N)` and dtype `DType`."""
189
+
190
+ Array3xNx2: TypeAlias = TypedNDArray[tuple[THREE, N, TWO], DType]
191
+ """A `numpy.ndarray` of shape `(3, N, 2)` and dtype `DType`."""
192
+ Array3xNx3: TypeAlias = TypedNDArray[tuple[THREE, N, THREE], DType]
193
+ """A `numpy.ndarray` of shape `(3, N, 3)` and dtype `DType`."""
194
+ Array3xNx4: TypeAlias = TypedNDArray[tuple[THREE, N, FOUR], DType]
195
+ """A `numpy.ndarray` of shape `(3, N, 4)` and dtype `DType`."""
196
+
197
+ Array4x2x2: TypeAlias = TypedNDArray[tuple[FOUR, TWO, TWO], DType]
198
+ """A `numpy.ndarray` of shape `(4, 2, 2)` and dtype `DType`."""
199
+ Array4x2x3: TypeAlias = TypedNDArray[tuple[FOUR, TWO, THREE], DType]
200
+ """A `numpy.ndarray` of shape `(4, 2, 3)` and dtype `DType`."""
201
+ Array4x2x4: TypeAlias = TypedNDArray[tuple[FOUR, TWO, FOUR], DType]
202
+ """A `numpy.ndarray` of shape `(4, 2, 4)` and dtype `DType`."""
203
+ Array4x2xN: TypeAlias = TypedNDArray[tuple[FOUR, TWO, N], DType]
204
+ """A `numpy.ndarray` of shape `(4, 2, N)` and dtype `DType`."""
205
+
206
+ Array4x3x2: TypeAlias = TypedNDArray[tuple[FOUR, THREE, TWO], DType]
207
+ """A `numpy.ndarray` of shape `(4, 3, 2)` and dtype `DType`."""
208
+ Array4x3x3: TypeAlias = TypedNDArray[tuple[FOUR, THREE, THREE], DType]
209
+ """A `numpy.ndarray` of shape `(4, 3, 3)` and dtype `DType`."""
210
+ Array4x3x4: TypeAlias = TypedNDArray[tuple[FOUR, THREE, FOUR], DType]
211
+ """A `numpy.ndarray` of shape `(4, 3, 4)` and dtype `DType`."""
212
+ Array4x3xN: TypeAlias = TypedNDArray[tuple[FOUR, THREE, N], DType]
213
+ """A `numpy.ndarray` of shape `(4, 3, N)` and dtype `DType`."""
214
+
215
+ Array4x4x2: TypeAlias = TypedNDArray[tuple[FOUR, FOUR, TWO], DType]
216
+ """A `numpy.ndarray` of shape `(4, 4, 2)` and dtype `DType`."""
217
+ Array4x4x3: TypeAlias = TypedNDArray[tuple[FOUR, FOUR, THREE], DType]
218
+ """A `numpy.ndarray` of shape `(4, 4, 3)` and dtype `DType`."""
219
+ Array4x4x4: TypeAlias = TypedNDArray[tuple[FOUR, FOUR, FOUR], DType]
220
+ """A `numpy.ndarray` of shape `(4, 4, 4)` and dtype `DType`."""
221
+ Array4x4xN: TypeAlias = TypedNDArray[tuple[FOUR, FOUR, N], DType]
222
+ """A `numpy.ndarray` of shape `(4, 4, N)` and dtype `DType`."""
223
+
224
+ Array4xNx2: TypeAlias = TypedNDArray[tuple[FOUR, N, TWO], DType]
225
+ """A `numpy.ndarray` of shape `(4, N, 2)` and dtype `DType`."""
226
+ Array4xNx3: TypeAlias = TypedNDArray[tuple[FOUR, N, THREE], DType]
227
+ """A `numpy.ndarray` of shape `(4, N, 3)` and dtype `DType`."""
228
+ Array4xNx4: TypeAlias = TypedNDArray[tuple[FOUR, N, FOUR], DType]
229
+ """A `numpy.ndarray` of shape `(4, N, 4)` and dtype `DType`."""
230
+
231
+ ArrayNx2x2: TypeAlias = TypedNDArray[tuple[N, TWO, TWO], DType]
232
+ """A `numpy.ndarray` of shape `(N, 2, 2)` and dtype `DType`."""
233
+ ArrayNx2x3: TypeAlias = TypedNDArray[tuple[N, TWO, THREE], DType]
234
+ """A `numpy.ndarray` of shape `(N, 2, 3)` and dtype `DType`."""
235
+ ArrayNx2x4: TypeAlias = TypedNDArray[tuple[N, TWO, FOUR], DType]
236
+ """A `numpy.ndarray` of shape `(N, 2, 4)` and dtype `DType`."""
237
+ ArrayNx3x2: TypeAlias = TypedNDArray[tuple[N, THREE, TWO], DType]
238
+ """A `numpy.ndarray` of shape `(N, 3, 2)` and dtype `DType`."""
239
+ ArrayNx3x3: TypeAlias = TypedNDArray[tuple[N, THREE, THREE], DType]
240
+ """A `numpy.ndarray` of shape `(N, 3, 3)` and dtype `DType`."""
241
+ ArrayNx3x4: TypeAlias = TypedNDArray[tuple[N, THREE, FOUR], DType]
242
+ """A `numpy.ndarray` of shape `(N, 3, 4)` and dtype `DType`."""
243
+ ArrayNx4x2: TypeAlias = TypedNDArray[tuple[N, FOUR, TWO], DType]
244
+ """A `numpy.ndarray` of shape `(N, 4, 2)` and dtype `DType`."""
245
+ ArrayNx4x3: TypeAlias = TypedNDArray[tuple[N, FOUR, THREE], DType]
246
+ """A `numpy.ndarray` of shape `(N, 4, 3)` and dtype `DType`."""
247
+ ArrayNx4x4: TypeAlias = TypedNDArray[tuple[N, FOUR, FOUR], DType]
248
+ """A `numpy.ndarray` of shape `(N, 4, 4)` and dtype `DType`."""
@@ -0,0 +1,206 @@
1
+ """
2
+ TypedList
3
+ =======
4
+ """
5
+ # src/typingkit/_typed/list.py
6
+
7
+ import copy
8
+ import numbers
9
+ from collections.abc import Iterable, Sequence
10
+ from types import GenericAlias, NoneType, UnionType
11
+ from typing import Any, Callable, Literal, Self, TypeVar, cast, get_args, get_origin
12
+
13
+ from typingkit._typed.generics import RuntimeGeneric
14
+
15
+ ## Typings
16
+
17
+ Length = TypeVar("Length", bound=int, default=int)
18
+ Item = TypeVar("Item", default=Any)
19
+
20
+
21
+ ## Exceptions
22
+
23
+
24
+ class LengthError(Exception):
25
+ """Raised when list length doesn't match expected length."""
26
+
27
+
28
+ class ItemError(Exception):
29
+ """Raised when list item type doesn't match expected item type."""
30
+
31
+
32
+ ## Runtime validation
33
+
34
+
35
+ class TypedListConfig:
36
+ VALIDATE_LENGTH: bool = True
37
+ VALIDATE_ITEM: bool = True
38
+
39
+ @classmethod
40
+ def enable_all(cls):
41
+ cls.VALIDATE_LENGTH = True
42
+ cls.VALIDATE_ITEM = True
43
+
44
+ @classmethod
45
+ def disable_all(cls):
46
+ cls.VALIDATE_LENGTH = False
47
+ cls.VALIDATE_ITEM = False
48
+
49
+
50
+ def _is_assignable(value: Any, expected_type: type) -> bool:
51
+ if expected_type is Any:
52
+ return True
53
+ if expected_type is complex:
54
+ return isinstance(value, numbers.Complex)
55
+ if expected_type is float:
56
+ return isinstance(value, numbers.Real)
57
+ if expected_type is int:
58
+ return isinstance(value, numbers.Integral)
59
+ try:
60
+ origin = get_origin(expected_type)
61
+ if origin is not None:
62
+ return isinstance(value, origin)
63
+ return isinstance(value, expected_type)
64
+ except Exception:
65
+ return False
66
+
67
+
68
+ def _resolve_length(length: Any) -> Any:
69
+ # ~TypeVar
70
+ if isinstance(length, TypeVar):
71
+ # [TODO]: How should typing.NoDefault be handled?
72
+ length = _resolve_length(length.__default__)
73
+
74
+ origin = get_origin(length)
75
+
76
+ # Literal[A, B, ...]
77
+ if origin is Literal:
78
+ length = set(get_args(length))
79
+
80
+ # Union[A, B, ...]
81
+ if origin is UnionType:
82
+ resolved = (_resolve_length(arg) for arg in get_args(length))
83
+ result = set[Any]()
84
+ for r in resolved:
85
+ if isinstance(r, set):
86
+ result |= r
87
+ else:
88
+ result.add(r)
89
+ return result
90
+
91
+ return length
92
+
93
+
94
+ def _validate_length(object: Sequence[Item], length: Any) -> None:
95
+ if not TypedListConfig.VALIDATE_LENGTH:
96
+ return None
97
+
98
+ length = _resolve_length(length)
99
+
100
+ # type[Any]
101
+ if length is Any:
102
+ return None
103
+
104
+ # type[int]; Including <subclass of int>
105
+ if isinstance(length, type) and issubclass(length, int):
106
+ return None
107
+
108
+ # Should already be disallowed statically, here we just raise a runtime error
109
+ if length is NoneType:
110
+ raise LengthError("Invalid length")
111
+ # [TODO]: Others
112
+
113
+ # [NOTE]: From a statical perspective, being strict,
114
+ # prefer Literal[~int] over ~int, although we allow it here, for now.
115
+
116
+ actual = len(object)
117
+ if isinstance(length, set):
118
+ if len(length) > 1: # pyright: ignore[reportUnknownArgumentType]
119
+ for arg in length: # pyright: ignore[reportUnknownVariableType]
120
+ if isinstance(arg, int):
121
+ if arg == actual:
122
+ break
123
+
124
+ ## [TODO]: Similar to the outer validation, so prolly can refactor through a recursive function call
125
+ # type[Any]
126
+ if arg is Any:
127
+ break
128
+
129
+ # type[int]; Including <subclass of int>
130
+ if isinstance(arg, type) and issubclass(arg, int):
131
+ break
132
+ else:
133
+ raise LengthError(
134
+ f"Length mismatch: expected one of {length}, got {actual}"
135
+ )
136
+ elif len(length) == 1: # pyright: ignore[reportUnknownArgumentType]
137
+ # Defer to the single case below
138
+ length = length.pop() # pyright: ignore[reportUnknownVariableType]
139
+ else: # len(length) == 0
140
+ # This case should prolly never arise? Strictly speaking, statically.
141
+ return None
142
+ # (Concrete) int
143
+ if isinstance(length, int):
144
+ # This case is just for a minimal error message, we could already handle
145
+ # it in the `set` case above, provided `_resolve_length` resolves it accordingly.
146
+ if actual != length:
147
+ raise LengthError(f"Length mismatch: expected {length}, got {actual}")
148
+
149
+ return None # Fallback
150
+
151
+
152
+ def _validate_item(object: Iterable[Item], item_type: Any) -> None:
153
+ if not TypedListConfig.VALIDATE_ITEM:
154
+ return
155
+
156
+ for index, item in enumerate(object):
157
+ if not _is_assignable(item, item_type):
158
+ raise ItemError(
159
+ f"Item type mismatch: expected {item_type.__name__}, got {type(item).__name__} at index {index}"
160
+ )
161
+
162
+
163
+ ## TypedList
164
+ class TypedList(RuntimeGeneric[Length, Item], list[Item]):
165
+ @classmethod
166
+ def __pre_new__(cls, alias: GenericAlias, *args: Any, **kwargs: Any) -> Self:
167
+ # Create `list` object
168
+ obj = super().__pre_new__(alias, *args, **kwargs)
169
+
170
+ ## Runtime validations
171
+ typeargs = get_args(alias)
172
+ if len(typeargs) == 2:
173
+ length, item_type = typeargs
174
+ elif len(typeargs) == 1:
175
+ (length,) = typeargs
176
+ item_type = Item.__default__ # type: ignore[misc]
177
+ # The `item_type` default here should match the default in `Item`.
178
+ else:
179
+ raise TypeError
180
+ _validate_length(obj, length)
181
+ _validate_item(obj, item_type)
182
+
183
+ return obj
184
+
185
+ def __len__(self) -> Length:
186
+ return cast(Length, super().__len__())
187
+
188
+ def copy(self) -> Self:
189
+ return type(self)(super().copy())
190
+
191
+ @property
192
+ def length(self) -> Length:
193
+ return self.__len__()
194
+
195
+ @classmethod
196
+ def full(
197
+ cls: "type[TypedList[Length, Item]]",
198
+ length: Length,
199
+ fill_value: Item | Callable[[int], Item],
200
+ ) -> "TypedList[Length, Item]":
201
+ data: list[Item]
202
+ if callable(fill_value):
203
+ data = [cast(Item, fill_value(i)) for i in range(length)]
204
+ else:
205
+ data = [copy.deepcopy(fill_value) for _ in range(length)]
206
+ return cls(data)