lodum.field

  1# SPDX-FileCopyrightText: 2025-present Michael R. Bernstein <zopemaven@gmail.com>
  2#
  3# SPDX-License-Identifier: Apache-2.0
  4import threading
  5from typing import Any, Callable, Dict, List, Optional, Type, Union
  6
  7
  8# Global registry for lodum-enabled classes to support ForwardRef resolution
  9_NAME_TO_TYPE_CACHE: Dict[str, Type[Any]] = {}
 10_REGISTRY_LOCK = threading.RLock()
 11
 12
 13def register_type(cls: Type[Any]) -> None:
 14    """Registers a class in the global name-to-type cache."""
 15    with _REGISTRY_LOCK:
 16        _NAME_TO_TYPE_CACHE[cls.__name__] = cls
 17
 18
 19# A sentinel object to detect if a parameter is supplied or not.
 20class _MISSING_TYPE:
 21    pass
 22
 23
 24_MISSING = _MISSING_TYPE()
 25
 26
 27class Field:
 28    """
 29    A class that stores metadata for a field in a lodum-enabled class.
 30
 31    This is not intended to be instantiated directly. Instead, use the `field()`
 32    function, which provides a more convenient API.
 33    """
 34
 35    def __init__(
 36        self,
 37        rename: Optional[str] = None,
 38        skip_serializing: bool = False,
 39        default: Any = _MISSING,
 40        default_factory: Optional[Callable[[], Any]] = None,
 41        serializer: Optional[Callable[[Any], Any]] = None,
 42        deserializer: Optional[Callable[[Any], Any]] = None,
 43        validate: Optional[
 44            Union[Callable[[Any], None], List[Callable[[Any], None]]]
 45        ] = None,
 46    ) -> None:
 47        if default is not _MISSING and default_factory is not None:
 48            raise ValueError("cannot specify both default and default_factory")
 49
 50        self.rename = rename
 51        self.skip_serializing = skip_serializing
 52        self.default = default
 53        self.default_factory = default_factory
 54        self.serializer = serializer
 55        self.deserializer = deserializer
 56        self.validate = validate
 57        self.name: str = ""  # Will be populated by the decorator
 58        self.type: Any = None  # Will be populated by the decorator
 59
 60    @property
 61    def has_default(self) -> bool:
 62        return self.default is not _MISSING or self.default_factory is not None
 63
 64    def get_default(self) -> Any:
 65        if self.default_factory is not None:
 66            return self.default_factory()
 67        return self.default
 68
 69    def __repr__(self) -> str:
 70        parts = []
 71        if self.name:
 72            parts.append(f"name={self.name!r}")
 73        if self.type:
 74            parts.append(f"type={self.type!r}")
 75        if self.rename:
 76            parts.append(f"rename={self.rename!r}")
 77        if self.skip_serializing:
 78            parts.append(f"skip_serializing={self.skip_serializing!r}")
 79        if self.default is not _MISSING:
 80            parts.append(f"default={self.default!r}")
 81        if self.default_factory:
 82            parts.append(f"default_factory={self.default_factory!r}")
 83        if self.serializer:
 84            parts.append(f"serializer={self.serializer!r}")
 85        if self.deserializer:
 86            parts.append(f"deserializer={self.deserializer!r}")
 87        if self.validate:
 88            parts.append(f"validate={self.validate!r}")
 89        return f"Field({', '.join(parts)})"
 90
 91    def __eq__(self, other: Any) -> bool:
 92        if not isinstance(other, Field):
 93            return NotImplemented
 94        return (
 95            self.name == other.name
 96            and self.type == other.type
 97            and self.rename == other.rename
 98            and self.skip_serializing == other.skip_serializing
 99            and self.default == other.default
100            and self.default_factory == other.default_factory
101            and self.serializer == other.serializer
102            and self.deserializer == other.deserializer
103            and self.validate == other.validate
104        )
105
106
107def field(
108    *,
109    rename: Optional[str] = None,
110    skip_serializing: bool = False,
111    default: Any = _MISSING,
112    default_factory: Optional[Callable[[], Any]] = None,
113    serializer: Optional[Callable[[Any], Any]] = None,
114    deserializer: Optional[Callable[[Any], Any]] = None,
115    validate: Optional[
116        Union[Callable[[Any], None], List[Callable[[Any], None]]]
117    ] = None,
118) -> Any:
119    """
120    Provides metadata to the `@lodum` decorator for a single field.
121
122    Args:
123        rename: The name to use for the field in the output.
124        skip_serializing: If `True`, the field will not be included in the
125            output.
126        default: A default value to use for the field during decoding
127            if it is missing from the input data.
128        default_factory: A zero-argument function that will be called to
129            create a default value for a missing field.
130        serializer: A function to call to encode the field's value.
131        deserializer: A function to call to decode the field's value.
132        validate: A callable or list of callables to validate the field's value during decoding.
133    """
134    return Field(
135        rename=rename,
136        skip_serializing=skip_serializing,
137        default=default,
138        default_factory=default_factory,
139        serializer=serializer,
140        deserializer=deserializer,
141        validate=validate,
142    )
def register_type(cls: Type[Any]) -> None:
14def register_type(cls: Type[Any]) -> None:
15    """Registers a class in the global name-to-type cache."""
16    with _REGISTRY_LOCK:
17        _NAME_TO_TYPE_CACHE[cls.__name__] = cls

Registers a class in the global name-to-type cache.

class Field:
 28class Field:
 29    """
 30    A class that stores metadata for a field in a lodum-enabled class.
 31
 32    This is not intended to be instantiated directly. Instead, use the `field()`
 33    function, which provides a more convenient API.
 34    """
 35
 36    def __init__(
 37        self,
 38        rename: Optional[str] = None,
 39        skip_serializing: bool = False,
 40        default: Any = _MISSING,
 41        default_factory: Optional[Callable[[], Any]] = None,
 42        serializer: Optional[Callable[[Any], Any]] = None,
 43        deserializer: Optional[Callable[[Any], Any]] = None,
 44        validate: Optional[
 45            Union[Callable[[Any], None], List[Callable[[Any], None]]]
 46        ] = None,
 47    ) -> None:
 48        if default is not _MISSING and default_factory is not None:
 49            raise ValueError("cannot specify both default and default_factory")
 50
 51        self.rename = rename
 52        self.skip_serializing = skip_serializing
 53        self.default = default
 54        self.default_factory = default_factory
 55        self.serializer = serializer
 56        self.deserializer = deserializer
 57        self.validate = validate
 58        self.name: str = ""  # Will be populated by the decorator
 59        self.type: Any = None  # Will be populated by the decorator
 60
 61    @property
 62    def has_default(self) -> bool:
 63        return self.default is not _MISSING or self.default_factory is not None
 64
 65    def get_default(self) -> Any:
 66        if self.default_factory is not None:
 67            return self.default_factory()
 68        return self.default
 69
 70    def __repr__(self) -> str:
 71        parts = []
 72        if self.name:
 73            parts.append(f"name={self.name!r}")
 74        if self.type:
 75            parts.append(f"type={self.type!r}")
 76        if self.rename:
 77            parts.append(f"rename={self.rename!r}")
 78        if self.skip_serializing:
 79            parts.append(f"skip_serializing={self.skip_serializing!r}")
 80        if self.default is not _MISSING:
 81            parts.append(f"default={self.default!r}")
 82        if self.default_factory:
 83            parts.append(f"default_factory={self.default_factory!r}")
 84        if self.serializer:
 85            parts.append(f"serializer={self.serializer!r}")
 86        if self.deserializer:
 87            parts.append(f"deserializer={self.deserializer!r}")
 88        if self.validate:
 89            parts.append(f"validate={self.validate!r}")
 90        return f"Field({', '.join(parts)})"
 91
 92    def __eq__(self, other: Any) -> bool:
 93        if not isinstance(other, Field):
 94            return NotImplemented
 95        return (
 96            self.name == other.name
 97            and self.type == other.type
 98            and self.rename == other.rename
 99            and self.skip_serializing == other.skip_serializing
100            and self.default == other.default
101            and self.default_factory == other.default_factory
102            and self.serializer == other.serializer
103            and self.deserializer == other.deserializer
104            and self.validate == other.validate
105        )

A class that stores metadata for a field in a lodum-enabled class.

This is not intended to be instantiated directly. Instead, use the field() function, which provides a more convenient API.

Field( rename: Optional[str] = None, skip_serializing: bool = False, default: Any = <lodum.field._MISSING_TYPE object>, default_factory: Optional[Callable[[], Any]] = None, serializer: Optional[Callable[[Any], Any]] = None, deserializer: Optional[Callable[[Any], Any]] = None, validate: Union[Callable[[Any], NoneType], List[Callable[[Any], NoneType]], NoneType] = None)
36    def __init__(
37        self,
38        rename: Optional[str] = None,
39        skip_serializing: bool = False,
40        default: Any = _MISSING,
41        default_factory: Optional[Callable[[], Any]] = None,
42        serializer: Optional[Callable[[Any], Any]] = None,
43        deserializer: Optional[Callable[[Any], Any]] = None,
44        validate: Optional[
45            Union[Callable[[Any], None], List[Callable[[Any], None]]]
46        ] = None,
47    ) -> None:
48        if default is not _MISSING and default_factory is not None:
49            raise ValueError("cannot specify both default and default_factory")
50
51        self.rename = rename
52        self.skip_serializing = skip_serializing
53        self.default = default
54        self.default_factory = default_factory
55        self.serializer = serializer
56        self.deserializer = deserializer
57        self.validate = validate
58        self.name: str = ""  # Will be populated by the decorator
59        self.type: Any = None  # Will be populated by the decorator
rename
skip_serializing
default
default_factory
serializer
deserializer
validate
name: str
type: Any
has_default: bool
61    @property
62    def has_default(self) -> bool:
63        return self.default is not _MISSING or self.default_factory is not None
def get_default(self) -> Any:
65    def get_default(self) -> Any:
66        if self.default_factory is not None:
67            return self.default_factory()
68        return self.default
def field( *, rename: Optional[str] = None, skip_serializing: bool = False, default: Any = <lodum.field._MISSING_TYPE object>, default_factory: Optional[Callable[[], Any]] = None, serializer: Optional[Callable[[Any], Any]] = None, deserializer: Optional[Callable[[Any], Any]] = None, validate: Union[Callable[[Any], NoneType], List[Callable[[Any], NoneType]], NoneType] = None) -> Any:
108def field(
109    *,
110    rename: Optional[str] = None,
111    skip_serializing: bool = False,
112    default: Any = _MISSING,
113    default_factory: Optional[Callable[[], Any]] = None,
114    serializer: Optional[Callable[[Any], Any]] = None,
115    deserializer: Optional[Callable[[Any], Any]] = None,
116    validate: Optional[
117        Union[Callable[[Any], None], List[Callable[[Any], None]]]
118    ] = None,
119) -> Any:
120    """
121    Provides metadata to the `@lodum` decorator for a single field.
122
123    Args:
124        rename: The name to use for the field in the output.
125        skip_serializing: If `True`, the field will not be included in the
126            output.
127        default: A default value to use for the field during decoding
128            if it is missing from the input data.
129        default_factory: A zero-argument function that will be called to
130            create a default value for a missing field.
131        serializer: A function to call to encode the field's value.
132        deserializer: A function to call to decode the field's value.
133        validate: A callable or list of callables to validate the field's value during decoding.
134    """
135    return Field(
136        rename=rename,
137        skip_serializing=skip_serializing,
138        default=default,
139        default_factory=default_factory,
140        serializer=serializer,
141        deserializer=deserializer,
142        validate=validate,
143    )

Provides metadata to the @lodum decorator for a single field.

Args: rename: The name to use for the field in the output. skip_serializing: If True, the field will not be included in the output. default: A default value to use for the field during decoding if it is missing from the input data. default_factory: A zero-argument function that will be called to create a default value for a missing field. serializer: A function to call to encode the field's value. deserializer: A function to call to decode the field's value. validate: A callable or list of callables to validate the field's value during decoding.