You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

107 lines
2.9 KiB

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)