1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
from __future__ import annotations
from typing import Any
from . import NODEFAULT, Struct, field
from ._core import ( # noqa
Factory as _Factory,
StructConfig,
asdict,
astuple,
replace,
force_setattr,
)
from ._utils import get_class_annotations as _get_class_annotations
__all__ = (
"FieldInfo",
"StructConfig",
"asdict",
"astuple",
"fields",
"force_setattr",
"replace",
)
def __dir__():
return __all__
class FieldInfo(Struct):
"""A record describing a field in a struct type.
Parameters
----------
name: str
The field name as seen by Python code (e.g. ``field_one``).
encode_name: str
The name used when encoding/decoding the field. This may differ if
the field is renamed (e.g. ``fieldOne``).
type: Any
The full field type annotation.
default: Any, optional
A default value for the field. Will be `NODEFAULT` if no default value
is set.
default_factory: Any, optional
A callable that creates a default value for the field. Will be
`NODEFAULT` if no ``default_factory`` is set.
"""
name: str
encode_name: str
type: Any
default: Any = field(default_factory=lambda: NODEFAULT)
default_factory: Any = field(default_factory=lambda: NODEFAULT)
@property
def required(self) -> bool:
"""A helper for checking whether a field is required"""
return self.default is NODEFAULT and self.default_factory is NODEFAULT
def fields(type_or_instance: Struct | type[Struct]) -> tuple[FieldInfo]:
"""Get information about the fields in a Struct.
Parameters
----------
type_or_instance:
A struct type or instance.
Returns
-------
tuple[FieldInfo]
"""
if isinstance(type_or_instance, Struct):
annotated_cls = cls = type(type_or_instance)
else:
annotated_cls = type_or_instance
cls = getattr(type_or_instance, "__origin__", type_or_instance)
if not (isinstance(cls, type) and issubclass(cls, Struct)):
raise TypeError("Must be called with a struct type or instance")
hints = _get_class_annotations(annotated_cls)
npos = len(cls.__struct_fields__) - len(cls.__struct_defaults__)
fields = []
for name, encode_name, default_obj in zip(
cls.__struct_fields__,
cls.__struct_encode_fields__,
(NODEFAULT,) * npos + cls.__struct_defaults__,
):
default = default_factory = NODEFAULT
if isinstance(default_obj, _Factory):
default_factory = default_obj.factory
elif default_obj is not NODEFAULT:
default = default_obj
field = FieldInfo(
name=name,
encode_name=encode_name,
type=hints[name],
default=default,
default_factory=default_factory,
)
fields.append(field)
return tuple(fields)
|