docs for muutils v0.6.13
View Source on GitHub

muutils.json_serialize.serializable_field

extends dataclasses.Field for use with SerializableDataclass

In particular, instead of using dataclasses.field, use serializable_field to define fields in a SerializableDataclass. You provide information on how the field should be serialized and loaded (as well as anything that goes into dataclasses.field) when you define the field, and the SerializableDataclass will automatically use those functions.


  1"""extends `dataclasses.Field` for use with `SerializableDataclass`
  2
  3In particular, instead of using `dataclasses.field`, use `serializable_field` to define fields in a `SerializableDataclass`.
  4You provide information on how the field should be serialized and loaded (as well as anything that goes into `dataclasses.field`)
  5when you define the field, and the `SerializableDataclass` will automatically use those functions.
  6
  7"""
  8
  9from __future__ import annotations
 10
 11import dataclasses
 12import sys
 13import types
 14from typing import Any, Callable, Optional, Union, overload, TypeVar
 15
 16
 17# pylint: disable=bad-mcs-classmethod-argument, too-many-arguments, protected-access
 18
 19
 20class SerializableField(dataclasses.Field):
 21    """extension of `dataclasses.Field` with additional serialization properties"""
 22
 23    __slots__ = (
 24        # from dataclasses.Field.__slots__
 25        "name",
 26        "type",
 27        "default",
 28        "default_factory",
 29        "repr",
 30        "hash",
 31        "init",
 32        "compare",
 33        "metadata",
 34        "kw_only",
 35        "_field_type",  # Private: not to be used by user code.
 36        # new ones
 37        "serialize",
 38        "serialization_fn",
 39        "loading_fn",
 40        "deserialize_fn",  # new alternative to loading_fn
 41        "assert_type",
 42        "custom_typecheck_fn",
 43    )
 44
 45    def __init__(
 46        self,
 47        default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
 48        default_factory: Union[
 49            Callable[[], Any], dataclasses._MISSING_TYPE
 50        ] = dataclasses.MISSING,
 51        init: bool = True,
 52        repr: bool = True,
 53        hash: Optional[bool] = None,
 54        compare: bool = True,
 55        # TODO: add field for custom comparator (such as serializing)
 56        metadata: Optional[types.MappingProxyType] = None,
 57        kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
 58        serialize: bool = True,
 59        serialization_fn: Optional[Callable[[Any], Any]] = None,
 60        loading_fn: Optional[Callable[[Any], Any]] = None,
 61        deserialize_fn: Optional[Callable[[Any], Any]] = None,
 62        assert_type: bool = True,
 63        custom_typecheck_fn: Optional[Callable[[type], bool]] = None,
 64    ):
 65        # TODO: should we do this check, or assume the user knows what they are doing?
 66        if init and not serialize:
 67            raise ValueError("Cannot have init=True and serialize=False")
 68
 69        # need to assemble kwargs in this hacky way so as not to upset type checking
 70        super_kwargs: dict[str, Any] = dict(
 71            default=default,
 72            default_factory=default_factory,
 73            init=init,
 74            repr=repr,
 75            hash=hash,
 76            compare=compare,
 77            kw_only=kw_only,
 78        )
 79
 80        if metadata is not None:
 81            super_kwargs["metadata"] = metadata
 82        else:
 83            super_kwargs["metadata"] = types.MappingProxyType({})
 84
 85        # special check, kw_only is not supported in python <3.9 and `dataclasses.MISSING` is truthy
 86        if sys.version_info < (3, 10):
 87            if super_kwargs["kw_only"] == True:  # noqa: E712
 88                raise ValueError("kw_only is not supported in python >=3.9")
 89            else:
 90                del super_kwargs["kw_only"]
 91
 92        # actually init the super class
 93        super().__init__(**super_kwargs)  # type: ignore[call-arg]
 94
 95        # now init the new fields
 96        self.serialize: bool = serialize
 97        self.serialization_fn: Optional[Callable[[Any], Any]] = serialization_fn
 98
 99        if loading_fn is not None and deserialize_fn is not None:
100            raise ValueError(
101                "Cannot pass both loading_fn and deserialize_fn, pass only one. ",
102                "`loading_fn` is the older interface and takes the dict of the class, ",
103                "`deserialize_fn` is the new interface and takes only the field's value.",
104            )
105        self.loading_fn: Optional[Callable[[Any], Any]] = loading_fn
106        self.deserialize_fn: Optional[Callable[[Any], Any]] = deserialize_fn
107
108        self.assert_type: bool = assert_type
109        self.custom_typecheck_fn: Optional[Callable[[type], bool]] = custom_typecheck_fn
110
111    @classmethod
112    def from_Field(cls, field: dataclasses.Field) -> "SerializableField":
113        """copy all values from a `dataclasses.Field` to new `SerializableField`"""
114        return cls(
115            default=field.default,
116            default_factory=field.default_factory,
117            init=field.init,
118            repr=field.repr,
119            hash=field.hash,
120            compare=field.compare,
121            metadata=field.metadata,
122            kw_only=getattr(field, "kw_only", dataclasses.MISSING),  # for python <3.9
123            serialize=field.repr,  # serialize if it's going to be repr'd
124            serialization_fn=None,
125            loading_fn=None,
126            deserialize_fn=None,
127        )
128
129
130Sfield_T = TypeVar("Sfield_T")
131
132
133@overload
134def serializable_field(
135    *_args,
136    default_factory: Callable[[], Sfield_T],
137    default: dataclasses._MISSING_TYPE = dataclasses.MISSING,
138    init: bool = True,
139    repr: bool = True,
140    hash: Optional[bool] = None,
141    compare: bool = True,
142    metadata: Optional[types.MappingProxyType] = None,
143    kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
144    serialize: bool = True,
145    serialization_fn: Optional[Callable[[Any], Any]] = None,
146    deserialize_fn: Optional[Callable[[Any], Any]] = None,
147    assert_type: bool = True,
148    custom_typecheck_fn: Optional[Callable[[type], bool]] = None,
149    **kwargs: Any,
150) -> Sfield_T: ...
151@overload
152def serializable_field(
153    *_args,
154    default: Sfield_T,
155    default_factory: dataclasses._MISSING_TYPE = dataclasses.MISSING,
156    init: bool = True,
157    repr: bool = True,
158    hash: Optional[bool] = None,
159    compare: bool = True,
160    metadata: Optional[types.MappingProxyType] = None,
161    kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
162    serialize: bool = True,
163    serialization_fn: Optional[Callable[[Any], Any]] = None,
164    deserialize_fn: Optional[Callable[[Any], Any]] = None,
165    assert_type: bool = True,
166    custom_typecheck_fn: Optional[Callable[[type], bool]] = None,
167    **kwargs: Any,
168) -> Sfield_T: ...
169@overload
170def serializable_field(
171    *_args,
172    default: dataclasses._MISSING_TYPE = dataclasses.MISSING,
173    default_factory: dataclasses._MISSING_TYPE = dataclasses.MISSING,
174    init: bool = True,
175    repr: bool = True,
176    hash: Optional[bool] = None,
177    compare: bool = True,
178    metadata: Optional[types.MappingProxyType] = None,
179    kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
180    serialize: bool = True,
181    serialization_fn: Optional[Callable[[Any], Any]] = None,
182    deserialize_fn: Optional[Callable[[Any], Any]] = None,
183    assert_type: bool = True,
184    custom_typecheck_fn: Optional[Callable[[type], bool]] = None,
185    **kwargs: Any,
186) -> Any: ...
187def serializable_field(
188    *_args,
189    default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
190    default_factory: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
191    init: bool = True,
192    repr: bool = True,
193    hash: Optional[bool] = None,
194    compare: bool = True,
195    metadata: Optional[types.MappingProxyType] = None,
196    kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
197    serialize: bool = True,
198    serialization_fn: Optional[Callable[[Any], Any]] = None,
199    deserialize_fn: Optional[Callable[[Any], Any]] = None,
200    assert_type: bool = True,
201    custom_typecheck_fn: Optional[Callable[[type], bool]] = None,
202    **kwargs: Any,
203) -> Any:
204    """Create a new `SerializableField`
205
206    ```
207    default: Sfield_T | dataclasses._MISSING_TYPE = dataclasses.MISSING,
208    default_factory: Callable[[], Sfield_T]
209    | dataclasses._MISSING_TYPE = dataclasses.MISSING,
210    init: bool = True,
211    repr: bool = True,
212    hash: Optional[bool] = None,
213    compare: bool = True,
214    metadata: types.MappingProxyType | None = None,
215    kw_only: bool | dataclasses._MISSING_TYPE = dataclasses.MISSING,
216    # ----------------------------------------------------------------------
217    # new in `SerializableField`, not in `dataclasses.Field`
218    serialize: bool = True,
219    serialization_fn: Optional[Callable[[Any], Any]] = None,
220    loading_fn: Optional[Callable[[Any], Any]] = None,
221    deserialize_fn: Optional[Callable[[Any], Any]] = None,
222    assert_type: bool = True,
223    custom_typecheck_fn: Optional[Callable[[type], bool]] = None,
224    ```
225
226    # new Parameters:
227    - `serialize`: whether to serialize this field when serializing the class'
228    - `serialization_fn`: function taking the instance of the field and returning a serializable object. If not provided, will iterate through the `SerializerHandler`s defined in `muutils.json_serialize.json_serialize`
229    - `loading_fn`: function taking the serialized object and returning the instance of the field. If not provided, will take object as-is.
230    - `deserialize_fn`: new alternative to `loading_fn`. takes only the field's value, not the whole class. if both `loading_fn` and `deserialize_fn` are provided, an error will be raised.
231
232    # Gotchas:
233    - `loading_fn` takes the dict of the **class**, not the field. if you wanted a `loading_fn` that does nothing, you'd write:
234
235    ```python
236    class MyClass:
237        my_field: int = serializable_field(
238            serialization_fn=lambda x: str(x),
239            loading_fn=lambda x["my_field"]: int(x)
240        )
241    ```
242
243    using `deserialize_fn` instead:
244
245    ```python
246    class MyClass:
247        my_field: int = serializable_field(
248            serialization_fn=lambda x: str(x),
249            deserialize_fn=lambda x: int(x)
250        )
251    ```
252
253    In the above code, `my_field` is an int but will be serialized as a string.
254
255    note that if not using ZANJ, and you have a class inside a container, you MUST provide
256    `serialization_fn` and `loading_fn` to serialize and load the container.
257    ZANJ will automatically do this for you.
258    """
259    assert len(_args) == 0, f"unexpected positional arguments: {_args}"
260    return SerializableField(
261        default=default,
262        default_factory=default_factory,
263        init=init,
264        repr=repr,
265        hash=hash,
266        compare=compare,
267        metadata=metadata,
268        kw_only=kw_only,
269        serialize=serialize,
270        serialization_fn=serialization_fn,
271        deserialize_fn=deserialize_fn,
272        assert_type=assert_type,
273        custom_typecheck_fn=custom_typecheck_fn,
274        **kwargs,
275    )

class SerializableField(dataclasses.Field):
 21class SerializableField(dataclasses.Field):
 22    """extension of `dataclasses.Field` with additional serialization properties"""
 23
 24    __slots__ = (
 25        # from dataclasses.Field.__slots__
 26        "name",
 27        "type",
 28        "default",
 29        "default_factory",
 30        "repr",
 31        "hash",
 32        "init",
 33        "compare",
 34        "metadata",
 35        "kw_only",
 36        "_field_type",  # Private: not to be used by user code.
 37        # new ones
 38        "serialize",
 39        "serialization_fn",
 40        "loading_fn",
 41        "deserialize_fn",  # new alternative to loading_fn
 42        "assert_type",
 43        "custom_typecheck_fn",
 44    )
 45
 46    def __init__(
 47        self,
 48        default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
 49        default_factory: Union[
 50            Callable[[], Any], dataclasses._MISSING_TYPE
 51        ] = dataclasses.MISSING,
 52        init: bool = True,
 53        repr: bool = True,
 54        hash: Optional[bool] = None,
 55        compare: bool = True,
 56        # TODO: add field for custom comparator (such as serializing)
 57        metadata: Optional[types.MappingProxyType] = None,
 58        kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
 59        serialize: bool = True,
 60        serialization_fn: Optional[Callable[[Any], Any]] = None,
 61        loading_fn: Optional[Callable[[Any], Any]] = None,
 62        deserialize_fn: Optional[Callable[[Any], Any]] = None,
 63        assert_type: bool = True,
 64        custom_typecheck_fn: Optional[Callable[[type], bool]] = None,
 65    ):
 66        # TODO: should we do this check, or assume the user knows what they are doing?
 67        if init and not serialize:
 68            raise ValueError("Cannot have init=True and serialize=False")
 69
 70        # need to assemble kwargs in this hacky way so as not to upset type checking
 71        super_kwargs: dict[str, Any] = dict(
 72            default=default,
 73            default_factory=default_factory,
 74            init=init,
 75            repr=repr,
 76            hash=hash,
 77            compare=compare,
 78            kw_only=kw_only,
 79        )
 80
 81        if metadata is not None:
 82            super_kwargs["metadata"] = metadata
 83        else:
 84            super_kwargs["metadata"] = types.MappingProxyType({})
 85
 86        # special check, kw_only is not supported in python <3.9 and `dataclasses.MISSING` is truthy
 87        if sys.version_info < (3, 10):
 88            if super_kwargs["kw_only"] == True:  # noqa: E712
 89                raise ValueError("kw_only is not supported in python >=3.9")
 90            else:
 91                del super_kwargs["kw_only"]
 92
 93        # actually init the super class
 94        super().__init__(**super_kwargs)  # type: ignore[call-arg]
 95
 96        # now init the new fields
 97        self.serialize: bool = serialize
 98        self.serialization_fn: Optional[Callable[[Any], Any]] = serialization_fn
 99
100        if loading_fn is not None and deserialize_fn is not None:
101            raise ValueError(
102                "Cannot pass both loading_fn and deserialize_fn, pass only one. ",
103                "`loading_fn` is the older interface and takes the dict of the class, ",
104                "`deserialize_fn` is the new interface and takes only the field's value.",
105            )
106        self.loading_fn: Optional[Callable[[Any], Any]] = loading_fn
107        self.deserialize_fn: Optional[Callable[[Any], Any]] = deserialize_fn
108
109        self.assert_type: bool = assert_type
110        self.custom_typecheck_fn: Optional[Callable[[type], bool]] = custom_typecheck_fn
111
112    @classmethod
113    def from_Field(cls, field: dataclasses.Field) -> "SerializableField":
114        """copy all values from a `dataclasses.Field` to new `SerializableField`"""
115        return cls(
116            default=field.default,
117            default_factory=field.default_factory,
118            init=field.init,
119            repr=field.repr,
120            hash=field.hash,
121            compare=field.compare,
122            metadata=field.metadata,
123            kw_only=getattr(field, "kw_only", dataclasses.MISSING),  # for python <3.9
124            serialize=field.repr,  # serialize if it's going to be repr'd
125            serialization_fn=None,
126            loading_fn=None,
127            deserialize_fn=None,
128        )

extension of dataclasses.Field with additional serialization properties

SerializableField( default: Union[Any, dataclasses._MISSING_TYPE] = <dataclasses._MISSING_TYPE object>, default_factory: Union[Callable[[], Any], dataclasses._MISSING_TYPE] = <dataclasses._MISSING_TYPE object>, init: bool = True, repr: bool = True, hash: Optional[bool] = None, compare: bool = True, metadata: Optional[mappingproxy] = None, kw_only: Union[bool, dataclasses._MISSING_TYPE] = <dataclasses._MISSING_TYPE object>, serialize: bool = True, serialization_fn: Optional[Callable[[Any], Any]] = None, loading_fn: Optional[Callable[[Any], Any]] = None, deserialize_fn: Optional[Callable[[Any], Any]] = None, assert_type: bool = True, custom_typecheck_fn: Optional[Callable[[<member 'type' of 'SerializableField' objects>], bool]] = None)
 46    def __init__(
 47        self,
 48        default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
 49        default_factory: Union[
 50            Callable[[], Any], dataclasses._MISSING_TYPE
 51        ] = dataclasses.MISSING,
 52        init: bool = True,
 53        repr: bool = True,
 54        hash: Optional[bool] = None,
 55        compare: bool = True,
 56        # TODO: add field for custom comparator (such as serializing)
 57        metadata: Optional[types.MappingProxyType] = None,
 58        kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
 59        serialize: bool = True,
 60        serialization_fn: Optional[Callable[[Any], Any]] = None,
 61        loading_fn: Optional[Callable[[Any], Any]] = None,
 62        deserialize_fn: Optional[Callable[[Any], Any]] = None,
 63        assert_type: bool = True,
 64        custom_typecheck_fn: Optional[Callable[[type], bool]] = None,
 65    ):
 66        # TODO: should we do this check, or assume the user knows what they are doing?
 67        if init and not serialize:
 68            raise ValueError("Cannot have init=True and serialize=False")
 69
 70        # need to assemble kwargs in this hacky way so as not to upset type checking
 71        super_kwargs: dict[str, Any] = dict(
 72            default=default,
 73            default_factory=default_factory,
 74            init=init,
 75            repr=repr,
 76            hash=hash,
 77            compare=compare,
 78            kw_only=kw_only,
 79        )
 80
 81        if metadata is not None:
 82            super_kwargs["metadata"] = metadata
 83        else:
 84            super_kwargs["metadata"] = types.MappingProxyType({})
 85
 86        # special check, kw_only is not supported in python <3.9 and `dataclasses.MISSING` is truthy
 87        if sys.version_info < (3, 10):
 88            if super_kwargs["kw_only"] == True:  # noqa: E712
 89                raise ValueError("kw_only is not supported in python >=3.9")
 90            else:
 91                del super_kwargs["kw_only"]
 92
 93        # actually init the super class
 94        super().__init__(**super_kwargs)  # type: ignore[call-arg]
 95
 96        # now init the new fields
 97        self.serialize: bool = serialize
 98        self.serialization_fn: Optional[Callable[[Any], Any]] = serialization_fn
 99
100        if loading_fn is not None and deserialize_fn is not None:
101            raise ValueError(
102                "Cannot pass both loading_fn and deserialize_fn, pass only one. ",
103                "`loading_fn` is the older interface and takes the dict of the class, ",
104                "`deserialize_fn` is the new interface and takes only the field's value.",
105            )
106        self.loading_fn: Optional[Callable[[Any], Any]] = loading_fn
107        self.deserialize_fn: Optional[Callable[[Any], Any]] = deserialize_fn
108
109        self.assert_type: bool = assert_type
110        self.custom_typecheck_fn: Optional[Callable[[type], bool]] = custom_typecheck_fn
serialize: bool
serialization_fn: Optional[Callable[[Any], Any]]
loading_fn: Optional[Callable[[Any], Any]]
deserialize_fn: Optional[Callable[[Any], Any]]
assert_type: bool
custom_typecheck_fn: Optional[Callable[[<member 'type' of 'SerializableField' objects>], bool]]
@classmethod
def from_Field( cls, field: dataclasses.Field) -> SerializableField:
112    @classmethod
113    def from_Field(cls, field: dataclasses.Field) -> "SerializableField":
114        """copy all values from a `dataclasses.Field` to new `SerializableField`"""
115        return cls(
116            default=field.default,
117            default_factory=field.default_factory,
118            init=field.init,
119            repr=field.repr,
120            hash=field.hash,
121            compare=field.compare,
122            metadata=field.metadata,
123            kw_only=getattr(field, "kw_only", dataclasses.MISSING),  # for python <3.9
124            serialize=field.repr,  # serialize if it's going to be repr'd
125            serialization_fn=None,
126            loading_fn=None,
127            deserialize_fn=None,
128        )

copy all values from a dataclasses.Field to new SerializableField

name
type
default
default_factory
init
repr
hash
compare
metadata
kw_only
def serializable_field( *_args, default: Union[Any, dataclasses._MISSING_TYPE] = <dataclasses._MISSING_TYPE object>, default_factory: Union[Any, dataclasses._MISSING_TYPE] = <dataclasses._MISSING_TYPE object>, init: bool = True, repr: bool = True, hash: Optional[bool] = None, compare: bool = True, metadata: Optional[mappingproxy] = None, kw_only: Union[bool, dataclasses._MISSING_TYPE] = <dataclasses._MISSING_TYPE object>, serialize: bool = True, serialization_fn: Optional[Callable[[Any], Any]] = None, deserialize_fn: Optional[Callable[[Any], Any]] = None, assert_type: bool = True, custom_typecheck_fn: Optional[Callable[[type], bool]] = None, **kwargs: Any) -> Any:
188def serializable_field(
189    *_args,
190    default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
191    default_factory: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
192    init: bool = True,
193    repr: bool = True,
194    hash: Optional[bool] = None,
195    compare: bool = True,
196    metadata: Optional[types.MappingProxyType] = None,
197    kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING,
198    serialize: bool = True,
199    serialization_fn: Optional[Callable[[Any], Any]] = None,
200    deserialize_fn: Optional[Callable[[Any], Any]] = None,
201    assert_type: bool = True,
202    custom_typecheck_fn: Optional[Callable[[type], bool]] = None,
203    **kwargs: Any,
204) -> Any:
205    """Create a new `SerializableField`
206
207    ```
208    default: Sfield_T | dataclasses._MISSING_TYPE = dataclasses.MISSING,
209    default_factory: Callable[[], Sfield_T]
210    | dataclasses._MISSING_TYPE = dataclasses.MISSING,
211    init: bool = True,
212    repr: bool = True,
213    hash: Optional[bool] = None,
214    compare: bool = True,
215    metadata: types.MappingProxyType | None = None,
216    kw_only: bool | dataclasses._MISSING_TYPE = dataclasses.MISSING,
217    # ----------------------------------------------------------------------
218    # new in `SerializableField`, not in `dataclasses.Field`
219    serialize: bool = True,
220    serialization_fn: Optional[Callable[[Any], Any]] = None,
221    loading_fn: Optional[Callable[[Any], Any]] = None,
222    deserialize_fn: Optional[Callable[[Any], Any]] = None,
223    assert_type: bool = True,
224    custom_typecheck_fn: Optional[Callable[[type], bool]] = None,
225    ```
226
227    # new Parameters:
228    - `serialize`: whether to serialize this field when serializing the class'
229    - `serialization_fn`: function taking the instance of the field and returning a serializable object. If not provided, will iterate through the `SerializerHandler`s defined in `muutils.json_serialize.json_serialize`
230    - `loading_fn`: function taking the serialized object and returning the instance of the field. If not provided, will take object as-is.
231    - `deserialize_fn`: new alternative to `loading_fn`. takes only the field's value, not the whole class. if both `loading_fn` and `deserialize_fn` are provided, an error will be raised.
232
233    # Gotchas:
234    - `loading_fn` takes the dict of the **class**, not the field. if you wanted a `loading_fn` that does nothing, you'd write:
235
236    ```python
237    class MyClass:
238        my_field: int = serializable_field(
239            serialization_fn=lambda x: str(x),
240            loading_fn=lambda x["my_field"]: int(x)
241        )
242    ```
243
244    using `deserialize_fn` instead:
245
246    ```python
247    class MyClass:
248        my_field: int = serializable_field(
249            serialization_fn=lambda x: str(x),
250            deserialize_fn=lambda x: int(x)
251        )
252    ```
253
254    In the above code, `my_field` is an int but will be serialized as a string.
255
256    note that if not using ZANJ, and you have a class inside a container, you MUST provide
257    `serialization_fn` and `loading_fn` to serialize and load the container.
258    ZANJ will automatically do this for you.
259    """
260    assert len(_args) == 0, f"unexpected positional arguments: {_args}"
261    return SerializableField(
262        default=default,
263        default_factory=default_factory,
264        init=init,
265        repr=repr,
266        hash=hash,
267        compare=compare,
268        metadata=metadata,
269        kw_only=kw_only,
270        serialize=serialize,
271        serialization_fn=serialization_fn,
272        deserialize_fn=deserialize_fn,
273        assert_type=assert_type,
274        custom_typecheck_fn=custom_typecheck_fn,
275        **kwargs,
276    )

Create a new SerializableField

default: Sfield_T | dataclasses._MISSING_TYPE = dataclasses.MISSING,
default_factory: Callable[[], Sfield_T]
| dataclasses._MISSING_TYPE = dataclasses.MISSING,
init: bool = True,
repr: bool = True,
hash: Optional[bool] = None,
compare: bool = True,
metadata: types.MappingProxyType | None = None,
kw_only: bool | dataclasses._MISSING_TYPE = dataclasses.MISSING,
# ----------------------------------------------------------------------
# new in `SerializableField`, not in `dataclasses.Field`
serialize: bool = True,
serialization_fn: Optional[Callable[[Any], Any]] = None,
loading_fn: Optional[Callable[[Any], Any]] = None,
deserialize_fn: Optional[Callable[[Any], Any]] = None,
assert_type: bool = True,
custom_typecheck_fn: Optional[Callable[[type], bool]] = None,

new Parameters:

  • serialize: whether to serialize this field when serializing the class'
  • serialization_fn: function taking the instance of the field and returning a serializable object. If not provided, will iterate through the SerializerHandlers defined in muutils.json_serialize.json_serialize
  • loading_fn: function taking the serialized object and returning the instance of the field. If not provided, will take object as-is.
  • deserialize_fn: new alternative to loading_fn. takes only the field's value, not the whole class. if both loading_fn and deserialize_fn are provided, an error will be raised.

Gotchas:

  • loading_fn takes the dict of the class, not the field. if you wanted a loading_fn that does nothing, you'd write:
class MyClass:
    my_field: int = serializable_field(
        serialization_fn=lambda x: str(x),
        loading_fn=lambda x["my_field"]: int(x)
    )

using deserialize_fn instead:

class MyClass:
    my_field: int = serializable_field(
        serialization_fn=lambda x: str(x),
        deserialize_fn=lambda x: int(x)
    )

In the above code, my_field is an int but will be serialized as a string.

note that if not using ZANJ, and you have a class inside a container, you MUST provide serialization_fn and loading_fn to serialize and load the container. ZANJ will automatically do this for you.