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)
|