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 "doc", 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 doc: str | None = None, 57 # TODO: add field for custom comparator (such as serializing) 58 metadata: Optional[types.MappingProxyType] = None, 59 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 60 serialize: bool = True, 61 serialization_fn: Optional[Callable[[Any], Any]] = None, 62 loading_fn: Optional[Callable[[Any], Any]] = None, 63 deserialize_fn: Optional[Callable[[Any], Any]] = None, 64 assert_type: bool = True, 65 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 66 ): 67 # TODO: should we do this check, or assume the user knows what they are doing? 68 if init and not serialize: 69 raise ValueError("Cannot have init=True and serialize=False") 70 71 # need to assemble kwargs in this hacky way so as not to upset type checking 72 super_kwargs: dict[str, Any] = dict( 73 default=default, 74 default_factory=default_factory, 75 init=init, 76 repr=repr, 77 hash=hash, 78 compare=compare, 79 kw_only=kw_only, 80 ) 81 82 if metadata is not None: 83 super_kwargs["metadata"] = metadata 84 else: 85 super_kwargs["metadata"] = types.MappingProxyType({}) 86 87 # only pass `doc` to super if python >=3.14 88 if sys.version_info >= (3, 14): 89 super_kwargs["doc"] = doc 90 91 # special check, kw_only is not supported in python <3.9 and `dataclasses.MISSING` is truthy 92 if sys.version_info < (3, 10): 93 if super_kwargs["kw_only"] == True: # noqa: E712 94 raise ValueError("kw_only is not supported in python >=3.9") 95 else: 96 del super_kwargs["kw_only"] 97 98 # actually init the super class 99 super().__init__(**super_kwargs) # type: ignore[call-arg] 100 101 # init doc if python <3.14 102 if sys.version_info < (3, 14): 103 self.doc: str | None = doc 104 105 # now init the new fields 106 self.serialize: bool = serialize 107 self.serialization_fn: Optional[Callable[[Any], Any]] = serialization_fn 108 109 if loading_fn is not None and deserialize_fn is not None: 110 raise ValueError( 111 "Cannot pass both loading_fn and deserialize_fn, pass only one. ", 112 "`loading_fn` is the older interface and takes the dict of the class, ", 113 "`deserialize_fn` is the new interface and takes only the field's value.", 114 ) 115 self.loading_fn: Optional[Callable[[Any], Any]] = loading_fn 116 self.deserialize_fn: Optional[Callable[[Any], Any]] = deserialize_fn 117 118 self.assert_type: bool = assert_type 119 self.custom_typecheck_fn: Optional[Callable[[type], bool]] = custom_typecheck_fn 120 121 @classmethod 122 def from_Field(cls, field: dataclasses.Field) -> "SerializableField": 123 """copy all values from a `dataclasses.Field` to new `SerializableField`""" 124 return cls( 125 default=field.default, 126 default_factory=field.default_factory, 127 init=field.init, 128 repr=field.repr, 129 hash=field.hash, 130 compare=field.compare, 131 doc=getattr(field, "doc", None), # `doc` added in python <3.14 132 metadata=field.metadata, 133 kw_only=getattr(field, "kw_only", dataclasses.MISSING), # for python <3.9 134 serialize=field.repr, # serialize if it's going to be repr'd 135 serialization_fn=None, 136 loading_fn=None, 137 deserialize_fn=None, 138 ) 139 140 141Sfield_T = TypeVar("Sfield_T") 142 143 144@overload 145def serializable_field( # only `default_factory` is provided 146 *_args, 147 default_factory: Callable[[], Sfield_T], 148 default: dataclasses._MISSING_TYPE = dataclasses.MISSING, 149 init: bool = True, 150 repr: bool = True, 151 hash: Optional[bool] = None, 152 compare: bool = True, 153 doc: str | None = None, 154 metadata: Optional[types.MappingProxyType] = None, 155 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 156 serialize: bool = True, 157 serialization_fn: Optional[Callable[[Any], Any]] = None, 158 deserialize_fn: Optional[Callable[[Any], Any]] = None, 159 assert_type: bool = True, 160 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 161 **kwargs: Any, 162) -> Sfield_T: ... 163@overload 164def serializable_field( # only `default` is provided 165 *_args, 166 default: Sfield_T, 167 default_factory: dataclasses._MISSING_TYPE = dataclasses.MISSING, 168 init: bool = True, 169 repr: bool = True, 170 hash: Optional[bool] = None, 171 compare: bool = True, 172 doc: str | None = None, 173 metadata: Optional[types.MappingProxyType] = None, 174 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 175 serialize: bool = True, 176 serialization_fn: Optional[Callable[[Any], Any]] = None, 177 deserialize_fn: Optional[Callable[[Any], Any]] = None, 178 assert_type: bool = True, 179 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 180 **kwargs: Any, 181) -> Sfield_T: ... 182@overload 183def serializable_field( # both `default` and `default_factory` are MISSING 184 *_args, 185 default: dataclasses._MISSING_TYPE = dataclasses.MISSING, 186 default_factory: dataclasses._MISSING_TYPE = dataclasses.MISSING, 187 init: bool = True, 188 repr: bool = True, 189 hash: Optional[bool] = None, 190 compare: bool = True, 191 doc: str | None = None, 192 metadata: Optional[types.MappingProxyType] = None, 193 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 194 serialize: bool = True, 195 serialization_fn: Optional[Callable[[Any], Any]] = None, 196 deserialize_fn: Optional[Callable[[Any], Any]] = None, 197 assert_type: bool = True, 198 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 199 **kwargs: Any, 200) -> Any: ... 201def serializable_field( # general implementation 202 *_args, 203 default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 204 default_factory: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 205 init: bool = True, 206 repr: bool = True, 207 hash: Optional[bool] = None, 208 compare: bool = True, 209 doc: str | None = None, 210 metadata: Optional[types.MappingProxyType] = None, 211 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 212 serialize: bool = True, 213 serialization_fn: Optional[Callable[[Any], Any]] = None, 214 deserialize_fn: Optional[Callable[[Any], Any]] = None, 215 assert_type: bool = True, 216 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 217 **kwargs: Any, 218) -> Any: 219 """Create a new `SerializableField` 220 221 ``` 222 default: Sfield_T | dataclasses._MISSING_TYPE = dataclasses.MISSING, 223 default_factory: Callable[[], Sfield_T] 224 | dataclasses._MISSING_TYPE = dataclasses.MISSING, 225 init: bool = True, 226 repr: bool = True, 227 hash: Optional[bool] = None, 228 compare: bool = True, 229 doc: str | None = None, # new in python 3.14. can alternately pass `description` to match pydantic, but this is discouraged 230 metadata: types.MappingProxyType | None = None, 231 kw_only: bool | dataclasses._MISSING_TYPE = dataclasses.MISSING, 232 # ---------------------------------------------------------------------- 233 # new in `SerializableField`, not in `dataclasses.Field` 234 serialize: bool = True, 235 serialization_fn: Optional[Callable[[Any], Any]] = None, 236 loading_fn: Optional[Callable[[Any], Any]] = None, 237 deserialize_fn: Optional[Callable[[Any], Any]] = None, 238 assert_type: bool = True, 239 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 240 ``` 241 242 # new Parameters: 243 - `serialize`: whether to serialize this field when serializing the class' 244 - `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` 245 - `loading_fn`: function taking the serialized object and returning the instance of the field. If not provided, will take object as-is. 246 - `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. 247 - `assert_type`: whether to assert the type of the field when loading. if `False`, will not check the type of the field. 248 - `custom_typecheck_fn`: function taking the type of the field and returning whether the type itself is valid. if not provided, will use the default type checking. 249 250 # Gotchas: 251 - `loading_fn` takes the dict of the **class**, not the field. if you wanted a `loading_fn` that does nothing, you'd write: 252 253 ```python 254 class MyClass: 255 my_field: int = serializable_field( 256 serialization_fn=lambda x: str(x), 257 loading_fn=lambda x["my_field"]: int(x) 258 ) 259 ``` 260 261 using `deserialize_fn` instead: 262 263 ```python 264 class MyClass: 265 my_field: int = serializable_field( 266 serialization_fn=lambda x: str(x), 267 deserialize_fn=lambda x: int(x) 268 ) 269 ``` 270 271 In the above code, `my_field` is an int but will be serialized as a string. 272 273 note that if not using ZANJ, and you have a class inside a container, you MUST provide 274 `serialization_fn` and `loading_fn` to serialize and load the container. 275 ZANJ will automatically do this for you. 276 277 # TODO: `custom_value_check_fn`: function taking the value of the field and returning whether the value itself is valid. if not provided, any value is valid as long as it passes the type test 278 """ 279 assert len(_args) == 0, f"unexpected positional arguments: {_args}" 280 281 if "description" in kwargs: 282 import warnings 283 284 warnings.warn( 285 "`description` is deprecated, use `doc` instead", 286 DeprecationWarning, 287 ) 288 if doc is not None: 289 err_msg: str = f"cannot pass both `doc` and `description`: {doc=}, {kwargs['description']=}" 290 raise ValueError(err_msg) 291 doc = kwargs.pop("description") 292 293 return SerializableField( 294 default=default, 295 default_factory=default_factory, 296 init=init, 297 repr=repr, 298 hash=hash, 299 compare=compare, 300 metadata=metadata, 301 kw_only=kw_only, 302 serialize=serialize, 303 serialization_fn=serialization_fn, 304 deserialize_fn=deserialize_fn, 305 assert_type=assert_type, 306 custom_typecheck_fn=custom_typecheck_fn, 307 **kwargs, 308 )
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 "doc", 35 "metadata", 36 "kw_only", 37 "_field_type", # Private: not to be used by user code. 38 # new ones 39 "serialize", 40 "serialization_fn", 41 "loading_fn", 42 "deserialize_fn", # new alternative to loading_fn 43 "assert_type", 44 "custom_typecheck_fn", 45 ) 46 47 def __init__( 48 self, 49 default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 50 default_factory: Union[ 51 Callable[[], Any], dataclasses._MISSING_TYPE 52 ] = dataclasses.MISSING, 53 init: bool = True, 54 repr: bool = True, 55 hash: Optional[bool] = None, 56 compare: bool = True, 57 doc: str | None = None, 58 # TODO: add field for custom comparator (such as serializing) 59 metadata: Optional[types.MappingProxyType] = None, 60 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 61 serialize: bool = True, 62 serialization_fn: Optional[Callable[[Any], Any]] = None, 63 loading_fn: Optional[Callable[[Any], Any]] = None, 64 deserialize_fn: Optional[Callable[[Any], Any]] = None, 65 assert_type: bool = True, 66 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 67 ): 68 # TODO: should we do this check, or assume the user knows what they are doing? 69 if init and not serialize: 70 raise ValueError("Cannot have init=True and serialize=False") 71 72 # need to assemble kwargs in this hacky way so as not to upset type checking 73 super_kwargs: dict[str, Any] = dict( 74 default=default, 75 default_factory=default_factory, 76 init=init, 77 repr=repr, 78 hash=hash, 79 compare=compare, 80 kw_only=kw_only, 81 ) 82 83 if metadata is not None: 84 super_kwargs["metadata"] = metadata 85 else: 86 super_kwargs["metadata"] = types.MappingProxyType({}) 87 88 # only pass `doc` to super if python >=3.14 89 if sys.version_info >= (3, 14): 90 super_kwargs["doc"] = doc 91 92 # special check, kw_only is not supported in python <3.9 and `dataclasses.MISSING` is truthy 93 if sys.version_info < (3, 10): 94 if super_kwargs["kw_only"] == True: # noqa: E712 95 raise ValueError("kw_only is not supported in python >=3.9") 96 else: 97 del super_kwargs["kw_only"] 98 99 # actually init the super class 100 super().__init__(**super_kwargs) # type: ignore[call-arg] 101 102 # init doc if python <3.14 103 if sys.version_info < (3, 14): 104 self.doc: str | None = doc 105 106 # now init the new fields 107 self.serialize: bool = serialize 108 self.serialization_fn: Optional[Callable[[Any], Any]] = serialization_fn 109 110 if loading_fn is not None and deserialize_fn is not None: 111 raise ValueError( 112 "Cannot pass both loading_fn and deserialize_fn, pass only one. ", 113 "`loading_fn` is the older interface and takes the dict of the class, ", 114 "`deserialize_fn` is the new interface and takes only the field's value.", 115 ) 116 self.loading_fn: Optional[Callable[[Any], Any]] = loading_fn 117 self.deserialize_fn: Optional[Callable[[Any], Any]] = deserialize_fn 118 119 self.assert_type: bool = assert_type 120 self.custom_typecheck_fn: Optional[Callable[[type], bool]] = custom_typecheck_fn 121 122 @classmethod 123 def from_Field(cls, field: dataclasses.Field) -> "SerializableField": 124 """copy all values from a `dataclasses.Field` to new `SerializableField`""" 125 return cls( 126 default=field.default, 127 default_factory=field.default_factory, 128 init=field.init, 129 repr=field.repr, 130 hash=field.hash, 131 compare=field.compare, 132 doc=getattr(field, "doc", None), # `doc` added in python <3.14 133 metadata=field.metadata, 134 kw_only=getattr(field, "kw_only", dataclasses.MISSING), # for python <3.9 135 serialize=field.repr, # serialize if it's going to be repr'd 136 serialization_fn=None, 137 loading_fn=None, 138 deserialize_fn=None, 139 )
extension of dataclasses.Field with additional serialization properties
47 def __init__( 48 self, 49 default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 50 default_factory: Union[ 51 Callable[[], Any], dataclasses._MISSING_TYPE 52 ] = dataclasses.MISSING, 53 init: bool = True, 54 repr: bool = True, 55 hash: Optional[bool] = None, 56 compare: bool = True, 57 doc: str | None = None, 58 # TODO: add field for custom comparator (such as serializing) 59 metadata: Optional[types.MappingProxyType] = None, 60 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 61 serialize: bool = True, 62 serialization_fn: Optional[Callable[[Any], Any]] = None, 63 loading_fn: Optional[Callable[[Any], Any]] = None, 64 deserialize_fn: Optional[Callable[[Any], Any]] = None, 65 assert_type: bool = True, 66 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 67 ): 68 # TODO: should we do this check, or assume the user knows what they are doing? 69 if init and not serialize: 70 raise ValueError("Cannot have init=True and serialize=False") 71 72 # need to assemble kwargs in this hacky way so as not to upset type checking 73 super_kwargs: dict[str, Any] = dict( 74 default=default, 75 default_factory=default_factory, 76 init=init, 77 repr=repr, 78 hash=hash, 79 compare=compare, 80 kw_only=kw_only, 81 ) 82 83 if metadata is not None: 84 super_kwargs["metadata"] = metadata 85 else: 86 super_kwargs["metadata"] = types.MappingProxyType({}) 87 88 # only pass `doc` to super if python >=3.14 89 if sys.version_info >= (3, 14): 90 super_kwargs["doc"] = doc 91 92 # special check, kw_only is not supported in python <3.9 and `dataclasses.MISSING` is truthy 93 if sys.version_info < (3, 10): 94 if super_kwargs["kw_only"] == True: # noqa: E712 95 raise ValueError("kw_only is not supported in python >=3.9") 96 else: 97 del super_kwargs["kw_only"] 98 99 # actually init the super class 100 super().__init__(**super_kwargs) # type: ignore[call-arg] 101 102 # init doc if python <3.14 103 if sys.version_info < (3, 14): 104 self.doc: str | None = doc 105 106 # now init the new fields 107 self.serialize: bool = serialize 108 self.serialization_fn: Optional[Callable[[Any], Any]] = serialization_fn 109 110 if loading_fn is not None and deserialize_fn is not None: 111 raise ValueError( 112 "Cannot pass both loading_fn and deserialize_fn, pass only one. ", 113 "`loading_fn` is the older interface and takes the dict of the class, ", 114 "`deserialize_fn` is the new interface and takes only the field's value.", 115 ) 116 self.loading_fn: Optional[Callable[[Any], Any]] = loading_fn 117 self.deserialize_fn: Optional[Callable[[Any], Any]] = deserialize_fn 118 119 self.assert_type: bool = assert_type 120 self.custom_typecheck_fn: Optional[Callable[[type], bool]] = custom_typecheck_fn
122 @classmethod 123 def from_Field(cls, field: dataclasses.Field) -> "SerializableField": 124 """copy all values from a `dataclasses.Field` to new `SerializableField`""" 125 return cls( 126 default=field.default, 127 default_factory=field.default_factory, 128 init=field.init, 129 repr=field.repr, 130 hash=field.hash, 131 compare=field.compare, 132 doc=getattr(field, "doc", None), # `doc` added in python <3.14 133 metadata=field.metadata, 134 kw_only=getattr(field, "kw_only", dataclasses.MISSING), # for python <3.9 135 serialize=field.repr, # serialize if it's going to be repr'd 136 serialization_fn=None, 137 loading_fn=None, 138 deserialize_fn=None, 139 )
copy all values from a dataclasses.Field to new SerializableField
202def serializable_field( # general implementation 203 *_args, 204 default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 205 default_factory: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 206 init: bool = True, 207 repr: bool = True, 208 hash: Optional[bool] = None, 209 compare: bool = True, 210 doc: str | None = None, 211 metadata: Optional[types.MappingProxyType] = None, 212 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 213 serialize: bool = True, 214 serialization_fn: Optional[Callable[[Any], Any]] = None, 215 deserialize_fn: Optional[Callable[[Any], Any]] = None, 216 assert_type: bool = True, 217 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 218 **kwargs: Any, 219) -> Any: 220 """Create a new `SerializableField` 221 222 ``` 223 default: Sfield_T | dataclasses._MISSING_TYPE = dataclasses.MISSING, 224 default_factory: Callable[[], Sfield_T] 225 | dataclasses._MISSING_TYPE = dataclasses.MISSING, 226 init: bool = True, 227 repr: bool = True, 228 hash: Optional[bool] = None, 229 compare: bool = True, 230 doc: str | None = None, # new in python 3.14. can alternately pass `description` to match pydantic, but this is discouraged 231 metadata: types.MappingProxyType | None = None, 232 kw_only: bool | dataclasses._MISSING_TYPE = dataclasses.MISSING, 233 # ---------------------------------------------------------------------- 234 # new in `SerializableField`, not in `dataclasses.Field` 235 serialize: bool = True, 236 serialization_fn: Optional[Callable[[Any], Any]] = None, 237 loading_fn: Optional[Callable[[Any], Any]] = None, 238 deserialize_fn: Optional[Callable[[Any], Any]] = None, 239 assert_type: bool = True, 240 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 241 ``` 242 243 # new Parameters: 244 - `serialize`: whether to serialize this field when serializing the class' 245 - `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` 246 - `loading_fn`: function taking the serialized object and returning the instance of the field. If not provided, will take object as-is. 247 - `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. 248 - `assert_type`: whether to assert the type of the field when loading. if `False`, will not check the type of the field. 249 - `custom_typecheck_fn`: function taking the type of the field and returning whether the type itself is valid. if not provided, will use the default type checking. 250 251 # Gotchas: 252 - `loading_fn` takes the dict of the **class**, not the field. if you wanted a `loading_fn` that does nothing, you'd write: 253 254 ```python 255 class MyClass: 256 my_field: int = serializable_field( 257 serialization_fn=lambda x: str(x), 258 loading_fn=lambda x["my_field"]: int(x) 259 ) 260 ``` 261 262 using `deserialize_fn` instead: 263 264 ```python 265 class MyClass: 266 my_field: int = serializable_field( 267 serialization_fn=lambda x: str(x), 268 deserialize_fn=lambda x: int(x) 269 ) 270 ``` 271 272 In the above code, `my_field` is an int but will be serialized as a string. 273 274 note that if not using ZANJ, and you have a class inside a container, you MUST provide 275 `serialization_fn` and `loading_fn` to serialize and load the container. 276 ZANJ will automatically do this for you. 277 278 # TODO: `custom_value_check_fn`: function taking the value of the field and returning whether the value itself is valid. if not provided, any value is valid as long as it passes the type test 279 """ 280 assert len(_args) == 0, f"unexpected positional arguments: {_args}" 281 282 if "description" in kwargs: 283 import warnings 284 285 warnings.warn( 286 "`description` is deprecated, use `doc` instead", 287 DeprecationWarning, 288 ) 289 if doc is not None: 290 err_msg: str = f"cannot pass both `doc` and `description`: {doc=}, {kwargs['description']=}" 291 raise ValueError(err_msg) 292 doc = kwargs.pop("description") 293 294 return SerializableField( 295 default=default, 296 default_factory=default_factory, 297 init=init, 298 repr=repr, 299 hash=hash, 300 compare=compare, 301 metadata=metadata, 302 kw_only=kw_only, 303 serialize=serialize, 304 serialization_fn=serialization_fn, 305 deserialize_fn=deserialize_fn, 306 assert_type=assert_type, 307 custom_typecheck_fn=custom_typecheck_fn, 308 **kwargs, 309 )
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,
doc: str | None = None, # new in python 3.14. can alternately pass `description` to match pydantic, but this is discouraged
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 theSerializerHandlers defined inmuutils.json_serialize.json_serializeloading_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 toloading_fn. takes only the field's value, not the whole class. if bothloading_fnanddeserialize_fnare provided, an error will be raised.assert_type: whether to assert the type of the field when loading. ifFalse, will not check the type of the field.custom_typecheck_fn: function taking the type of the field and returning whether the type itself is valid. if not provided, will use the default type checking.
Gotchas:
loading_fntakes the dict of the class, not the field. if you wanted aloading_fnthat 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.