typingkit 0.2.3__tar.gz → 0.2.4__tar.gz
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.
- {typingkit-0.2.3 → typingkit-0.2.4}/PKG-INFO +1 -1
- {typingkit-0.2.3 → typingkit-0.2.4}/pyproject.toml +1 -1
- typingkit-0.2.4/src/typingkit/__init__.py +5 -0
- typingkit-0.2.4/src/typingkit/core/__init__.py +17 -0
- {typingkit-0.2.3/src/typingkit/_typed → typingkit-0.2.4/src/typingkit/core}/_debug.py +1 -1
- typingkit-0.2.4/src/typingkit/core/dict.py +152 -0
- typingkit-0.2.4/src/typingkit/core/generics.py +180 -0
- {typingkit-0.2.3/src/typingkit/_typed → typingkit-0.2.4/src/typingkit/core}/list.py +4 -8
- typingkit-0.2.4/src/typingkit/numpy/__init__.py +15 -0
- typingkit-0.2.4/src/typingkit/numpy/_typed/__init__.py +13 -0
- {typingkit-0.2.3/src/typingkit → typingkit-0.2.4/src/typingkit/numpy}/_typed/context.py +2 -2
- {typingkit-0.2.3/src/typingkit → typingkit-0.2.4/src/typingkit/numpy}/_typed/dimexpr.py +1 -1
- {typingkit-0.2.3/src/typingkit → typingkit-0.2.4/src/typingkit/numpy}/_typed/factory.py +2 -2
- {typingkit-0.2.3/src/typingkit → typingkit-0.2.4/src/typingkit/numpy}/_typed/helpers.py +2 -2
- {typingkit-0.2.3/src/typingkit → typingkit-0.2.4/src/typingkit/numpy}/_typed/ndarray.py +3 -3
- typingkit-0.2.3/src/typingkit/__init__.py +0 -11
- typingkit-0.2.3/src/typingkit/_typed/__init__.py +0 -11
- typingkit-0.2.3/src/typingkit/_typed/generics.py +0 -50
- {typingkit-0.2.3 → typingkit-0.2.4}/README.md +0 -0
- {typingkit-0.2.3 → typingkit-0.2.4}/src/typingkit/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "typingkit"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.4"
|
|
4
4
|
description = "Python strong typing suite, along with Typed NumPy: Static shape typing and runtime shape validation."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Ashrith Sagar", email = "ashrith9sagar@gmail.com" }]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TypingKit core
|
|
3
|
+
=======
|
|
4
|
+
"""
|
|
5
|
+
# src/typingkit/core/__init__.py
|
|
6
|
+
|
|
7
|
+
from typingkit.core.dict import TypedDict, TypedDictConfig
|
|
8
|
+
from typingkit.core.generics import RuntimeGeneric
|
|
9
|
+
from typingkit.core.list import TypedList, TypedListConfig
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"RuntimeGeneric",
|
|
13
|
+
"TypedList",
|
|
14
|
+
"TypedListConfig",
|
|
15
|
+
"TypedDict",
|
|
16
|
+
"TypedDictConfig",
|
|
17
|
+
]
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TypedDict
|
|
3
|
+
=======
|
|
4
|
+
"""
|
|
5
|
+
# src/typingkit/core/dict.py
|
|
6
|
+
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
from types import GenericAlias, NoneType, UnionType
|
|
9
|
+
from typing import Any, Literal, Self, TypeVar, cast, get_args, get_origin
|
|
10
|
+
|
|
11
|
+
from typingkit.core.generics import RuntimeGeneric, get_runtime_args
|
|
12
|
+
|
|
13
|
+
## Typings
|
|
14
|
+
|
|
15
|
+
Length = TypeVar("Length", bound=int, default=int)
|
|
16
|
+
Key = TypeVar("Key", default=Any)
|
|
17
|
+
Value = TypeVar("Value", default=Any)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Exceptions
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class LengthError(Exception):
|
|
24
|
+
"""Raised when dict length doesn't match expected length."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Runtime validation
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TypedDictConfig:
|
|
31
|
+
VALIDATE_LENGTH: bool = True
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def enable_all(cls):
|
|
35
|
+
cls.VALIDATE_LENGTH = True
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def disable_all(cls):
|
|
39
|
+
cls.VALIDATE_LENGTH = False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _resolve_length(length: Any) -> Any:
|
|
43
|
+
# ~TypeVar
|
|
44
|
+
if isinstance(length, TypeVar):
|
|
45
|
+
# [TODO]: How should typing.NoDefault be handled?
|
|
46
|
+
length = _resolve_length(length.__default__)
|
|
47
|
+
|
|
48
|
+
origin = get_origin(length)
|
|
49
|
+
|
|
50
|
+
# Literal[A, B, ...]
|
|
51
|
+
if origin is Literal:
|
|
52
|
+
length = set(get_args(length))
|
|
53
|
+
|
|
54
|
+
# Union[A, B, ...]
|
|
55
|
+
if origin is UnionType:
|
|
56
|
+
resolved = (_resolve_length(arg) for arg in get_args(length))
|
|
57
|
+
result = set[Any]()
|
|
58
|
+
for r in resolved:
|
|
59
|
+
if isinstance(r, set):
|
|
60
|
+
result |= r
|
|
61
|
+
else:
|
|
62
|
+
result.add(r)
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
return length
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _validate_length(object: Mapping[Key, Value], length: Any) -> None:
|
|
69
|
+
if not TypedDictConfig.VALIDATE_LENGTH:
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
length = _resolve_length(length)
|
|
73
|
+
|
|
74
|
+
# type[Any]
|
|
75
|
+
if length is Any:
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
# type[int]; Including <subclass of int>
|
|
79
|
+
if isinstance(length, type) and issubclass(length, int):
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
# Should already be disallowed statically, here we just raise a runtime error
|
|
83
|
+
if length is NoneType:
|
|
84
|
+
raise LengthError("Invalid length")
|
|
85
|
+
# [TODO]: Others
|
|
86
|
+
|
|
87
|
+
# [NOTE]: From a statical perspective, being strict,
|
|
88
|
+
# prefer Literal[~int] over ~int, although we allow it here, for now.
|
|
89
|
+
|
|
90
|
+
actual = len(object)
|
|
91
|
+
if isinstance(length, set):
|
|
92
|
+
if len(length) > 1: # pyright: ignore[reportUnknownArgumentType]
|
|
93
|
+
for arg in length: # pyright: ignore[reportUnknownVariableType]
|
|
94
|
+
if isinstance(arg, int):
|
|
95
|
+
if arg == actual:
|
|
96
|
+
break
|
|
97
|
+
|
|
98
|
+
## [TODO]: Similar to the outer validation, so prolly can refactor through a recursive function call
|
|
99
|
+
# type[Any]
|
|
100
|
+
if arg is Any:
|
|
101
|
+
break
|
|
102
|
+
|
|
103
|
+
# type[int]; Including <subclass of int>
|
|
104
|
+
if isinstance(arg, type) and issubclass(arg, int):
|
|
105
|
+
break
|
|
106
|
+
else:
|
|
107
|
+
raise LengthError(
|
|
108
|
+
f"Length mismatch: expected one of {length}, got {actual}"
|
|
109
|
+
)
|
|
110
|
+
elif len(length) == 1: # pyright: ignore[reportUnknownArgumentType]
|
|
111
|
+
# Defer to the single case below
|
|
112
|
+
length = length.pop() # pyright: ignore[reportUnknownVariableType]
|
|
113
|
+
else: # len(length) == 0
|
|
114
|
+
# This case should prolly never arise? Strictly speaking, statically.
|
|
115
|
+
return None
|
|
116
|
+
# (Concrete) int
|
|
117
|
+
if isinstance(length, int):
|
|
118
|
+
# This case is just for a minimal error message, we could already handle
|
|
119
|
+
# it in the `set` case above, provided `_resolve_length` resolves it accordingly.
|
|
120
|
+
if actual != length:
|
|
121
|
+
raise LengthError(f"Length mismatch: expected {length}, got {actual}")
|
|
122
|
+
|
|
123
|
+
return None # Fallback
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
## TypedDict
|
|
127
|
+
class TypedDict(RuntimeGeneric[Length, Key, Value], dict[Key, Value]):
|
|
128
|
+
@classmethod
|
|
129
|
+
def __pre_new__(cls, alias: GenericAlias, *args: Any, **kwargs: Any) -> Self:
|
|
130
|
+
# Create `dict` object
|
|
131
|
+
obj = super().__pre_new__(alias, *args, **kwargs)
|
|
132
|
+
|
|
133
|
+
## Runtime validations
|
|
134
|
+
typeargs = get_runtime_args(alias, cls)
|
|
135
|
+
if len(typeargs) == 3:
|
|
136
|
+
(length, _, _) = typeargs
|
|
137
|
+
elif len(typeargs) == 2:
|
|
138
|
+
(length, _) = typeargs
|
|
139
|
+
elif len(typeargs) == 1:
|
|
140
|
+
(length,) = typeargs
|
|
141
|
+
else:
|
|
142
|
+
raise TypeError
|
|
143
|
+
_validate_length(obj, length)
|
|
144
|
+
|
|
145
|
+
return obj
|
|
146
|
+
|
|
147
|
+
def __len__(self) -> Length:
|
|
148
|
+
return cast(Length, super().__len__())
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def length(self) -> Length:
|
|
152
|
+
return self.__len__()
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Generics
|
|
3
|
+
=======
|
|
4
|
+
"""
|
|
5
|
+
# src/typingkit/core/generics.py
|
|
6
|
+
|
|
7
|
+
from types import GenericAlias, get_original_bases
|
|
8
|
+
from typing import (
|
|
9
|
+
Any,
|
|
10
|
+
Generic,
|
|
11
|
+
Self,
|
|
12
|
+
TypeVar,
|
|
13
|
+
TypeVarTuple,
|
|
14
|
+
Unpack,
|
|
15
|
+
cast,
|
|
16
|
+
get_args,
|
|
17
|
+
get_origin,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
Ts = TypeVarTuple("Ts")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class RuntimeGeneric(Generic[Unpack[Ts]]):
|
|
24
|
+
@classmethod
|
|
25
|
+
def __class_getitem__(cls, item: Any, /) -> GenericAlias:
|
|
26
|
+
# [HACK] Misuses __class_getitem__
|
|
27
|
+
# See https://docs.python.org/3/reference/datamodel.html#the-purpose-of-class-getitem
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
ga = cast(GenericAlias, super().__class_getitem__(item)) # type: ignore[misc]
|
|
31
|
+
except: # noqa: E722
|
|
32
|
+
# Fallback if superclass does not implement `__class_getitem__`
|
|
33
|
+
ga = GenericAlias(cls, item)
|
|
34
|
+
return _RuntimeGenericAlias.from_generic_alias(ga)
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def __pre_new__(cls, alias: GenericAlias, *args: Any, **kwargs: Any) -> Self:
|
|
38
|
+
return cls(*args, **kwargs)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class _RuntimeGenericAlias(GenericAlias):
|
|
42
|
+
"""
|
|
43
|
+
Deferred RuntimeGeneric constructor.
|
|
44
|
+
Enables progressive type specialisation, behaving like a type-level curry.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def from_generic_alias(cls, alias: GenericAlias) -> Self:
|
|
49
|
+
origin = get_origin(alias)
|
|
50
|
+
typeargs = get_args(alias)
|
|
51
|
+
return cls(origin, typeargs)
|
|
52
|
+
|
|
53
|
+
def __getitem__(self, typeargs: Any) -> Self:
|
|
54
|
+
ga = super().__getitem__(typeargs)
|
|
55
|
+
return type(self).from_generic_alias(ga)
|
|
56
|
+
|
|
57
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
|
58
|
+
origin: type[RuntimeGeneric[Unpack[Ts]]] = get_origin(self) # type: ignore[valid-type]
|
|
59
|
+
obj = origin.__pre_new__(self, *args, **kwargs)
|
|
60
|
+
return obj
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## Generics resolution
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _substitute(tp: Any, mapping: dict[Any, Any]) -> Any:
|
|
67
|
+
if isinstance(tp, TypeVar):
|
|
68
|
+
return mapping.get(tp, tp)
|
|
69
|
+
|
|
70
|
+
if isinstance(tp, TypeVarTuple):
|
|
71
|
+
return mapping.get(tp, tp)
|
|
72
|
+
|
|
73
|
+
origin = get_origin(tp)
|
|
74
|
+
if origin is None:
|
|
75
|
+
return tp
|
|
76
|
+
|
|
77
|
+
args = get_args(tp)
|
|
78
|
+
|
|
79
|
+
if origin is Unpack:
|
|
80
|
+
(inner,) = args
|
|
81
|
+
value = _substitute(inner, mapping)
|
|
82
|
+
|
|
83
|
+
# If the inner resolved to a tuple (TypeVarTuple binding),
|
|
84
|
+
# return the tuple directly so the parent can expand it.
|
|
85
|
+
if isinstance(value, tuple):
|
|
86
|
+
return value # pyright: ignore[reportUnknownVariableType]
|
|
87
|
+
|
|
88
|
+
return Unpack[value]
|
|
89
|
+
|
|
90
|
+
new_args_list = list[Any]()
|
|
91
|
+
for arg in args:
|
|
92
|
+
val = _substitute(arg, mapping)
|
|
93
|
+
if isinstance(val, tuple):
|
|
94
|
+
new_args_list.extend(val) # pyright: ignore[reportUnknownArgumentType]
|
|
95
|
+
else:
|
|
96
|
+
new_args_list.append(val)
|
|
97
|
+
new_args = tuple(new_args_list)
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
return origin[new_args]
|
|
101
|
+
except TypeError:
|
|
102
|
+
return tp
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _build_mapping(params: tuple[Any, ...], args: tuple[Any, ...]) -> dict[Any, Any]:
|
|
106
|
+
mapping = dict[Any, Any]()
|
|
107
|
+
i: int = 0
|
|
108
|
+
j: int = 0
|
|
109
|
+
|
|
110
|
+
while i < len(params):
|
|
111
|
+
p = params[i]
|
|
112
|
+
|
|
113
|
+
if isinstance(p, TypeVarTuple):
|
|
114
|
+
# Handle tuple unpacking
|
|
115
|
+
remaining_params = len(params) - i - 1
|
|
116
|
+
remaining_args = len(args) - j
|
|
117
|
+
size = remaining_args - remaining_params
|
|
118
|
+
if size < 0:
|
|
119
|
+
raise TypeError(
|
|
120
|
+
f"Not enough type arguments to bind TypeVarTuple {p}. "
|
|
121
|
+
f"Expected at least {len(params)} but got {len(args)}"
|
|
122
|
+
)
|
|
123
|
+
mapping[p] = args[j : j + size]
|
|
124
|
+
j += size
|
|
125
|
+
i += 1
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
if j >= len(args):
|
|
129
|
+
# No argument supplied, try default
|
|
130
|
+
default = getattr(p, "__default__", None)
|
|
131
|
+
if default is not None:
|
|
132
|
+
mapping[p] = default
|
|
133
|
+
else:
|
|
134
|
+
raise TypeError(
|
|
135
|
+
f"Missing type argument for {p}. Expected {len(params)} args, got {len(args)}"
|
|
136
|
+
)
|
|
137
|
+
else:
|
|
138
|
+
mapping[p] = args[j]
|
|
139
|
+
j += 1
|
|
140
|
+
|
|
141
|
+
i += 1
|
|
142
|
+
|
|
143
|
+
if j < len(args):
|
|
144
|
+
# Extra arguments leftover
|
|
145
|
+
raise TypeError(
|
|
146
|
+
f"Too many type arguments. Expected {len(params)}, got {len(args)}"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return mapping
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def get_runtime_args(tp: Any, cls: type) -> tuple[Any, ...]:
|
|
153
|
+
args = get_args(tp)
|
|
154
|
+
|
|
155
|
+
parameters: tuple[Any, ...] = getattr(cls, "__parameters__", ())
|
|
156
|
+
mapping = _build_mapping(parameters, args)
|
|
157
|
+
|
|
158
|
+
current_cls = cls
|
|
159
|
+
while True:
|
|
160
|
+
orig_bases = get_original_bases(current_cls)
|
|
161
|
+
for base in orig_bases:
|
|
162
|
+
origin = get_origin(base)
|
|
163
|
+
if origin is None:
|
|
164
|
+
continue
|
|
165
|
+
|
|
166
|
+
resolved = _substitute(base, mapping)
|
|
167
|
+
|
|
168
|
+
if get_origin(resolved) is RuntimeGeneric:
|
|
169
|
+
return get_args(resolved)
|
|
170
|
+
|
|
171
|
+
parent_params = getattr(origin, "__parameters__", ())
|
|
172
|
+
parent_args = get_args(resolved)
|
|
173
|
+
|
|
174
|
+
mapping = _build_mapping(parent_params, parent_args)
|
|
175
|
+
current_cls = origin
|
|
176
|
+
break
|
|
177
|
+
else:
|
|
178
|
+
break
|
|
179
|
+
|
|
180
|
+
return args
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
TypedList
|
|
3
3
|
=======
|
|
4
4
|
"""
|
|
5
|
-
# src/typingkit/
|
|
5
|
+
# src/typingkit/core/list.py
|
|
6
6
|
|
|
7
7
|
import copy
|
|
8
8
|
import numbers
|
|
@@ -10,7 +10,7 @@ from collections.abc import Iterable, Sequence
|
|
|
10
10
|
from types import GenericAlias, NoneType, UnionType
|
|
11
11
|
from typing import Any, Callable, Literal, Self, TypeVar, cast, get_args, get_origin
|
|
12
12
|
|
|
13
|
-
from typingkit.
|
|
13
|
+
from typingkit.core.generics import RuntimeGeneric, get_runtime_args
|
|
14
14
|
|
|
15
15
|
## Typings
|
|
16
16
|
|
|
@@ -168,7 +168,7 @@ class TypedList(RuntimeGeneric[Length, Item], list[Item]):
|
|
|
168
168
|
obj = super().__pre_new__(alias, *args, **kwargs)
|
|
169
169
|
|
|
170
170
|
## Runtime validations
|
|
171
|
-
typeargs =
|
|
171
|
+
typeargs = get_runtime_args(alias, cls)
|
|
172
172
|
if len(typeargs) == 2:
|
|
173
173
|
length, item_type = typeargs
|
|
174
174
|
elif len(typeargs) == 1:
|
|
@@ -193,11 +193,7 @@ class TypedList(RuntimeGeneric[Length, Item], list[Item]):
|
|
|
193
193
|
return self.__len__()
|
|
194
194
|
|
|
195
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]":
|
|
196
|
+
def full(cls, length: Length, fill_value: Item | Callable[[int], Item]) -> Self:
|
|
201
197
|
data: list[Item]
|
|
202
198
|
if callable(fill_value):
|
|
203
199
|
data = [cast(Item, fill_value(i)) for i in range(length)]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Typed NumPy core
|
|
3
|
+
=======
|
|
4
|
+
"""
|
|
5
|
+
# src/typingkit/numpy/_typed/__init__.py
|
|
6
|
+
|
|
7
|
+
from typingkit.numpy._typed.context import enforce_shapes
|
|
8
|
+
from typingkit.numpy._typed.ndarray import TypedNDArray
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"TypedNDArray",
|
|
12
|
+
"enforce_shapes",
|
|
13
|
+
]
|
|
@@ -3,7 +3,7 @@ Context binding
|
|
|
3
3
|
=======
|
|
4
4
|
Manages TypeVar binding contexts for shape validation.
|
|
5
5
|
"""
|
|
6
|
-
# src/typingkit/_typed/context.py
|
|
6
|
+
# src/typingkit/numpy/_typed/context.py
|
|
7
7
|
|
|
8
8
|
# pyright: reportPrivateUsage = false
|
|
9
9
|
|
|
@@ -21,7 +21,7 @@ from typing import (
|
|
|
21
21
|
get_type_hints,
|
|
22
22
|
)
|
|
23
23
|
|
|
24
|
-
from typingkit._typed.ndarray import (
|
|
24
|
+
from typingkit.numpy._typed.ndarray import (
|
|
25
25
|
DimensionError,
|
|
26
26
|
_validate_shape,
|
|
27
27
|
_validate_shape_against_contexts,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Shape factory for TypedNDArray
|
|
3
3
|
=======
|
|
4
4
|
"""
|
|
5
|
-
# src/typingkit/_typed/factory.py
|
|
5
|
+
# src/typingkit/numpy/_typed/factory.py
|
|
6
6
|
|
|
7
7
|
# pyright: reportPrivateUsage = false
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ from typing import Any, Generic, TypeVar
|
|
|
12
12
|
import numpy as np
|
|
13
13
|
import numpy.typing as npt
|
|
14
14
|
|
|
15
|
-
from typingkit._typed.ndarray import TypedNDArray, _AnyShape
|
|
15
|
+
from typingkit.numpy._typed.ndarray import TypedNDArray, _AnyShape
|
|
16
16
|
|
|
17
17
|
_ShapeT = TypeVar("_ShapeT", bound=_AnyShape)
|
|
18
18
|
_ScalarT = TypeVar("_ScalarT", bound=np.generic)
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
Helpers for TypedNDArray
|
|
3
3
|
=======
|
|
4
4
|
"""
|
|
5
|
-
# src/typingkit/_typed/helpers.py
|
|
5
|
+
# src/typingkit/numpy/_typed/helpers.py
|
|
6
6
|
|
|
7
7
|
from typing import Literal, TypeAlias, TypeVar
|
|
8
8
|
|
|
9
9
|
import numpy as np
|
|
10
10
|
|
|
11
|
-
from typingkit._typed.ndarray import TypedNDArray
|
|
11
|
+
from typingkit.numpy._typed.ndarray import TypedNDArray
|
|
12
12
|
|
|
13
13
|
## Helpers
|
|
14
14
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
NDArray
|
|
3
3
|
=======
|
|
4
4
|
"""
|
|
5
|
-
# src/typingkit/_typed/ndarray.py
|
|
5
|
+
# src/typingkit/numpy/_typed/ndarray.py
|
|
6
6
|
|
|
7
7
|
# pyright: reportPrivateUsage = false
|
|
8
8
|
|
|
@@ -134,7 +134,7 @@ def _validate_dtype(
|
|
|
134
134
|
|
|
135
135
|
|
|
136
136
|
def _resolve_shape(args: _AnyShape) -> _AnyShape:
|
|
137
|
-
from typingkit._typed.dimexpr import _resolve_dim
|
|
137
|
+
from typingkit.numpy._typed.dimexpr import _resolve_dim
|
|
138
138
|
|
|
139
139
|
# [TODO]: Handle TypeAliasType
|
|
140
140
|
return tuple(_resolve_dim(arg) for arg in args)
|
|
@@ -199,7 +199,7 @@ def _validate_shape(expected: _AnyShape, actual: _Shape) -> None:
|
|
|
199
199
|
|
|
200
200
|
def _validate_shape_against_contexts(shape_spec: _AnyShape, actual: _Shape) -> None:
|
|
201
201
|
"""Validate shape against active TypeVar contexts (class-level and method-level)."""
|
|
202
|
-
from typingkit._typed.context import (
|
|
202
|
+
from typingkit.numpy._typed.context import (
|
|
203
203
|
_active_class_context,
|
|
204
204
|
_method_typevar_context,
|
|
205
205
|
)
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Generics
|
|
3
|
-
=======
|
|
4
|
-
"""
|
|
5
|
-
# src/typingkit/_typed/generics.py
|
|
6
|
-
|
|
7
|
-
from types import GenericAlias
|
|
8
|
-
from typing import Any, Generic, Self, TypeVarTuple, Unpack, cast, get_args, get_origin
|
|
9
|
-
|
|
10
|
-
Ts = TypeVarTuple("Ts", default=Unpack[tuple[Any, ...]])
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class RuntimeGeneric(Generic[Unpack[Ts]]):
|
|
14
|
-
@classmethod
|
|
15
|
-
def __class_getitem__(cls, item: Any, /) -> GenericAlias:
|
|
16
|
-
# [HACK] Misuses __class_getitem__
|
|
17
|
-
# See https://docs.python.org/3/reference/datamodel.html#the-purpose-of-class-getitem
|
|
18
|
-
|
|
19
|
-
try:
|
|
20
|
-
ga = cast(GenericAlias, super().__class_getitem__(item)) # type: ignore[misc]
|
|
21
|
-
except: # noqa: E722
|
|
22
|
-
# Fallback if superclass does not implement `__class_getitem__`
|
|
23
|
-
ga = GenericAlias(cls, item)
|
|
24
|
-
return _RuntimeGenericAlias.from_generic_alias(ga)
|
|
25
|
-
|
|
26
|
-
@classmethod
|
|
27
|
-
def __pre_new__(cls, alias: GenericAlias, *args: Any, **kwargs: Any) -> Self:
|
|
28
|
-
return cls(*args, **kwargs)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class _RuntimeGenericAlias(GenericAlias):
|
|
32
|
-
"""
|
|
33
|
-
Deferred RuntimeGeneric constructor.
|
|
34
|
-
Enables progressive type specialisation, behaving like a type-level curry.
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
@classmethod
|
|
38
|
-
def from_generic_alias(cls, alias: GenericAlias) -> Self:
|
|
39
|
-
origin = get_origin(alias)
|
|
40
|
-
typeargs = get_args(alias)
|
|
41
|
-
return cls(origin, typeargs)
|
|
42
|
-
|
|
43
|
-
def __getitem__(self, typeargs: Any) -> Self:
|
|
44
|
-
ga = super().__getitem__(typeargs)
|
|
45
|
-
return type(self).from_generic_alias(ga)
|
|
46
|
-
|
|
47
|
-
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
|
48
|
-
origin: type[RuntimeGeneric] = get_origin(self)
|
|
49
|
-
obj = origin.__pre_new__(self, *args, **kwargs)
|
|
50
|
-
return obj
|
|
File without changes
|
|
File without changes
|