summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/msgspec/structs.py
blob: 76f2fdfe9e80b2a99f8d5d2c3d01762eb88dd876 (plain)
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)