structtype 0.3.0__cp315-cp315-win_amd64.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.
- structtype/__init__.py +17 -0
- structtype/__init__.pyi +350 -0
- structtype/_adapter.py +128 -0
- structtype/_core.cp315-win_amd64.pyd +0 -0
- structtype/_inspect.py +1183 -0
- structtype/_json_schema.py +532 -0
- structtype/_utils.py +410 -0
- structtype/_version.py +24 -0
- structtype/py.typed +0 -0
- structtype-0.3.0.dist-info/METADATA +80 -0
- structtype-0.3.0.dist-info/RECORD +14 -0
- structtype-0.3.0.dist-info/WHEEL +5 -0
- structtype-0.3.0.dist-info/licenses/LICENSE +27 -0
- structtype-0.3.0.dist-info/top_level.txt +1 -0
structtype/_inspect.py
ADDED
|
@@ -0,0 +1,1183 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import decimal
|
|
3
|
+
import enum
|
|
4
|
+
import sys
|
|
5
|
+
import uuid
|
|
6
|
+
from collections.abc import Iterable
|
|
7
|
+
from types import UnionType as _types_UnionType
|
|
8
|
+
from typing import Any, Final, Literal, TypeVar, Union
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from typing import TypeAliasType as _TypeAliasType # type: ignore
|
|
12
|
+
except Exception:
|
|
13
|
+
_TypeAliasType = type("TypeAliasType", (), {}) # type: ignore
|
|
14
|
+
|
|
15
|
+
import structtype
|
|
16
|
+
from structtype import NODEFAULT, UNSET
|
|
17
|
+
from structtype import UnsetType as _UnsetType
|
|
18
|
+
|
|
19
|
+
from ._core import ( # type: ignore
|
|
20
|
+
Factory as _Factory,
|
|
21
|
+
)
|
|
22
|
+
from ._core import (
|
|
23
|
+
Struct,
|
|
24
|
+
StructMeta,
|
|
25
|
+
_dump,
|
|
26
|
+
)
|
|
27
|
+
from ._utils import ( # type: ignore
|
|
28
|
+
_CONCRETE_TYPES,
|
|
29
|
+
_AnnotatedAlias,
|
|
30
|
+
)
|
|
31
|
+
from ._utils import (
|
|
32
|
+
get_class_annotations as _get_class_annotations,
|
|
33
|
+
)
|
|
34
|
+
from ._utils import (
|
|
35
|
+
get_dataclass_info as _get_dataclass_info,
|
|
36
|
+
)
|
|
37
|
+
from ._utils import (
|
|
38
|
+
get_pydantic_info as _get_pydantic_info,
|
|
39
|
+
)
|
|
40
|
+
from ._utils import (
|
|
41
|
+
get_typeddict_info as _get_typeddict_info,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
__all__ = (
|
|
45
|
+
"AnyType",
|
|
46
|
+
"BoolType",
|
|
47
|
+
"ByteArrayType",
|
|
48
|
+
"BytesType",
|
|
49
|
+
"CollectionType",
|
|
50
|
+
"CustomType",
|
|
51
|
+
"DataclassType",
|
|
52
|
+
"DateTimeType",
|
|
53
|
+
"DateType",
|
|
54
|
+
"DecimalType",
|
|
55
|
+
"DictType",
|
|
56
|
+
"EnumType",
|
|
57
|
+
"FieldInfo",
|
|
58
|
+
"FieldNode",
|
|
59
|
+
"FloatType",
|
|
60
|
+
"FrozenDictType",
|
|
61
|
+
"FrozenSetType",
|
|
62
|
+
"IntType",
|
|
63
|
+
"ListType",
|
|
64
|
+
"LiteralType",
|
|
65
|
+
"MemoryViewType",
|
|
66
|
+
"Metadata",
|
|
67
|
+
"NamedTupleType",
|
|
68
|
+
"NoneType",
|
|
69
|
+
"PydanticType",
|
|
70
|
+
"RawType",
|
|
71
|
+
"SetType",
|
|
72
|
+
"StrType",
|
|
73
|
+
"StructType",
|
|
74
|
+
"TimeDeltaType",
|
|
75
|
+
"TimeType",
|
|
76
|
+
"TupleType",
|
|
77
|
+
"Type",
|
|
78
|
+
"TypedDictType",
|
|
79
|
+
"UUIDType",
|
|
80
|
+
"UnionType",
|
|
81
|
+
"VarTupleType",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
_PY315_PLUS = sys.version_info >= (3, 15)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def __dir__():
|
|
88
|
+
return __all__
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class Type(structtype.Struct):
|
|
92
|
+
"""The base Type."""
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class Metadata(Type):
|
|
96
|
+
"""A type wrapping a subtype with additional metadata.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
type: Type
|
|
101
|
+
The subtype.
|
|
102
|
+
json_schema_extra: dict, optional
|
|
103
|
+
A dict of extra fields to set for the subtype when generating a
|
|
104
|
+
json-schema.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
type: Type
|
|
108
|
+
json_schema_extra: dict | None = None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class AnyType(Type):
|
|
112
|
+
"""A type corresponding to `typing.Any`."""
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class NoneType(Type):
|
|
116
|
+
"""A type corresponding to `None`."""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class BoolType(Type):
|
|
120
|
+
"""A type corresponding to `bool`."""
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class IntType(Type):
|
|
124
|
+
"""A type corresponding to `int`.
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
gt: int, optional
|
|
129
|
+
If set, an instance of this type must be greater than ``gt``.
|
|
130
|
+
ge: int, optional
|
|
131
|
+
If set, an instance of this type must be greater than or equal to ``ge``.
|
|
132
|
+
lt: int, optional
|
|
133
|
+
If set, an instance of this type must be less than to ``lt``.
|
|
134
|
+
le: int, optional
|
|
135
|
+
If set, an instance of this type must be less than or equal to ``le``.
|
|
136
|
+
multiple_of: int, optional
|
|
137
|
+
If set, an instance of this type must be a multiple of ``multiple_of``.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
gt: int | None = None
|
|
141
|
+
ge: int | None = None
|
|
142
|
+
lt: int | None = None
|
|
143
|
+
le: int | None = None
|
|
144
|
+
multiple_of: int | None = None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class FloatType(Type):
|
|
148
|
+
"""A type corresponding to `float`.
|
|
149
|
+
|
|
150
|
+
Parameters
|
|
151
|
+
----------
|
|
152
|
+
gt: float, optional
|
|
153
|
+
If set, an instance of this type must be greater than ``gt``.
|
|
154
|
+
ge: float, optional
|
|
155
|
+
If set, an instance of this type must be greater than or equal to ``ge``.
|
|
156
|
+
lt: float, optional
|
|
157
|
+
If set, an instance of this type must be less than to ``lt``.
|
|
158
|
+
le: float, optional
|
|
159
|
+
If set, an instance of this type must be less than or equal to ``le``.
|
|
160
|
+
multiple_of: float, optional
|
|
161
|
+
If set, an instance of this type must be a multiple of ``multiple_of``.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
gt: float | None = None
|
|
165
|
+
ge: float | None = None
|
|
166
|
+
lt: float | None = None
|
|
167
|
+
le: float | None = None
|
|
168
|
+
multiple_of: float | None = None
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class StrType(Type):
|
|
172
|
+
"""A type corresponding to `str`.
|
|
173
|
+
|
|
174
|
+
Parameters
|
|
175
|
+
----------
|
|
176
|
+
min_length: int, optional
|
|
177
|
+
If set, an instance of this type must have length greater than or equal
|
|
178
|
+
to ``min_length``.
|
|
179
|
+
max_length: int, optional
|
|
180
|
+
If set, an instance of this type must have length less than or equal
|
|
181
|
+
to ``max_length``.
|
|
182
|
+
pattern: str, optional
|
|
183
|
+
If set, an instance of this type must match against this regex pattern.
|
|
184
|
+
Note that the pattern is treated as **unanchored**.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
min_length: int | None = None
|
|
188
|
+
max_length: int | None = None
|
|
189
|
+
pattern: str | None = None
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class BytesType(Type):
|
|
193
|
+
"""A type corresponding to `bytes`.
|
|
194
|
+
|
|
195
|
+
Parameters
|
|
196
|
+
----------
|
|
197
|
+
min_length: int, optional
|
|
198
|
+
If set, an instance of this type must have length greater than or equal
|
|
199
|
+
to ``min_length``.
|
|
200
|
+
max_length: int, optional
|
|
201
|
+
If set, an instance of this type must have length less than or equal
|
|
202
|
+
to ``max_length``.
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
min_length: int | None = None
|
|
206
|
+
max_length: int | None = None
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class ByteArrayType(Type):
|
|
210
|
+
"""A type corresponding to `bytearray`.
|
|
211
|
+
|
|
212
|
+
Parameters
|
|
213
|
+
----------
|
|
214
|
+
min_length: int, optional
|
|
215
|
+
If set, an instance of this type must have length greater than or equal
|
|
216
|
+
to ``min_length``.
|
|
217
|
+
max_length: int, optional
|
|
218
|
+
If set, an instance of this type must have length less than or equal
|
|
219
|
+
to ``max_length``.
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
min_length: int | None = None
|
|
223
|
+
max_length: int | None = None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class MemoryViewType(Type):
|
|
227
|
+
"""A type corresponding to `memoryview`.
|
|
228
|
+
|
|
229
|
+
Parameters
|
|
230
|
+
----------
|
|
231
|
+
min_length: int, optional
|
|
232
|
+
If set, an instance of this type must have length greater than or equal
|
|
233
|
+
to ``min_length``.
|
|
234
|
+
max_length: int, optional
|
|
235
|
+
If set, an instance of this type must have length less than or equal
|
|
236
|
+
to ``max_length``.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
min_length: int | None = None
|
|
240
|
+
max_length: int | None = None
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class DateTimeType(Type):
|
|
244
|
+
"""A type corresponding to `datetime.datetime`.
|
|
245
|
+
|
|
246
|
+
Parameters
|
|
247
|
+
----------
|
|
248
|
+
tz: bool
|
|
249
|
+
The timezone-requirements for an instance of this type. ``True``
|
|
250
|
+
indicates a timezone-aware value is required, ``False`` indicates a
|
|
251
|
+
timezone-naive value is required. The default is ``None``, which
|
|
252
|
+
accepts either timezone-aware or timezone-naive values.
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
tz: bool | None = None
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class TimeType(Type):
|
|
259
|
+
"""A type corresponding to `datetime.time`.
|
|
260
|
+
|
|
261
|
+
Parameters
|
|
262
|
+
----------
|
|
263
|
+
tz: bool
|
|
264
|
+
The timezone-requirements for an instance of this type. ``True``
|
|
265
|
+
indicates a timezone-aware value is required, ``False`` indicates a
|
|
266
|
+
timezone-aware value is required. The default is ``None``, which
|
|
267
|
+
accepts either timezone-aware or timezone-naive values.
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
tz: bool | None = None
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class DateType(Type):
|
|
274
|
+
"""A type corresponding to `datetime.date`."""
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class TimeDeltaType(Type):
|
|
278
|
+
"""A type corresponding to `datetime.timedelta`."""
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class UUIDType(Type):
|
|
282
|
+
"""A type corresponding to `uuid.UUID`."""
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class DecimalType(Type):
|
|
286
|
+
"""A type corresponding to `decimal.Decimal`."""
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class RawType(Type):
|
|
290
|
+
"""A type corresponding to `structtype.Raw`."""
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class EnumType(Type):
|
|
294
|
+
"""A type corresponding to an `enum.Enum` type.
|
|
295
|
+
|
|
296
|
+
Parameters
|
|
297
|
+
----------
|
|
298
|
+
cls: type
|
|
299
|
+
The corresponding `enum.Enum` type.
|
|
300
|
+
"""
|
|
301
|
+
|
|
302
|
+
cls: type[enum.Enum]
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class LiteralType(Type):
|
|
306
|
+
"""A type corresponding to a `typing.Literal` type.
|
|
307
|
+
|
|
308
|
+
Parameters
|
|
309
|
+
----------
|
|
310
|
+
values: tuple
|
|
311
|
+
A tuple of possible values for this literal instance. Only `bool`,
|
|
312
|
+
`str`, `int`, or `None` literals are supported, and a single literal
|
|
313
|
+
may mix these types (e.g. ``Literal[1, None]``).
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
values: tuple[bool | str | int | None, ...]
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class CustomType(Type):
|
|
320
|
+
"""A custom type.
|
|
321
|
+
|
|
322
|
+
Parameters
|
|
323
|
+
----------
|
|
324
|
+
cls: type
|
|
325
|
+
The corresponding custom type.
|
|
326
|
+
"""
|
|
327
|
+
|
|
328
|
+
cls: type
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
class UnionType(Type):
|
|
332
|
+
"""A union type.
|
|
333
|
+
|
|
334
|
+
Parameters
|
|
335
|
+
----------
|
|
336
|
+
types: tuple[type, ...]
|
|
337
|
+
A tuple of possible types for this union.
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
types: tuple[Type, ...]
|
|
341
|
+
|
|
342
|
+
@property
|
|
343
|
+
def includes_none(self) -> bool:
|
|
344
|
+
"""A helper for checking whether ``None`` is included in this union."""
|
|
345
|
+
return any(isinstance(t, NoneType) for t in self.types)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
class CollectionType(Type):
|
|
349
|
+
"""A collection type.
|
|
350
|
+
|
|
351
|
+
This is the base type shared by collection types like `ListType`,
|
|
352
|
+
`SetType`, etc.
|
|
353
|
+
|
|
354
|
+
Parameters
|
|
355
|
+
----------
|
|
356
|
+
item_type: Type
|
|
357
|
+
The item type.
|
|
358
|
+
min_length: int, optional
|
|
359
|
+
If set, an instance of this type must have length greater than or equal
|
|
360
|
+
to ``min_length``.
|
|
361
|
+
max_length: int, optional
|
|
362
|
+
If set, an instance of this type must have length less than or equal
|
|
363
|
+
to ``max_length``.
|
|
364
|
+
"""
|
|
365
|
+
|
|
366
|
+
item_type: Type
|
|
367
|
+
min_length: int | None = None
|
|
368
|
+
max_length: int | None = None
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
class ListType(CollectionType):
|
|
372
|
+
"""A type corresponding to a `list`.
|
|
373
|
+
|
|
374
|
+
Parameters
|
|
375
|
+
----------
|
|
376
|
+
item_type: Type
|
|
377
|
+
The item type.
|
|
378
|
+
min_length: int, optional
|
|
379
|
+
If set, an instance of this type must have length greater than or equal
|
|
380
|
+
to ``min_length``.
|
|
381
|
+
max_length: int, optional
|
|
382
|
+
If set, an instance of this type must have length less than or equal
|
|
383
|
+
to ``max_length``.
|
|
384
|
+
"""
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
class VarTupleType(CollectionType):
|
|
388
|
+
"""A type corresponding to a variadic `tuple`.
|
|
389
|
+
|
|
390
|
+
Parameters
|
|
391
|
+
----------
|
|
392
|
+
item_type: Type
|
|
393
|
+
The item type.
|
|
394
|
+
min_length: int, optional
|
|
395
|
+
If set, an instance of this type must have length greater than or equal
|
|
396
|
+
to ``min_length``.
|
|
397
|
+
max_length: int, optional
|
|
398
|
+
If set, an instance of this type must have length less than or equal
|
|
399
|
+
to ``max_length``.
|
|
400
|
+
"""
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
class SetType(CollectionType):
|
|
404
|
+
"""A type corresponding to a `set`.
|
|
405
|
+
|
|
406
|
+
Parameters
|
|
407
|
+
----------
|
|
408
|
+
item_type: Type
|
|
409
|
+
The item type.
|
|
410
|
+
min_length: int, optional
|
|
411
|
+
If set, an instance of this type must have length greater than or equal
|
|
412
|
+
to ``min_length``.
|
|
413
|
+
max_length: int, optional
|
|
414
|
+
If set, an instance of this type must have length less than or equal
|
|
415
|
+
to ``max_length``.
|
|
416
|
+
"""
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
class FrozenSetType(CollectionType):
|
|
420
|
+
"""A type corresponding to a `frozenset`.
|
|
421
|
+
|
|
422
|
+
Parameters
|
|
423
|
+
----------
|
|
424
|
+
item_type: Type
|
|
425
|
+
The item type.
|
|
426
|
+
min_length: int, optional
|
|
427
|
+
If set, an instance of this type must have length greater than or equal
|
|
428
|
+
to ``min_length``.
|
|
429
|
+
max_length: int, optional
|
|
430
|
+
If set, an instance of this type must have length less than or equal
|
|
431
|
+
to ``max_length``.
|
|
432
|
+
"""
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
class TupleType(Type):
|
|
436
|
+
"""A type corresponding to `tuple`.
|
|
437
|
+
|
|
438
|
+
Parameters
|
|
439
|
+
----------
|
|
440
|
+
item_types: tuple[Type, ...]
|
|
441
|
+
A tuple of types for each element in the tuple.
|
|
442
|
+
"""
|
|
443
|
+
|
|
444
|
+
item_types: tuple[Type, ...]
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
class DictType(Type):
|
|
448
|
+
"""A type corresponding to `dict`.
|
|
449
|
+
|
|
450
|
+
Parameters
|
|
451
|
+
----------
|
|
452
|
+
key_type: Type
|
|
453
|
+
The key type.
|
|
454
|
+
value_type: Type
|
|
455
|
+
The value type.
|
|
456
|
+
min_length: int, optional
|
|
457
|
+
If set, an instance of this type must have length greater than or equal
|
|
458
|
+
to ``min_length``.
|
|
459
|
+
max_length: int, optional
|
|
460
|
+
If set, an instance of this type must have length less than or equal
|
|
461
|
+
to ``max_length``.
|
|
462
|
+
"""
|
|
463
|
+
|
|
464
|
+
key_type: Type
|
|
465
|
+
value_type: Type
|
|
466
|
+
min_length: int | None = None
|
|
467
|
+
max_length: int | None = None
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
class FrozenDictType(Type):
|
|
471
|
+
"""A type corresponding to `frozendict`.
|
|
472
|
+
|
|
473
|
+
Can only be emitted on Python 3.15+.
|
|
474
|
+
|
|
475
|
+
Parameters
|
|
476
|
+
----------
|
|
477
|
+
key_type: Type
|
|
478
|
+
The key type.
|
|
479
|
+
value_type: Type
|
|
480
|
+
The value type.
|
|
481
|
+
min_length: int, optional
|
|
482
|
+
If set, an instance of this type must have length greater than or equal
|
|
483
|
+
to ``min_length``.
|
|
484
|
+
max_length: int, optional
|
|
485
|
+
If set, an instance of this type must have length less than or equal
|
|
486
|
+
to ``max_length``.
|
|
487
|
+
"""
|
|
488
|
+
|
|
489
|
+
key_type: Type
|
|
490
|
+
value_type: Type
|
|
491
|
+
min_length: Union[int, None] = None
|
|
492
|
+
max_length: Union[int, None] = None
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
class FieldNode(structtype.Struct):
|
|
496
|
+
"""A record describing a field in an object-like type.
|
|
497
|
+
|
|
498
|
+
Parameters
|
|
499
|
+
----------
|
|
500
|
+
name: str
|
|
501
|
+
The field name as seen by Python code (e.g. ``field_one``).
|
|
502
|
+
encode_name: str
|
|
503
|
+
The name used when encoding/decoding the field. This may differ if
|
|
504
|
+
the field is renamed (e.g. ``fieldOne``).
|
|
505
|
+
type: Type
|
|
506
|
+
The field type.
|
|
507
|
+
required: bool, optional
|
|
508
|
+
Whether the field is required. Note that if `required` is False doesn't
|
|
509
|
+
necessarily mean that `default` or `default_factory` will be set -
|
|
510
|
+
optional fields may exist with no default value.
|
|
511
|
+
default: Any, optional
|
|
512
|
+
A default value for the field. Will be `NODEFAULT` if no default value
|
|
513
|
+
is set.
|
|
514
|
+
default_factory: Any, optional
|
|
515
|
+
A callable that creates a default value for the field. Will be
|
|
516
|
+
`NODEFAULT` if no ``default_factory`` is set.
|
|
517
|
+
"""
|
|
518
|
+
|
|
519
|
+
_FIELD_UNSET = object()
|
|
520
|
+
|
|
521
|
+
name: str
|
|
522
|
+
encode_name: str
|
|
523
|
+
type: Type
|
|
524
|
+
required: bool = True
|
|
525
|
+
default: Any = _FIELD_UNSET
|
|
526
|
+
default_factory: Any = _FIELD_UNSET
|
|
527
|
+
|
|
528
|
+
def __post_init__(self):
|
|
529
|
+
if self.default is self._FIELD_UNSET:
|
|
530
|
+
self.default = NODEFAULT
|
|
531
|
+
if self.default_factory is self._FIELD_UNSET:
|
|
532
|
+
self.default_factory = NODEFAULT
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
class TypedDictType(Type):
|
|
536
|
+
"""A type corresponding to a `typing.TypedDict` type.
|
|
537
|
+
|
|
538
|
+
Parameters
|
|
539
|
+
----------
|
|
540
|
+
cls: type
|
|
541
|
+
The corresponding TypedDict type.
|
|
542
|
+
fields: tuple[FieldNode, ...]
|
|
543
|
+
A tuple of fields in the TypedDict.
|
|
544
|
+
"""
|
|
545
|
+
|
|
546
|
+
cls: type
|
|
547
|
+
fields: tuple[FieldNode, ...]
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
class NamedTupleType(Type):
|
|
551
|
+
"""A type corresponding to a `typing.NamedTuple` type.
|
|
552
|
+
|
|
553
|
+
Parameters
|
|
554
|
+
----------
|
|
555
|
+
cls: type
|
|
556
|
+
The corresponding NamedTuple type.
|
|
557
|
+
fields: tuple[FieldNode, ...]
|
|
558
|
+
A tuple of fields in the NamedTuple.
|
|
559
|
+
"""
|
|
560
|
+
|
|
561
|
+
cls: type
|
|
562
|
+
fields: tuple[FieldNode, ...]
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
class DataclassType(Type):
|
|
566
|
+
"""A type corresponding to a `dataclasses` or `attrs` type.
|
|
567
|
+
|
|
568
|
+
Parameters
|
|
569
|
+
----------
|
|
570
|
+
cls: type
|
|
571
|
+
The corresponding dataclass type.
|
|
572
|
+
fields: tuple[FieldNode, ...]
|
|
573
|
+
A tuple of fields in the dataclass.
|
|
574
|
+
"""
|
|
575
|
+
|
|
576
|
+
cls: type
|
|
577
|
+
fields: tuple[FieldNode, ...]
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
class PydanticType(Type):
|
|
581
|
+
"""A type corresponding to a Pydantic v2 BaseModel type.
|
|
582
|
+
|
|
583
|
+
Parameters
|
|
584
|
+
----------
|
|
585
|
+
cls: type
|
|
586
|
+
The corresponding Pydantic model type.
|
|
587
|
+
fields: tuple[FieldNode, ...]
|
|
588
|
+
A tuple of fields in the Pydantic model.
|
|
589
|
+
"""
|
|
590
|
+
|
|
591
|
+
cls: type
|
|
592
|
+
fields: tuple[FieldNode, ...]
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
class StructType(Type):
|
|
596
|
+
"""A type corresponding to a `structtype.Struct` type.
|
|
597
|
+
|
|
598
|
+
Parameters
|
|
599
|
+
----------
|
|
600
|
+
cls: type
|
|
601
|
+
The corresponding Struct type.
|
|
602
|
+
fields: tuple[FieldNode, ...]
|
|
603
|
+
A tuple of fields in the Struct.
|
|
604
|
+
tag_field: str or None, optional
|
|
605
|
+
If set, the field name used for the tag in a tagged union.
|
|
606
|
+
tag: str, int, or None, optional
|
|
607
|
+
If set, the value used for the tag in a tagged union.
|
|
608
|
+
array_like: bool, optional
|
|
609
|
+
Whether the struct is encoded as an array rather than an object.
|
|
610
|
+
forbid_unknown_fields: bool, optional
|
|
611
|
+
If ``False`` (the default) unknown fields are ignored when decoding. If
|
|
612
|
+
``True`` any unknown fields will result in an error.
|
|
613
|
+
"""
|
|
614
|
+
|
|
615
|
+
cls: type[structtype.Struct]
|
|
616
|
+
fields: tuple[FieldNode, ...]
|
|
617
|
+
tag_field: str | None = None
|
|
618
|
+
tag: str | int | None = None
|
|
619
|
+
array_like: bool = False
|
|
620
|
+
forbid_unknown_fields: bool = False
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
class FieldInfo(structtype.Struct):
|
|
624
|
+
"""A record describing a field in a struct."""
|
|
625
|
+
|
|
626
|
+
name: str
|
|
627
|
+
encode_name: str
|
|
628
|
+
type: Any
|
|
629
|
+
default: Any = UNSET
|
|
630
|
+
default_factory: Any = UNSET
|
|
631
|
+
|
|
632
|
+
def __post_init__(self):
|
|
633
|
+
if self.default is UNSET:
|
|
634
|
+
self.default = NODEFAULT
|
|
635
|
+
if self.default_factory is UNSET:
|
|
636
|
+
self.default_factory = NODEFAULT
|
|
637
|
+
|
|
638
|
+
@property
|
|
639
|
+
def required(self) -> bool:
|
|
640
|
+
return self.default is NODEFAULT and self.default_factory is NODEFAULT
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def fields(type_or_instance: Struct | type[Struct]) -> tuple[FieldInfo, ...]:
|
|
644
|
+
"""Get introspection information about a struct's fields.
|
|
645
|
+
|
|
646
|
+
Returns a tuple of :class:`FieldInfo` objects describing each field in
|
|
647
|
+
declaration order.
|
|
648
|
+
|
|
649
|
+
Parameters
|
|
650
|
+
----------
|
|
651
|
+
type_or_instance : Struct or type[Struct]
|
|
652
|
+
A struct type or struct instance.
|
|
653
|
+
|
|
654
|
+
Returns
|
|
655
|
+
-------
|
|
656
|
+
tuple[FieldInfo, ...]
|
|
657
|
+
A tuple of FieldInfo objects, one per field in declaration order.
|
|
658
|
+
|
|
659
|
+
Examples
|
|
660
|
+
--------
|
|
661
|
+
>>> from structtype import Struct, fields
|
|
662
|
+
|
|
663
|
+
>>> class Point(Struct):
|
|
664
|
+
... x: float
|
|
665
|
+
... y: float
|
|
666
|
+
|
|
667
|
+
>>> fields(Point)
|
|
668
|
+
(FieldInfo(name='x', encode_name='x', type=<class 'float'>, required=True),
|
|
669
|
+
FieldInfo(name='y', encode_name='y', type=<class 'float'>, required=True))
|
|
670
|
+
"""
|
|
671
|
+
obj = type_or_instance
|
|
672
|
+
if isinstance(obj, StructMeta):
|
|
673
|
+
annotated_cls = cls = obj
|
|
674
|
+
elif isinstance(type(obj), StructMeta):
|
|
675
|
+
annotated_cls = cls = type(obj)
|
|
676
|
+
else:
|
|
677
|
+
annotated_cls = obj
|
|
678
|
+
cls = getattr(obj, "__origin__", obj)
|
|
679
|
+
if not isinstance(cls, StructMeta):
|
|
680
|
+
raise TypeError("Must be called with a struct type or instance")
|
|
681
|
+
|
|
682
|
+
hints = _get_class_annotations(annotated_cls)
|
|
683
|
+
npos = len(cls.__struct_fields__) - len(cls.__struct_defaults__)
|
|
684
|
+
fields_list = []
|
|
685
|
+
for name, encode_name, default_obj in zip(
|
|
686
|
+
cls.__struct_fields__,
|
|
687
|
+
cls.__struct_encode_fields__,
|
|
688
|
+
(NODEFAULT,) * npos + cls.__struct_defaults__,
|
|
689
|
+
):
|
|
690
|
+
default = default_factory = NODEFAULT
|
|
691
|
+
if isinstance(default_obj, _Factory):
|
|
692
|
+
default_factory = default_obj.factory
|
|
693
|
+
elif default_obj is not NODEFAULT:
|
|
694
|
+
default = default_obj
|
|
695
|
+
|
|
696
|
+
field_instance = FieldInfo(
|
|
697
|
+
name=name,
|
|
698
|
+
encode_name=encode_name,
|
|
699
|
+
type=hints[name],
|
|
700
|
+
default=default,
|
|
701
|
+
default_factory=default_factory,
|
|
702
|
+
)
|
|
703
|
+
fields_list.append(field_instance)
|
|
704
|
+
|
|
705
|
+
return tuple(fields_list)
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
def multi_type_info(types: Iterable[Any]) -> tuple[Type, ...]:
|
|
709
|
+
"""Get information about multiple structtype-compatible types.
|
|
710
|
+
|
|
711
|
+
Parameters
|
|
712
|
+
----------
|
|
713
|
+
types: an iterable of types
|
|
714
|
+
The types to get info about.
|
|
715
|
+
|
|
716
|
+
Returns
|
|
717
|
+
-------
|
|
718
|
+
tuple[Type, ...]
|
|
719
|
+
|
|
720
|
+
Examples
|
|
721
|
+
--------
|
|
722
|
+
>>> structtype._inspect.multi_type_info([int, float, list[str]]) # doctest: +NORMALIZE_WHITESPACE
|
|
723
|
+
(IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
|
|
724
|
+
FloatType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
|
|
725
|
+
ListType(item_type=StrType(min_length=None, max_length=None, pattern=None),
|
|
726
|
+
min_length=None, max_length=None))
|
|
727
|
+
"""
|
|
728
|
+
return _Translator(types).run()
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
def type_info(type: Any) -> Type:
|
|
732
|
+
"""Get information about a structtype-compatible type.
|
|
733
|
+
|
|
734
|
+
Note that if you need to inspect multiple types it's more efficient to call
|
|
735
|
+
`multi_type_info` once with a sequence of types than calling `type_info`
|
|
736
|
+
multiple times.
|
|
737
|
+
|
|
738
|
+
Parameters
|
|
739
|
+
----------
|
|
740
|
+
type: type
|
|
741
|
+
The type to get info about.
|
|
742
|
+
|
|
743
|
+
Returns
|
|
744
|
+
-------
|
|
745
|
+
Type
|
|
746
|
+
|
|
747
|
+
Examples
|
|
748
|
+
--------
|
|
749
|
+
>>> structtype._inspect.type_info(bool)
|
|
750
|
+
BoolType()
|
|
751
|
+
|
|
752
|
+
>>> structtype._inspect.type_info(int)
|
|
753
|
+
IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None)
|
|
754
|
+
|
|
755
|
+
>>> structtype._inspect.type_info(list[int]) # doctest: +NORMALIZE_WHITESPACE
|
|
756
|
+
ListType(item_type=IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
|
|
757
|
+
min_length=None, max_length=None)
|
|
758
|
+
"""
|
|
759
|
+
return multi_type_info([type])[0]
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
# Implementation details
|
|
763
|
+
def _origin_args_metadata(t):
|
|
764
|
+
# Strip wrappers (Annotated, NewType, Final) until we hit a concrete type
|
|
765
|
+
metadata = []
|
|
766
|
+
while True:
|
|
767
|
+
try:
|
|
768
|
+
origin = _CONCRETE_TYPES.get(t)
|
|
769
|
+
except TypeError:
|
|
770
|
+
# t is not hashable
|
|
771
|
+
origin = None
|
|
772
|
+
|
|
773
|
+
if origin is not None:
|
|
774
|
+
args = None
|
|
775
|
+
break
|
|
776
|
+
|
|
777
|
+
origin = getattr(t, "__origin__", None)
|
|
778
|
+
if origin is not None:
|
|
779
|
+
if type(t) is _AnnotatedAlias:
|
|
780
|
+
metadata.extend(
|
|
781
|
+
m for m in t.__metadata__ if type(m) is structtype.Field
|
|
782
|
+
)
|
|
783
|
+
t = origin
|
|
784
|
+
elif origin == Final:
|
|
785
|
+
t = t.__args__[0]
|
|
786
|
+
elif type(origin) is _TypeAliasType:
|
|
787
|
+
t = origin.__value__[t.__args__]
|
|
788
|
+
else:
|
|
789
|
+
args = getattr(t, "__args__", None)
|
|
790
|
+
origin = _CONCRETE_TYPES.get(origin, origin)
|
|
791
|
+
break
|
|
792
|
+
else:
|
|
793
|
+
supertype = getattr(t, "__supertype__", None)
|
|
794
|
+
if supertype is not None:
|
|
795
|
+
t = supertype
|
|
796
|
+
elif type(t) is _TypeAliasType:
|
|
797
|
+
t = t.__value__
|
|
798
|
+
else:
|
|
799
|
+
origin = t
|
|
800
|
+
args = None
|
|
801
|
+
break
|
|
802
|
+
|
|
803
|
+
if type(origin) is _types_UnionType:
|
|
804
|
+
args = origin.__args__
|
|
805
|
+
origin = Union
|
|
806
|
+
return origin, args, tuple(metadata)
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
def _sort_literal_args(args):
|
|
810
|
+
# `Literal` may mix value types (e.g. `Literal[1, None]`), which a plain
|
|
811
|
+
# `sorted` can't order since Python 3 forbids comparing across types. Sort
|
|
812
|
+
# by type name first so members of the same type stay ordered as before,
|
|
813
|
+
# while mixed-type literals are grouped deterministically instead of
|
|
814
|
+
# crashing.
|
|
815
|
+
try:
|
|
816
|
+
return tuple(sorted(args, key=lambda x: (type(x).__name__, x)))
|
|
817
|
+
except TypeError:
|
|
818
|
+
return tuple(args)
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
def _is_enum(t):
|
|
822
|
+
return type(t) is enum.EnumMeta
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
def _is_dataclass(t):
|
|
826
|
+
return hasattr(t, "__dataclass_fields__")
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
def _is_attrs(t):
|
|
830
|
+
return hasattr(t, "__attrs_attrs__")
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
def _is_pydantic(t):
|
|
834
|
+
return hasattr(t, "model_fields") and hasattr(t, "model_validate")
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def _is_typeddict(t):
|
|
838
|
+
try:
|
|
839
|
+
return issubclass(t, dict) and hasattr(t, "__total__")
|
|
840
|
+
except TypeError:
|
|
841
|
+
return False
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
def _is_namedtuple(t):
|
|
845
|
+
try:
|
|
846
|
+
return issubclass(t, tuple) and hasattr(t, "_fields")
|
|
847
|
+
except TypeError:
|
|
848
|
+
return False
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
def _merge_json(a, b):
|
|
852
|
+
if b:
|
|
853
|
+
a = a.copy()
|
|
854
|
+
for key, b_val in b.items():
|
|
855
|
+
if key in a:
|
|
856
|
+
a_val = a[key]
|
|
857
|
+
if isinstance(a_val, dict) and isinstance(b_val, dict):
|
|
858
|
+
a[key] = _merge_json(a_val, b_val)
|
|
859
|
+
elif isinstance(a_val, (list, tuple)) and isinstance(
|
|
860
|
+
b_val, (list, tuple)
|
|
861
|
+
):
|
|
862
|
+
a[key] = list(a_val) + list(b_val)
|
|
863
|
+
else:
|
|
864
|
+
a[key] = b_val
|
|
865
|
+
else:
|
|
866
|
+
a[key] = b_val
|
|
867
|
+
return a
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
class _Translator:
|
|
871
|
+
def __init__(self, types):
|
|
872
|
+
self.types = tuple(types)
|
|
873
|
+
self.type_hints = {}
|
|
874
|
+
self.cache = {}
|
|
875
|
+
|
|
876
|
+
def _get_class_annotations(self, t):
|
|
877
|
+
"""A cached version of `get_class_annotations`"""
|
|
878
|
+
try:
|
|
879
|
+
return self.type_hints[t]
|
|
880
|
+
except KeyError:
|
|
881
|
+
out = self.type_hints[t] = _get_class_annotations(t)
|
|
882
|
+
return out
|
|
883
|
+
|
|
884
|
+
def run(self):
|
|
885
|
+
# First construct a decoder to validate the types are valid
|
|
886
|
+
from ._core import JSONDecoder
|
|
887
|
+
|
|
888
|
+
JSONDecoder(tuple[self.types])
|
|
889
|
+
return tuple(self.translate(t) for t in self.types)
|
|
890
|
+
|
|
891
|
+
def translate(self, typ):
|
|
892
|
+
t, args, metadata = _origin_args_metadata(typ)
|
|
893
|
+
|
|
894
|
+
# Extract and merge components of any `Field` annotations
|
|
895
|
+
constrs = {}
|
|
896
|
+
extra_json_schema = {}
|
|
897
|
+
for meta in metadata:
|
|
898
|
+
for attr in (
|
|
899
|
+
"ge",
|
|
900
|
+
"gt",
|
|
901
|
+
"le",
|
|
902
|
+
"lt",
|
|
903
|
+
"multiple_of",
|
|
904
|
+
"pattern",
|
|
905
|
+
"min_length",
|
|
906
|
+
"max_length",
|
|
907
|
+
"tz",
|
|
908
|
+
):
|
|
909
|
+
if (val := getattr(meta, attr)) is not None:
|
|
910
|
+
constrs[attr] = val
|
|
911
|
+
for attr in ("title", "description", "examples"):
|
|
912
|
+
if (val := getattr(meta, attr)) is not None:
|
|
913
|
+
extra_json_schema[attr] = val
|
|
914
|
+
if meta.json_schema_extra is not None:
|
|
915
|
+
extra_json_schema = _merge_json(
|
|
916
|
+
extra_json_schema,
|
|
917
|
+
_dump(meta.json_schema_extra, str_keys=True),
|
|
918
|
+
)
|
|
919
|
+
|
|
920
|
+
out = self._translate_inner(t, args, **constrs)
|
|
921
|
+
if extra_json_schema:
|
|
922
|
+
# If extra metadata is present, wrap the output type in a Metadata
|
|
923
|
+
# wrapper object
|
|
924
|
+
return Metadata(out, json_schema_extra=extra_json_schema or None)
|
|
925
|
+
return out
|
|
926
|
+
|
|
927
|
+
def _translate_inner(
|
|
928
|
+
self,
|
|
929
|
+
t,
|
|
930
|
+
args,
|
|
931
|
+
ge=None,
|
|
932
|
+
gt=None,
|
|
933
|
+
le=None,
|
|
934
|
+
lt=None,
|
|
935
|
+
multiple_of=None,
|
|
936
|
+
pattern=None,
|
|
937
|
+
min_length=None,
|
|
938
|
+
max_length=None,
|
|
939
|
+
tz=None,
|
|
940
|
+
):
|
|
941
|
+
if t is Any:
|
|
942
|
+
return AnyType()
|
|
943
|
+
elif isinstance(t, TypeVar):
|
|
944
|
+
if t.__bound__ is not None:
|
|
945
|
+
return self.translate(t.__bound__)
|
|
946
|
+
return AnyType()
|
|
947
|
+
elif t is None or t is type(None):
|
|
948
|
+
return NoneType()
|
|
949
|
+
elif t is bool:
|
|
950
|
+
return BoolType()
|
|
951
|
+
elif t is int:
|
|
952
|
+
return IntType(ge=ge, gt=gt, le=le, lt=lt, multiple_of=multiple_of)
|
|
953
|
+
elif t is float:
|
|
954
|
+
return FloatType(ge=ge, gt=gt, le=le, lt=lt, multiple_of=multiple_of)
|
|
955
|
+
elif t is str:
|
|
956
|
+
return StrType(
|
|
957
|
+
min_length=min_length, max_length=max_length, pattern=pattern
|
|
958
|
+
)
|
|
959
|
+
elif t is bytes:
|
|
960
|
+
return BytesType(min_length=min_length, max_length=max_length)
|
|
961
|
+
elif t is bytearray:
|
|
962
|
+
return ByteArrayType(min_length=min_length, max_length=max_length)
|
|
963
|
+
elif t is memoryview:
|
|
964
|
+
return MemoryViewType(min_length=min_length, max_length=max_length)
|
|
965
|
+
elif t is datetime.datetime:
|
|
966
|
+
return DateTimeType(tz=tz)
|
|
967
|
+
elif t is datetime.time:
|
|
968
|
+
return TimeType(tz=tz)
|
|
969
|
+
elif t is datetime.date:
|
|
970
|
+
return DateType()
|
|
971
|
+
elif t is datetime.timedelta:
|
|
972
|
+
return TimeDeltaType()
|
|
973
|
+
elif t is uuid.UUID:
|
|
974
|
+
return UUIDType()
|
|
975
|
+
elif t is decimal.Decimal:
|
|
976
|
+
return DecimalType()
|
|
977
|
+
elif t is structtype.Raw:
|
|
978
|
+
return RawType()
|
|
979
|
+
elif False: # Ext type removed
|
|
980
|
+
return ExtType()
|
|
981
|
+
elif t is list:
|
|
982
|
+
return ListType(
|
|
983
|
+
self.translate(args[0]) if args else AnyType(),
|
|
984
|
+
min_length=min_length,
|
|
985
|
+
max_length=max_length,
|
|
986
|
+
)
|
|
987
|
+
elif t is set:
|
|
988
|
+
return SetType(
|
|
989
|
+
self.translate(args[0]) if args else AnyType(),
|
|
990
|
+
min_length=min_length,
|
|
991
|
+
max_length=max_length,
|
|
992
|
+
)
|
|
993
|
+
elif t is frozenset:
|
|
994
|
+
return FrozenSetType(
|
|
995
|
+
self.translate(args[0]) if args else AnyType(),
|
|
996
|
+
min_length=min_length,
|
|
997
|
+
max_length=max_length,
|
|
998
|
+
)
|
|
999
|
+
elif t is tuple:
|
|
1000
|
+
# Handle an annoying compatibility issue:
|
|
1001
|
+
# - Tuple[()] has args == ((),)
|
|
1002
|
+
# - tuple[()] has args == ()
|
|
1003
|
+
if args == ((),):
|
|
1004
|
+
args = ()
|
|
1005
|
+
if args is None:
|
|
1006
|
+
return VarTupleType(
|
|
1007
|
+
AnyType(), min_length=min_length, max_length=max_length
|
|
1008
|
+
)
|
|
1009
|
+
elif len(args) == 2 and args[-1] is ...:
|
|
1010
|
+
return VarTupleType(
|
|
1011
|
+
self.translate(args[0]),
|
|
1012
|
+
min_length=min_length,
|
|
1013
|
+
max_length=max_length,
|
|
1014
|
+
)
|
|
1015
|
+
else:
|
|
1016
|
+
return TupleType(tuple(self.translate(a) for a in args))
|
|
1017
|
+
elif t is dict:
|
|
1018
|
+
return DictType(
|
|
1019
|
+
self.translate(args[0]) if args else AnyType(),
|
|
1020
|
+
self.translate(args[1]) if args else AnyType(),
|
|
1021
|
+
min_length=min_length,
|
|
1022
|
+
max_length=max_length,
|
|
1023
|
+
)
|
|
1024
|
+
elif _PY315_PLUS and t is frozendict: # noqa: F821
|
|
1025
|
+
return FrozenDictType(
|
|
1026
|
+
self.translate(args[0]) if args else AnyType(),
|
|
1027
|
+
self.translate(args[1]) if args else AnyType(),
|
|
1028
|
+
min_length=min_length,
|
|
1029
|
+
max_length=max_length,
|
|
1030
|
+
)
|
|
1031
|
+
elif t is Union:
|
|
1032
|
+
args = tuple(self.translate(a) for a in args if a is not _UnsetType)
|
|
1033
|
+
return args[0] if len(args) == 1 else UnionType(args)
|
|
1034
|
+
elif t is Literal:
|
|
1035
|
+
return LiteralType(_sort_literal_args(args))
|
|
1036
|
+
elif _is_enum(t):
|
|
1037
|
+
return EnumType(t)
|
|
1038
|
+
elif isinstance(t, StructMeta):
|
|
1039
|
+
cls = t[args] if args else t
|
|
1040
|
+
if cls in self.cache:
|
|
1041
|
+
return self.cache[cls]
|
|
1042
|
+
config = t.__struct_config__
|
|
1043
|
+
self.cache[cls] = out = StructType(
|
|
1044
|
+
cls,
|
|
1045
|
+
(),
|
|
1046
|
+
tag_field=config.tag_field,
|
|
1047
|
+
tag=config.tag,
|
|
1048
|
+
array_like=config.array_like,
|
|
1049
|
+
forbid_unknown_fields=config.forbid_unknown_fields,
|
|
1050
|
+
)
|
|
1051
|
+
|
|
1052
|
+
hints = self._get_class_annotations(cls)
|
|
1053
|
+
npos = len(t.__struct_fields__) - len(t.__struct_defaults__)
|
|
1054
|
+
fields = []
|
|
1055
|
+
for name, encode_name, default_obj in zip(
|
|
1056
|
+
t.__struct_fields__,
|
|
1057
|
+
t.__struct_encode_fields__,
|
|
1058
|
+
(NODEFAULT,) * npos + t.__struct_defaults__,
|
|
1059
|
+
):
|
|
1060
|
+
if default_obj is NODEFAULT:
|
|
1061
|
+
required = True
|
|
1062
|
+
default = default_factory = NODEFAULT
|
|
1063
|
+
elif isinstance(default_obj, _Factory):
|
|
1064
|
+
required = False
|
|
1065
|
+
default = NODEFAULT
|
|
1066
|
+
default_factory = default_obj.factory
|
|
1067
|
+
else:
|
|
1068
|
+
required = False
|
|
1069
|
+
default = NODEFAULT if default_obj is UNSET else default_obj
|
|
1070
|
+
default_factory = NODEFAULT
|
|
1071
|
+
|
|
1072
|
+
field = FieldNode(
|
|
1073
|
+
name=name,
|
|
1074
|
+
encode_name=encode_name,
|
|
1075
|
+
type=self.translate(hints[name]),
|
|
1076
|
+
required=required,
|
|
1077
|
+
default=default,
|
|
1078
|
+
default_factory=default_factory,
|
|
1079
|
+
)
|
|
1080
|
+
fields.append(field)
|
|
1081
|
+
|
|
1082
|
+
out.fields = tuple(fields)
|
|
1083
|
+
return out
|
|
1084
|
+
elif _is_typeddict(t):
|
|
1085
|
+
cls = t[args] if args else t
|
|
1086
|
+
if cls in self.cache:
|
|
1087
|
+
return self.cache[cls]
|
|
1088
|
+
self.cache[cls] = out = TypedDictType(cls, ())
|
|
1089
|
+
hints, required = _get_typeddict_info(cls)
|
|
1090
|
+
out.fields = tuple(
|
|
1091
|
+
FieldNode(
|
|
1092
|
+
name=name,
|
|
1093
|
+
encode_name=name,
|
|
1094
|
+
type=self.translate(field_type),
|
|
1095
|
+
required=name in required,
|
|
1096
|
+
)
|
|
1097
|
+
for name, field_type in sorted(hints.items())
|
|
1098
|
+
)
|
|
1099
|
+
return out
|
|
1100
|
+
elif _is_dataclass(t) or _is_attrs(t):
|
|
1101
|
+
cls = t[args] if args else t
|
|
1102
|
+
if cls in self.cache:
|
|
1103
|
+
return self.cache[cls]
|
|
1104
|
+
self.cache[cls] = out = DataclassType(cls, ())
|
|
1105
|
+
_, info, defaults, _, _ = _get_dataclass_info(cls)
|
|
1106
|
+
defaults = ((NODEFAULT,) * (len(info) - len(defaults))) + defaults
|
|
1107
|
+
fields = []
|
|
1108
|
+
for (name, typ, is_factory), default_obj in zip(info, defaults):
|
|
1109
|
+
if default_obj is NODEFAULT:
|
|
1110
|
+
required = True
|
|
1111
|
+
default = default_factory = NODEFAULT
|
|
1112
|
+
elif is_factory:
|
|
1113
|
+
required = False
|
|
1114
|
+
default = NODEFAULT
|
|
1115
|
+
default_factory = default_obj
|
|
1116
|
+
else:
|
|
1117
|
+
required = False
|
|
1118
|
+
default = NODEFAULT if default_obj is UNSET else default_obj
|
|
1119
|
+
default_factory = NODEFAULT
|
|
1120
|
+
|
|
1121
|
+
fields.append(
|
|
1122
|
+
FieldNode(
|
|
1123
|
+
name=name,
|
|
1124
|
+
encode_name=name,
|
|
1125
|
+
type=self.translate(typ),
|
|
1126
|
+
required=required,
|
|
1127
|
+
default=default,
|
|
1128
|
+
default_factory=default_factory,
|
|
1129
|
+
)
|
|
1130
|
+
)
|
|
1131
|
+
out.fields = tuple(fields)
|
|
1132
|
+
return out
|
|
1133
|
+
elif _is_namedtuple(t):
|
|
1134
|
+
cls = t[args] if args else t
|
|
1135
|
+
if cls in self.cache:
|
|
1136
|
+
return self.cache[cls]
|
|
1137
|
+
self.cache[cls] = out = NamedTupleType(cls, ())
|
|
1138
|
+
hints = self._get_class_annotations(cls)
|
|
1139
|
+
out.fields = tuple(
|
|
1140
|
+
FieldNode(
|
|
1141
|
+
name=name,
|
|
1142
|
+
encode_name=name,
|
|
1143
|
+
type=self.translate(hints.get(name, Any)),
|
|
1144
|
+
required=name not in t._field_defaults,
|
|
1145
|
+
default=t._field_defaults.get(name, NODEFAULT),
|
|
1146
|
+
)
|
|
1147
|
+
for name in t._fields
|
|
1148
|
+
)
|
|
1149
|
+
return out
|
|
1150
|
+
elif _is_pydantic(t):
|
|
1151
|
+
cls = t[args] if args else t
|
|
1152
|
+
if cls in self.cache:
|
|
1153
|
+
return self.cache[cls]
|
|
1154
|
+
self.cache[cls] = out = PydanticType(cls, ())
|
|
1155
|
+
_, info, defaults = _get_pydantic_info(cls)
|
|
1156
|
+
defaults = ((NODEFAULT,) * (len(info) - len(defaults))) + defaults
|
|
1157
|
+
fields = []
|
|
1158
|
+
for (name, typ, is_factory), default_obj in zip(info, defaults):
|
|
1159
|
+
is_required = default_obj is NODEFAULT
|
|
1160
|
+
if is_required:
|
|
1161
|
+
default = default_factory = NODEFAULT
|
|
1162
|
+
elif is_factory:
|
|
1163
|
+
default = NODEFAULT
|
|
1164
|
+
default_factory = default_obj
|
|
1165
|
+
else:
|
|
1166
|
+
default = default_obj
|
|
1167
|
+
default_factory = NODEFAULT
|
|
1168
|
+
field_info = cls.model_fields[name]
|
|
1169
|
+
encode_name = field_info.alias or field_info.serialization_alias or name
|
|
1170
|
+
fields.append(
|
|
1171
|
+
FieldNode(
|
|
1172
|
+
name=name,
|
|
1173
|
+
encode_name=encode_name,
|
|
1174
|
+
type=self.translate(typ),
|
|
1175
|
+
required=is_required,
|
|
1176
|
+
default=default,
|
|
1177
|
+
default_factory=default_factory,
|
|
1178
|
+
)
|
|
1179
|
+
)
|
|
1180
|
+
out.fields = tuple(fields)
|
|
1181
|
+
return out
|
|
1182
|
+
else:
|
|
1183
|
+
return CustomType(t)
|