| | from __future__ import annotations |
| |
|
| | from typing import Any |
| |
|
| | from . import NODEFAULT, Struct, field |
| | from ._core import ( |
| | 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) |
| |
|