haiway 0.6.3__py3-none-any.whl → 0.7.0__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.
- haiway/state/attributes.py +632 -260
- haiway/state/structure.py +47 -31
- haiway/state/validation.py +197 -47
- {haiway-0.6.3.dist-info → haiway-0.7.0.dist-info}/METADATA +1 -1
- {haiway-0.6.3.dist-info → haiway-0.7.0.dist-info}/RECORD +8 -8
- {haiway-0.6.3.dist-info → haiway-0.7.0.dist-info}/LICENSE +0 -0
- {haiway-0.6.3.dist-info → haiway-0.7.0.dist-info}/WHEEL +0 -0
- {haiway-0.6.3.dist-info → haiway-0.7.0.dist-info}/top_level.txt +0 -0
haiway/state/attributes.py
CHANGED
@@ -1,21 +1,29 @@
|
|
1
|
-
import sys
|
2
1
|
import types
|
3
2
|
import typing
|
4
|
-
from collections.abc import Mapping
|
5
|
-
from types import NoneType, UnionType
|
3
|
+
from collections.abc import Callable, Mapping, MutableMapping, Sequence
|
4
|
+
from types import GenericAlias, NoneType, UnionType
|
6
5
|
from typing import (
|
7
6
|
Any,
|
8
7
|
ClassVar,
|
9
8
|
ForwardRef,
|
10
9
|
Generic,
|
11
10
|
Literal,
|
11
|
+
ParamSpec,
|
12
|
+
Self,
|
12
13
|
TypeAliasType,
|
13
14
|
TypeVar,
|
15
|
+
TypeVarTuple,
|
16
|
+
_GenericAlias, # pyright: ignore
|
14
17
|
get_args,
|
15
18
|
get_origin,
|
16
19
|
get_type_hints,
|
20
|
+
is_typeddict,
|
21
|
+
overload,
|
17
22
|
)
|
18
23
|
|
24
|
+
from haiway import types as haiway_types
|
25
|
+
from haiway.types import MISSING, Missing
|
26
|
+
|
19
27
|
__all__ = [
|
20
28
|
"AttributeAnnotation",
|
21
29
|
"attribute_annotations",
|
@@ -28,339 +36,703 @@ class AttributeAnnotation:
|
|
28
36
|
self,
|
29
37
|
*,
|
30
38
|
origin: Any,
|
31
|
-
arguments:
|
39
|
+
arguments: Sequence[Any] | None = None,
|
40
|
+
required: bool = True,
|
41
|
+
extra: Mapping[str, Any] | None = None,
|
32
42
|
) -> None:
|
33
43
|
self.origin: Any = origin
|
34
|
-
self.arguments:
|
44
|
+
self.arguments: Sequence[Any]
|
45
|
+
if arguments is None:
|
46
|
+
self.arguments = ()
|
47
|
+
|
48
|
+
else:
|
49
|
+
self.arguments = arguments
|
50
|
+
|
51
|
+
self.required: bool = required
|
52
|
+
self.extra: Mapping[str, Any]
|
53
|
+
if extra is None:
|
54
|
+
self.extra = {}
|
35
55
|
|
36
|
-
|
56
|
+
else:
|
57
|
+
self.extra = extra
|
58
|
+
|
59
|
+
def update_required(
|
37
60
|
self,
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
)
|
61
|
+
required: bool,
|
62
|
+
/,
|
63
|
+
) -> Self:
|
64
|
+
self.required = self.required and required
|
65
|
+
|
66
|
+
return self
|
45
67
|
|
46
68
|
def __str__(self) -> str:
|
47
|
-
|
48
|
-
|
49
|
-
|
69
|
+
origin_str: str = getattr(self.origin, "__name__", str(self.origin))
|
70
|
+
arguments_str: str
|
71
|
+
if self.arguments:
|
72
|
+
arguments_str = "[" + ", ".join(str(arg) for arg in self.arguments) + "]"
|
73
|
+
|
74
|
+
else:
|
75
|
+
arguments_str = ""
|
76
|
+
|
77
|
+
if module := getattr(self.origin, "__module__", None):
|
78
|
+
return f"{module}.{origin_str}{arguments_str}"
|
79
|
+
|
80
|
+
else:
|
81
|
+
return f"{origin_str}{arguments_str}"
|
50
82
|
|
51
83
|
|
52
84
|
def attribute_annotations(
|
53
85
|
cls: type[Any],
|
54
86
|
/,
|
55
|
-
type_parameters:
|
56
|
-
) ->
|
57
|
-
type_parameters = type_parameters or {}
|
58
|
-
|
87
|
+
type_parameters: Mapping[str, Any],
|
88
|
+
) -> Mapping[str, AttributeAnnotation]:
|
59
89
|
self_annotation = AttributeAnnotation(
|
60
90
|
origin=cls,
|
61
|
-
|
91
|
+
# ignore arguments here, State (and draive.DataModel) will have them resolved at this stage
|
92
|
+
arguments=[],
|
62
93
|
)
|
63
|
-
|
64
|
-
|
94
|
+
|
95
|
+
# ignore args_keys here, State (and draive.DataModel) will have them resolved at this stage
|
96
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation] = {
|
97
|
+
_recursion_key(cls, default=str(self_annotation)): self_annotation
|
98
|
+
}
|
99
|
+
|
65
100
|
attributes: dict[str, AttributeAnnotation] = {}
|
101
|
+
for key, annotation in get_type_hints(cls, localns={cls.__name__: cls}).items():
|
102
|
+
# do not include private or special items
|
103
|
+
if key.startswith("_"):
|
104
|
+
continue
|
66
105
|
|
67
|
-
|
68
|
-
|
69
|
-
if ((get_origin(annotation) or annotation) is ClassVar) or key.startswith("_"):
|
106
|
+
# do not include ClassVars
|
107
|
+
if (get_origin(annotation) or annotation) is ClassVar:
|
70
108
|
continue
|
71
109
|
|
72
110
|
attributes[key] = resolve_attribute_annotation(
|
73
111
|
annotation,
|
74
|
-
self_annotation=self_annotation,
|
75
112
|
type_parameters=type_parameters,
|
76
113
|
module=cls.__module__,
|
77
|
-
|
114
|
+
self_annotation=self_annotation,
|
78
115
|
recursion_guard=recursion_guard,
|
79
116
|
)
|
80
117
|
|
81
118
|
return attributes
|
82
119
|
|
83
120
|
|
84
|
-
def
|
121
|
+
def _resolve_none(
|
122
|
+
annotation: Any,
|
123
|
+
) -> AttributeAnnotation:
|
124
|
+
return AttributeAnnotation(origin=NoneType)
|
125
|
+
|
126
|
+
|
127
|
+
def _resolve_missing(
|
128
|
+
annotation: Any,
|
129
|
+
) -> AttributeAnnotation:
|
130
|
+
# special case - attributes marked as missing are not required
|
131
|
+
# Missing does not work properly within TypedDict though
|
132
|
+
return AttributeAnnotation(
|
133
|
+
origin=Missing,
|
134
|
+
required=False,
|
135
|
+
)
|
136
|
+
|
137
|
+
|
138
|
+
def _resolve_literal(
|
85
139
|
annotation: Any,
|
140
|
+
) -> AttributeAnnotation:
|
141
|
+
return AttributeAnnotation(
|
142
|
+
origin=Literal,
|
143
|
+
arguments=get_args(annotation),
|
144
|
+
)
|
145
|
+
|
146
|
+
|
147
|
+
def _resolve_forward_ref(
|
148
|
+
annotation: ForwardRef | str,
|
86
149
|
/,
|
87
|
-
self_annotation: AttributeAnnotation | None,
|
88
|
-
type_parameters: dict[str, Any],
|
89
150
|
module: str,
|
90
|
-
|
91
|
-
|
151
|
+
type_parameters: Mapping[str, Any],
|
152
|
+
self_annotation: AttributeAnnotation | None,
|
153
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
92
154
|
) -> AttributeAnnotation:
|
93
|
-
|
155
|
+
forward_ref: ForwardRef
|
94
156
|
match annotation:
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
157
|
+
case str() as string:
|
158
|
+
forward_ref = ForwardRef(string, module=module)
|
159
|
+
|
160
|
+
case reference:
|
161
|
+
forward_ref = reference
|
162
|
+
|
163
|
+
if evaluated := forward_ref._evaluate(
|
164
|
+
globalns=None,
|
165
|
+
localns=None,
|
166
|
+
recursive_guard=frozenset(),
|
167
|
+
):
|
168
|
+
return resolve_attribute_annotation(
|
169
|
+
evaluated,
|
170
|
+
type_parameters=type_parameters,
|
171
|
+
module=module,
|
172
|
+
self_annotation=self_annotation,
|
173
|
+
recursion_guard=recursion_guard,
|
174
|
+
)
|
101
175
|
|
102
|
-
|
103
|
-
|
104
|
-
return resolve_attribute_annotation(
|
105
|
-
ForwardRef(forward_ref, module=module)._evaluate(
|
106
|
-
globalns=None,
|
107
|
-
localns=localns,
|
108
|
-
recursive_guard=frozenset(),
|
109
|
-
),
|
110
|
-
self_annotation=self_annotation,
|
111
|
-
type_parameters=type_parameters,
|
112
|
-
module=module,
|
113
|
-
localns=localns,
|
114
|
-
recursion_guard=recursion_guard, # we might need to update it somehow?
|
115
|
-
)
|
176
|
+
else:
|
177
|
+
raise RuntimeError(f"Cannot resolve annotation of {annotation}")
|
116
178
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
179
|
+
|
180
|
+
def _resolve_generic_alias( # noqa: PLR0911, PLR0912
|
181
|
+
annotation: GenericAlias,
|
182
|
+
/,
|
183
|
+
module: str,
|
184
|
+
type_parameters: Mapping[str, Any],
|
185
|
+
self_annotation: AttributeAnnotation | None,
|
186
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
187
|
+
) -> AttributeAnnotation:
|
188
|
+
match get_origin(annotation):
|
189
|
+
case TypeAliasType() as alias: # pyright: ignore[reportUnnecessaryComparison]
|
190
|
+
return _resolve_type_alias(
|
191
|
+
alias,
|
192
|
+
type_parameters={
|
193
|
+
# verify if we should pass all parameters
|
194
|
+
param.__name__: get_args(annotation)[idx]
|
195
|
+
for idx, param in enumerate(alias.__type_params__)
|
196
|
+
},
|
127
197
|
module=module,
|
128
|
-
|
129
|
-
recursion_guard=recursion_guard,
|
198
|
+
self_annotation=self_annotation,
|
199
|
+
recursion_guard=recursion_guard,
|
130
200
|
)
|
131
201
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
origin=TypeAliasType,
|
139
|
-
arguments=[],
|
140
|
-
)
|
141
|
-
resolved: AttributeAnnotation = resolve_attribute_annotation(
|
142
|
-
alias.__value__,
|
143
|
-
self_annotation=None,
|
144
|
-
type_parameters=type_parameters,
|
145
|
-
module=module,
|
146
|
-
localns=localns,
|
147
|
-
recursion_guard=recursion_guard,
|
202
|
+
case origin if issubclass(origin, Generic):
|
203
|
+
match origin.__class_getitem__( # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
204
|
+
tuple(
|
205
|
+
type_parameters.get(
|
206
|
+
arg.__name__,
|
207
|
+
arg.__bound__ or Any,
|
148
208
|
)
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
case
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
209
|
+
if isinstance(arg, TypeVar)
|
210
|
+
else arg
|
211
|
+
for arg in get_args(annotation)
|
212
|
+
)
|
213
|
+
):
|
214
|
+
case GenericAlias() as generic_alias:
|
215
|
+
resolved_attribute = AttributeAnnotation(origin=generic_alias.__origin__)
|
216
|
+
if recursion_key := _recursion_key(generic_alias):
|
217
|
+
if recursive := recursion_guard.get(recursion_key):
|
218
|
+
return recursive
|
219
|
+
|
220
|
+
else:
|
221
|
+
recursion_guard[recursion_key] = resolved_attribute
|
222
|
+
|
223
|
+
resolved_attribute.arguments = [
|
224
|
+
resolve_attribute_annotation(
|
225
|
+
argument,
|
226
|
+
type_parameters=type_parameters,
|
227
|
+
module=module,
|
228
|
+
self_annotation=self_annotation,
|
229
|
+
recursion_guard=recursion_guard,
|
164
230
|
)
|
165
|
-
|
231
|
+
for argument in get_args(generic_alias)
|
232
|
+
]
|
166
233
|
|
167
|
-
|
168
|
-
# verify if we got any specific type or generic alias again
|
169
|
-
case types.GenericAlias():
|
170
|
-
return AttributeAnnotation(
|
171
|
-
origin=parametrized,
|
172
|
-
arguments=[
|
173
|
-
resolve_attribute_annotation(
|
174
|
-
argument,
|
175
|
-
self_annotation=self_annotation,
|
176
|
-
type_parameters=type_parameters,
|
177
|
-
module=module,
|
178
|
-
localns=localns,
|
179
|
-
recursion_guard=recursion_guard,
|
180
|
-
)
|
181
|
-
for argument in get_args(generic_alias)
|
182
|
-
],
|
183
|
-
)
|
184
|
-
|
185
|
-
# use resolved type if it is not an alias again
|
186
|
-
case _:
|
187
|
-
return AttributeAnnotation(
|
188
|
-
origin=parametrized_type,
|
189
|
-
arguments=[],
|
190
|
-
)
|
191
|
-
|
192
|
-
# anything else - try to resolve a concrete type or use as is
|
193
|
-
case origin:
|
194
|
-
return AttributeAnnotation(
|
195
|
-
origin=origin,
|
196
|
-
arguments=[
|
197
|
-
resolve_attribute_annotation(
|
198
|
-
argument,
|
199
|
-
self_annotation=self_annotation,
|
200
|
-
type_parameters=type_parameters,
|
201
|
-
module=module,
|
202
|
-
localns=localns,
|
203
|
-
recursion_guard=recursion_guard,
|
204
|
-
)
|
205
|
-
for argument in get_args(generic_alias)
|
206
|
-
],
|
207
|
-
)
|
234
|
+
return resolved_attribute
|
208
235
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
236
|
+
# use resolved type if it is not an alias again
|
237
|
+
case resolved: # pyright: ignore
|
238
|
+
resolved_attribute = AttributeAnnotation(origin=resolved)
|
239
|
+
|
240
|
+
if recursion_key := _recursion_key(origin):
|
241
|
+
if recursive := recursion_guard.get(recursion_key):
|
242
|
+
return recursive
|
243
|
+
|
244
|
+
else:
|
245
|
+
recursion_guard[recursion_key] = resolved_attribute
|
246
|
+
|
247
|
+
resolved_attribute.arguments = [
|
248
|
+
resolve_attribute_annotation(
|
249
|
+
argument,
|
250
|
+
type_parameters=type_parameters,
|
251
|
+
module=module,
|
252
|
+
self_annotation=self_annotation,
|
253
|
+
recursion_guard=recursion_guard,
|
254
|
+
)
|
255
|
+
for argument in get_args(annotation)
|
256
|
+
]
|
257
|
+
|
258
|
+
return resolved_attribute
|
259
|
+
|
260
|
+
case origin:
|
261
|
+
resolved_attribute = AttributeAnnotation(origin=origin)
|
262
|
+
|
263
|
+
if recursion_key := _recursion_key(origin):
|
264
|
+
if recursive := recursion_guard.get(recursion_key):
|
265
|
+
return recursive
|
266
|
+
|
267
|
+
resolved_attribute.arguments = [
|
268
|
+
resolve_attribute_annotation(
|
269
|
+
argument,
|
270
|
+
type_parameters=type_parameters,
|
271
|
+
module=module,
|
272
|
+
self_annotation=self_annotation,
|
273
|
+
recursion_guard=recursion_guard,
|
274
|
+
)
|
275
|
+
for argument in get_args(annotation)
|
276
|
+
]
|
277
|
+
|
278
|
+
return resolved_attribute
|
279
|
+
|
280
|
+
|
281
|
+
def _resolve_special_generic_alias(
|
282
|
+
annotation: Any,
|
283
|
+
/,
|
284
|
+
module: str,
|
285
|
+
type_parameters: Mapping[str, Any],
|
286
|
+
self_annotation: AttributeAnnotation | None,
|
287
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
288
|
+
) -> AttributeAnnotation:
|
289
|
+
origin: type[Any] = get_origin(annotation)
|
290
|
+
resolved_attribute = AttributeAnnotation(origin=origin)
|
291
|
+
|
292
|
+
if recursion_key := _recursion_key(origin):
|
293
|
+
if recursive := recursion_guard.get(recursion_key):
|
294
|
+
return recursive
|
295
|
+
|
296
|
+
else:
|
297
|
+
recursion_guard[recursion_key] = resolved_attribute
|
298
|
+
|
299
|
+
resolved_attribute.arguments = [
|
300
|
+
resolve_attribute_annotation(
|
301
|
+
argument,
|
302
|
+
type_parameters=type_parameters,
|
303
|
+
module=module,
|
304
|
+
self_annotation=self_annotation,
|
305
|
+
recursion_guard=recursion_guard,
|
306
|
+
)
|
307
|
+
for argument in get_args(annotation)
|
308
|
+
]
|
309
|
+
|
310
|
+
return resolved_attribute
|
311
|
+
|
312
|
+
|
313
|
+
def _resolve_type_alias(
|
314
|
+
annotation: TypeAliasType,
|
315
|
+
/,
|
316
|
+
module: str,
|
317
|
+
type_parameters: Mapping[str, Any],
|
318
|
+
self_annotation: AttributeAnnotation | None,
|
319
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
320
|
+
) -> AttributeAnnotation:
|
321
|
+
resolved_attribute = AttributeAnnotation(origin=MISSING)
|
322
|
+
|
323
|
+
if recursion_key := _recursion_key(annotation):
|
324
|
+
if recursive := recursion_guard.get(recursion_key):
|
325
|
+
return recursive
|
326
|
+
|
327
|
+
else:
|
328
|
+
recursion_guard[recursion_key] = resolved_attribute
|
329
|
+
|
330
|
+
resolved: AttributeAnnotation = resolve_attribute_annotation(
|
331
|
+
annotation.__value__,
|
332
|
+
module=annotation.__module__ or module,
|
333
|
+
type_parameters=type_parameters,
|
334
|
+
self_annotation=self_annotation,
|
335
|
+
recursion_guard=recursion_guard,
|
336
|
+
)
|
337
|
+
|
338
|
+
resolved_attribute.origin = resolved.origin
|
339
|
+
resolved_attribute.arguments = resolved.arguments
|
340
|
+
resolved_attribute.extra = resolved.extra
|
341
|
+
resolved_attribute.required = resolved.required
|
342
|
+
|
343
|
+
return resolved_attribute
|
344
|
+
|
345
|
+
|
346
|
+
def _resolve_type_var(
|
347
|
+
annotation: TypeVar,
|
348
|
+
/,
|
349
|
+
module: str,
|
350
|
+
type_parameters: Mapping[str, Any],
|
351
|
+
self_annotation: AttributeAnnotation | None,
|
352
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
353
|
+
) -> AttributeAnnotation:
|
354
|
+
return resolve_attribute_annotation(
|
355
|
+
type_parameters.get(
|
356
|
+
annotation.__name__,
|
357
|
+
# use bound as default or Any otherwise
|
358
|
+
annotation.__bound__ or Any,
|
359
|
+
),
|
360
|
+
module=module,
|
361
|
+
type_parameters=type_parameters,
|
362
|
+
self_annotation=self_annotation,
|
363
|
+
recursion_guard=recursion_guard,
|
364
|
+
)
|
365
|
+
|
366
|
+
|
367
|
+
def _resolve_type_union(
|
368
|
+
annotation: UnionType,
|
369
|
+
/,
|
370
|
+
module: str,
|
371
|
+
type_parameters: Mapping[str, Any],
|
372
|
+
self_annotation: AttributeAnnotation | None,
|
373
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
374
|
+
) -> AttributeAnnotation:
|
375
|
+
arguments: Sequence[AttributeAnnotation] = [
|
376
|
+
resolve_attribute_annotation(
|
377
|
+
argument,
|
378
|
+
type_parameters=type_parameters,
|
379
|
+
module=module,
|
380
|
+
self_annotation=self_annotation,
|
381
|
+
recursion_guard=recursion_guard,
|
382
|
+
)
|
383
|
+
for argument in get_args(annotation)
|
384
|
+
]
|
385
|
+
return AttributeAnnotation(
|
386
|
+
origin=UnionType, # pyright: ignore[reportArgumentType]
|
387
|
+
arguments=arguments,
|
388
|
+
required=all(argument.required for argument in arguments),
|
389
|
+
)
|
390
|
+
|
391
|
+
|
392
|
+
def _resolve_callable(
|
393
|
+
annotation: Any,
|
394
|
+
/,
|
395
|
+
module: str,
|
396
|
+
type_parameters: Mapping[str, Any],
|
397
|
+
self_annotation: AttributeAnnotation | None,
|
398
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
399
|
+
) -> AttributeAnnotation:
|
400
|
+
return AttributeAnnotation(
|
401
|
+
origin=Callable,
|
402
|
+
arguments=[
|
403
|
+
resolve_attribute_annotation(
|
404
|
+
argument,
|
218
405
|
type_parameters=type_parameters,
|
219
406
|
module=module,
|
220
|
-
|
407
|
+
self_annotation=self_annotation,
|
221
408
|
recursion_guard=recursion_guard,
|
222
409
|
)
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
410
|
+
for argument in get_args(annotation)
|
411
|
+
],
|
412
|
+
)
|
413
|
+
|
414
|
+
|
415
|
+
def _resolve_type_box(
|
416
|
+
annotation: Any,
|
417
|
+
/,
|
418
|
+
module: str,
|
419
|
+
type_parameters: Mapping[str, Any],
|
420
|
+
self_annotation: AttributeAnnotation | None,
|
421
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
422
|
+
) -> AttributeAnnotation:
|
423
|
+
return resolve_attribute_annotation(
|
424
|
+
get_args(annotation)[0],
|
425
|
+
type_parameters=type_parameters,
|
426
|
+
module=module,
|
427
|
+
self_annotation=self_annotation,
|
428
|
+
recursion_guard=recursion_guard,
|
429
|
+
)
|
430
|
+
|
431
|
+
|
432
|
+
def _resolve_type_not_required(
|
433
|
+
annotation: Any,
|
434
|
+
/,
|
435
|
+
module: str,
|
436
|
+
type_parameters: Mapping[str, Any],
|
437
|
+
self_annotation: AttributeAnnotation | None,
|
438
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
439
|
+
) -> AttributeAnnotation:
|
440
|
+
return resolve_attribute_annotation(
|
441
|
+
get_args(annotation)[0],
|
442
|
+
type_parameters=type_parameters,
|
443
|
+
module=module,
|
444
|
+
self_annotation=self_annotation,
|
445
|
+
recursion_guard=recursion_guard,
|
446
|
+
).update_required(False)
|
447
|
+
|
448
|
+
|
449
|
+
def _resolve_type_optional(
|
450
|
+
annotation: Any,
|
451
|
+
/,
|
452
|
+
module: str,
|
453
|
+
type_parameters: Mapping[str, Any],
|
454
|
+
self_annotation: AttributeAnnotation | None,
|
455
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
456
|
+
) -> AttributeAnnotation:
|
457
|
+
return AttributeAnnotation(
|
458
|
+
origin=UnionType, # pyright: ignore[reportArgumentType]
|
459
|
+
arguments=[
|
460
|
+
resolve_attribute_annotation(
|
461
|
+
get_args(annotation)[0],
|
237
462
|
type_parameters=type_parameters,
|
238
463
|
module=module,
|
239
|
-
|
464
|
+
self_annotation=self_annotation,
|
240
465
|
recursion_guard=recursion_guard,
|
241
|
-
)
|
466
|
+
),
|
467
|
+
AttributeAnnotation(origin=NoneType),
|
468
|
+
],
|
469
|
+
)
|
242
470
|
|
243
|
-
case typing.ParamSpec():
|
244
|
-
sys.stderr.write(
|
245
|
-
"ParamSpec is not supported for attribute annotations,"
|
246
|
-
" ignoring with Any type - it might incorrectly validate types\n"
|
247
|
-
)
|
248
|
-
return AttributeAnnotation(
|
249
|
-
origin=Any,
|
250
|
-
arguments=[],
|
251
|
-
)
|
252
471
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
472
|
+
def _resolve_type_typeddict(
|
473
|
+
annotation: Any,
|
474
|
+
/,
|
475
|
+
module: str,
|
476
|
+
type_parameters: Mapping[str, Any],
|
477
|
+
self_annotation: AttributeAnnotation | None,
|
478
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
479
|
+
) -> AttributeAnnotation:
|
480
|
+
resolved_attribute = AttributeAnnotation(origin=annotation)
|
481
|
+
|
482
|
+
if recursion_key := _recursion_key(annotation):
|
483
|
+
if recursive := recursion_guard.get(recursion_key):
|
484
|
+
return recursive
|
485
|
+
|
486
|
+
else:
|
487
|
+
recursion_guard[recursion_key] = resolved_attribute
|
488
|
+
|
489
|
+
resolved_attribute.arguments = [
|
490
|
+
resolve_attribute_annotation(
|
491
|
+
argument,
|
492
|
+
type_parameters=type_parameters,
|
493
|
+
module=module,
|
494
|
+
self_annotation=self_annotation,
|
495
|
+
recursion_guard=recursion_guard,
|
496
|
+
)
|
497
|
+
for argument in get_args(annotation)
|
498
|
+
]
|
499
|
+
|
500
|
+
attributes: dict[str, AttributeAnnotation] = {}
|
501
|
+
for key, element in get_type_hints(
|
502
|
+
annotation,
|
503
|
+
localns={annotation.__name__: annotation},
|
504
|
+
).items():
|
505
|
+
attributes[key] = resolve_attribute_annotation(
|
506
|
+
element,
|
507
|
+
type_parameters=type_parameters,
|
508
|
+
module=getattr(annotation, "__module__", module),
|
509
|
+
self_annotation=resolved_attribute,
|
510
|
+
recursion_guard=recursion_guard,
|
511
|
+
).update_required(key in annotation.__required_keys__)
|
512
|
+
resolved_attribute.extra = attributes
|
513
|
+
return resolved_attribute
|
514
|
+
|
515
|
+
|
516
|
+
def _resolve_type(
|
517
|
+
annotation: Any,
|
518
|
+
/,
|
519
|
+
module: str,
|
520
|
+
type_parameters: Mapping[str, Any],
|
521
|
+
self_annotation: AttributeAnnotation | None,
|
522
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
523
|
+
) -> AttributeAnnotation:
|
524
|
+
if recursion_key := _recursion_key(annotation):
|
525
|
+
if recursive := recursion_guard.get(recursion_key):
|
526
|
+
return recursive
|
527
|
+
|
528
|
+
# not updating recursion guard here - it might be a builtin type
|
529
|
+
|
530
|
+
return AttributeAnnotation(
|
531
|
+
origin=annotation,
|
532
|
+
arguments=[
|
533
|
+
resolve_attribute_annotation(
|
534
|
+
argument,
|
535
|
+
type_parameters=type_parameters,
|
536
|
+
module=module,
|
537
|
+
self_annotation=self_annotation,
|
538
|
+
recursion_guard=recursion_guard,
|
261
539
|
)
|
540
|
+
for argument in get_args(annotation)
|
541
|
+
],
|
542
|
+
)
|
262
543
|
|
263
|
-
case _:
|
264
|
-
pass # proceed to resolving based on origin
|
265
544
|
|
266
|
-
|
545
|
+
def resolve_attribute_annotation( # noqa: C901, PLR0911, PLR0912
|
546
|
+
annotation: Any,
|
547
|
+
/,
|
548
|
+
module: str,
|
549
|
+
type_parameters: Mapping[str, Any],
|
550
|
+
self_annotation: AttributeAnnotation | None,
|
551
|
+
recursion_guard: MutableMapping[str, AttributeAnnotation],
|
552
|
+
) -> AttributeAnnotation:
|
267
553
|
match get_origin(annotation) or annotation:
|
268
|
-
case types.
|
269
|
-
return
|
270
|
-
|
271
|
-
arguments=[
|
272
|
-
recursion_guard.get(
|
273
|
-
argument,
|
274
|
-
resolve_attribute_annotation(
|
275
|
-
argument,
|
276
|
-
self_annotation=self_annotation,
|
277
|
-
type_parameters=type_parameters,
|
278
|
-
module=module,
|
279
|
-
localns=localns,
|
280
|
-
recursion_guard=recursion_guard,
|
281
|
-
),
|
282
|
-
)
|
283
|
-
for argument in get_args(annotation)
|
284
|
-
],
|
554
|
+
case types.NoneType | None:
|
555
|
+
return _resolve_none(
|
556
|
+
annotation=annotation,
|
285
557
|
)
|
286
558
|
|
287
|
-
case
|
288
|
-
return
|
289
|
-
|
290
|
-
arguments=[
|
291
|
-
resolve_attribute_annotation(
|
292
|
-
argument,
|
293
|
-
self_annotation=self_annotation,
|
294
|
-
type_parameters=type_parameters,
|
295
|
-
module=module,
|
296
|
-
localns=localns,
|
297
|
-
recursion_guard=recursion_guard,
|
298
|
-
)
|
299
|
-
for argument in get_args(annotation)
|
300
|
-
],
|
559
|
+
case haiway_types.Missing:
|
560
|
+
return _resolve_missing(
|
561
|
+
annotation=annotation,
|
301
562
|
)
|
302
563
|
|
303
|
-
case typing.
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
arguments=[],
|
312
|
-
)
|
564
|
+
case types.UnionType | typing.Union:
|
565
|
+
return _resolve_type_union(
|
566
|
+
annotation,
|
567
|
+
module=module,
|
568
|
+
type_parameters=type_parameters,
|
569
|
+
self_annotation=self_annotation,
|
570
|
+
recursion_guard=recursion_guard,
|
571
|
+
)
|
313
572
|
|
314
|
-
|
573
|
+
case typing.Literal:
|
574
|
+
return _resolve_literal(annotation)
|
315
575
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
576
|
+
case typeddict if is_typeddict(typeddict):
|
577
|
+
return _resolve_type_typeddict(
|
578
|
+
typeddict,
|
579
|
+
module=module,
|
580
|
+
type_parameters=type_parameters,
|
320
581
|
self_annotation=self_annotation,
|
582
|
+
recursion_guard=recursion_guard,
|
583
|
+
)
|
584
|
+
|
585
|
+
case typing.Callable: # pyright: ignore
|
586
|
+
return _resolve_callable(
|
587
|
+
annotation,
|
588
|
+
module=module,
|
321
589
|
type_parameters=type_parameters,
|
590
|
+
self_annotation=self_annotation,
|
591
|
+
recursion_guard=recursion_guard,
|
592
|
+
)
|
593
|
+
|
594
|
+
case typing.Annotated | typing.Final | typing.Required:
|
595
|
+
return _resolve_type_box(
|
596
|
+
annotation,
|
322
597
|
module=module,
|
323
|
-
|
598
|
+
type_parameters=type_parameters,
|
599
|
+
self_annotation=self_annotation,
|
600
|
+
recursion_guard=recursion_guard,
|
601
|
+
)
|
602
|
+
|
603
|
+
case typing.NotRequired:
|
604
|
+
return _resolve_type_not_required(
|
605
|
+
annotation,
|
606
|
+
module=module,
|
607
|
+
type_parameters=type_parameters,
|
608
|
+
self_annotation=self_annotation,
|
324
609
|
recursion_guard=recursion_guard,
|
325
610
|
)
|
326
611
|
|
327
612
|
case typing.Optional: # optional is a Union[Value, None]
|
328
|
-
return
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
613
|
+
return _resolve_type_optional(
|
614
|
+
annotation,
|
615
|
+
module=module,
|
616
|
+
type_parameters=type_parameters,
|
617
|
+
self_annotation=self_annotation,
|
618
|
+
recursion_guard=recursion_guard,
|
619
|
+
)
|
620
|
+
|
621
|
+
case typing.Self: # pyright: ignore
|
622
|
+
if self_annotation:
|
623
|
+
return self_annotation
|
624
|
+
|
625
|
+
else:
|
626
|
+
raise RuntimeError(f"Unresolved Self annotation: {annotation}")
|
627
|
+
|
628
|
+
case _:
|
629
|
+
match annotation:
|
630
|
+
case str() | ForwardRef():
|
631
|
+
return _resolve_forward_ref(
|
632
|
+
annotation,
|
633
|
+
module=module,
|
334
634
|
type_parameters=type_parameters,
|
635
|
+
self_annotation=self_annotation,
|
636
|
+
recursion_guard=recursion_guard,
|
637
|
+
)
|
638
|
+
|
639
|
+
case GenericAlias():
|
640
|
+
return _resolve_generic_alias(
|
641
|
+
annotation,
|
335
642
|
module=module,
|
336
|
-
|
643
|
+
type_parameters=type_parameters,
|
644
|
+
self_annotation=self_annotation,
|
337
645
|
recursion_guard=recursion_guard,
|
338
|
-
)
|
339
|
-
AttributeAnnotation(
|
340
|
-
origin=NoneType,
|
341
|
-
arguments=[],
|
342
|
-
),
|
343
|
-
],
|
344
|
-
)
|
646
|
+
)
|
345
647
|
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
648
|
+
case _GenericAlias():
|
649
|
+
return _resolve_special_generic_alias(
|
650
|
+
annotation,
|
651
|
+
module=module,
|
652
|
+
type_parameters=type_parameters,
|
653
|
+
self_annotation=self_annotation,
|
654
|
+
recursion_guard=recursion_guard,
|
655
|
+
)
|
351
656
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
argument,
|
657
|
+
case TypeAliasType():
|
658
|
+
return _resolve_type_alias(
|
659
|
+
annotation,
|
660
|
+
module=module,
|
661
|
+
type_parameters=type_parameters,
|
358
662
|
self_annotation=self_annotation,
|
663
|
+
recursion_guard=recursion_guard,
|
664
|
+
)
|
665
|
+
|
666
|
+
case TypeVar():
|
667
|
+
return _resolve_type_var(
|
668
|
+
annotation,
|
669
|
+
module=module,
|
359
670
|
type_parameters=type_parameters,
|
671
|
+
self_annotation=self_annotation,
|
672
|
+
recursion_guard=recursion_guard,
|
673
|
+
)
|
674
|
+
|
675
|
+
case ParamSpec():
|
676
|
+
raise NotImplementedError(f"Unresolved ParamSpec annotation: {annotation}")
|
677
|
+
|
678
|
+
case TypeVarTuple():
|
679
|
+
raise NotImplementedError(f"Unresolved TypeVarTuple annotation: {annotation}")
|
680
|
+
|
681
|
+
case _: # finally use whatever there was
|
682
|
+
return _resolve_type(
|
683
|
+
annotation,
|
360
684
|
module=module,
|
361
|
-
|
685
|
+
type_parameters=type_parameters,
|
686
|
+
self_annotation=self_annotation,
|
362
687
|
recursion_guard=recursion_guard,
|
363
688
|
)
|
364
|
-
|
365
|
-
|
689
|
+
|
690
|
+
|
691
|
+
@overload
|
692
|
+
def _recursion_key(
|
693
|
+
annotation: Any,
|
694
|
+
/,
|
695
|
+
) -> str | None: ...
|
696
|
+
|
697
|
+
|
698
|
+
@overload
|
699
|
+
def _recursion_key(
|
700
|
+
annotation: Any,
|
701
|
+
/,
|
702
|
+
default: str,
|
703
|
+
) -> str: ...
|
704
|
+
|
705
|
+
|
706
|
+
def _recursion_key(
|
707
|
+
annotation: Any,
|
708
|
+
/,
|
709
|
+
default: str | None = None,
|
710
|
+
) -> str | None:
|
711
|
+
args_suffix: str
|
712
|
+
if arguments := get_args(annotation):
|
713
|
+
arguments_string: str = ", ".join(
|
714
|
+
_recursion_key(
|
715
|
+
argument,
|
716
|
+
default="?",
|
366
717
|
)
|
718
|
+
for argument in arguments
|
719
|
+
)
|
720
|
+
args_suffix = f"[{arguments_string}]"
|
721
|
+
|
722
|
+
else:
|
723
|
+
args_suffix = ""
|
724
|
+
|
725
|
+
if qualname := getattr(annotation, "__qualname__", None):
|
726
|
+
return qualname + args_suffix
|
727
|
+
|
728
|
+
module_prefix: str
|
729
|
+
if module := getattr(annotation, "__module__", None):
|
730
|
+
module_prefix = module + "."
|
731
|
+
|
732
|
+
else:
|
733
|
+
module_prefix = ""
|
734
|
+
|
735
|
+
if name := getattr(annotation, "__name__", None):
|
736
|
+
return module_prefix + name + args_suffix
|
737
|
+
|
738
|
+
return default
|