Edit on GitHub

serde

pyserde

Yet another serialization library on top of dataclasses, inspired by serde-rs.

pypi pypi GithubActions CodeCov

GuideπŸ‡¬πŸ‡§ | γ‚¬γ‚€γƒ‰πŸ‡―πŸ‡΅ | API Reference | Examples

Overview

pyserde is a simple yet powerful serialization library on top of dataclasses. It allows you to convert Python objects to and from JSON, YAML, and other formats easily and efficiently.

Declare your class with @serde decorator and annotate fields using PEP484 as below.

@serde
class Foo:
    i: int
    s: str
    f: float
    b: bool

You can serialize Foo object into JSON.

>>> to_json(Foo(i=10, s='foo', f=100.0, b=True))
'{"i":10,"s":"foo","f":100.0,"b":true}'

You can deserialize JSON into Foo object.

>>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}')
Foo(i=10, s='foo', f=100.0, b=True)

That's it! If you're interested in pyserde, please check our documentation! Happy coding with pyserde! πŸš€

Features

Extensions

Contributors ✨

Thanks goes to these wonderful people:

Made with contrib.rocks.

LICENSE

This project is licensed under the MIT license.

Modules

The following modules provide the core functionalities of pyserde.

The following modules provide pyserde's (de)serialize APIs.

Other modules

  1"""
  2.. include:: ../README.md
  3
  4## Modules
  5
  6The following modules provide the core functionalities of `pyserde`.
  7* `serde.se`: All about serialization.
  8* `serde.de`: All about deserialization.
  9* `serde.core`: Core module used by `serde.se` and `serde.de` modules.
 10* `serde.compat`: Compatibility layer which handles mostly differences of `typing` module between
 11python versions.
 12
 13The following modules provide pyserde's (de)serialize APIs.
 14* `serde.json`: Serialize and Deserialize in JSON.
 15* `serde.msgpack`: Serialize and Deserialize in MsgPack.
 16* `serde.yaml`: Serialize and Deserialize in YAML.
 17* `serde.toml`: Serialize and Deserialize in TOML.
 18* `serde.pickle`: Serialize and Deserialize in Pickle.
 19
 20Other modules
 21* `serde.inspect`: Prints generated code by pyserde.
 22"""
 23
 24from dataclasses import dataclass
 25from collections.abc import Callable
 26from typing import Any, Type, overload
 27
 28from typing_extensions import dataclass_transform
 29
 30from .compat import SerdeError, SerdeSkip, T
 31from .core import (
 32    ClassSerializer,
 33    ClassDeserializer,
 34    AdjacentTagging,
 35    coerce,
 36    DefaultTagging,
 37    ExternalTagging,
 38    InternalTagging,
 39    disabled,
 40    strict,
 41    Tagging,
 42    TypeCheck,
 43    Untagged,
 44    field,
 45    init,
 46    logger,
 47    should_impl_dataclass,
 48    add_serializer,
 49    add_deserializer,
 50)
 51from .de import (
 52    DeserializeFunc,
 53    default_deserializer,
 54    deserialize,
 55    from_dict,
 56    from_tuple,
 57    is_deserializable,
 58)
 59from .se import (
 60    SerializeFunc,
 61    asdict,
 62    astuple,
 63    default_serializer,
 64    is_serializable,
 65    serialize,
 66    to_dict,
 67    to_tuple,
 68)
 69
 70__all__ = [
 71    "serde",
 72    "serialize",
 73    "deserialize",
 74    "is_serializable",
 75    "is_deserializable",
 76    "to_dict",
 77    "from_dict",
 78    "to_tuple",
 79    "from_tuple",
 80    "SerdeError",
 81    "SerdeSkip",
 82    "AdjacentTagging",
 83    "ExternalTagging",
 84    "InternalTagging",
 85    "Untagged",
 86    "disabled",
 87    "strict",
 88    "coerce",
 89    "field",
 90    "default_deserializer",
 91    "asdict",
 92    "astuple",
 93    "default_serializer",
 94    "compat",
 95    "core",
 96    "de",
 97    "inspect",
 98    "json",
 99    "msgpack",
100    "numpy",
101    "se",
102    "toml",
103    "pickle",
104    "yaml",
105    "init",
106    "logger",
107    "ClassSerializer",
108    "ClassDeserializer",
109    "add_serializer",
110    "add_deserializer",
111]
112
113
114@overload
115def serde(
116    _cls: Type[T],
117    rename_all: str | None = None,
118    reuse_instances_default: bool = True,
119    convert_sets_default: bool = False,
120    transparent: bool = False,
121    serializer: SerializeFunc | None = None,
122    deserializer: DeserializeFunc | None = None,
123    tagging: Tagging = DefaultTagging,
124    type_check: TypeCheck = strict,
125    serialize_class_var: bool = False,
126    class_serializer: ClassSerializer | None = None,
127    class_deserializer: ClassDeserializer | None = None,
128    deny_unknown_fields: bool = False,
129) -> Type[T]: ...
130
131
132@overload
133def serde(
134    _cls: Any = None,
135    rename_all: str | None = None,
136    reuse_instances_default: bool = True,
137    convert_sets_default: bool = False,
138    transparent: bool = False,
139    serializer: SerializeFunc | None = None,
140    deserializer: DeserializeFunc | None = None,
141    tagging: Tagging = DefaultTagging,
142    type_check: TypeCheck = strict,
143    serialize_class_var: bool = False,
144    class_serializer: ClassSerializer | None = None,
145    class_deserializer: ClassDeserializer | None = None,
146    deny_unknown_fields: bool = False,
147) -> Callable[[type[T]], type[T]]: ...
148
149
150@dataclass_transform(field_specifiers=(field,))
151def serde(
152    _cls: Any = None,
153    rename_all: str | None = None,
154    reuse_instances_default: bool = True,
155    convert_sets_default: bool = False,
156    transparent: bool = False,
157    serializer: SerializeFunc | None = None,
158    deserializer: DeserializeFunc | None = None,
159    tagging: Tagging = DefaultTagging,
160    type_check: TypeCheck = strict,
161    serialize_class_var: bool = False,
162    class_serializer: ClassSerializer | None = None,
163    class_deserializer: ClassDeserializer | None = None,
164    deny_unknown_fields: bool = False,
165) -> Any:
166    """
167    serde decorator. Keyword arguments are passed in `serialize` and `deserialize`.
168    """
169
170    def wrap(cls: Any) -> Any:
171        if should_impl_dataclass(cls):
172            dataclass(cls)
173        serialize(
174            cls,
175            rename_all=rename_all,
176            reuse_instances_default=reuse_instances_default,
177            convert_sets_default=convert_sets_default,
178            transparent=transparent,
179            serializer=serializer,
180            deserializer=deserializer,
181            tagging=tagging,
182            type_check=type_check,
183            serialize_class_var=serialize_class_var,
184            class_serializer=class_serializer,
185        )
186        deserialize(
187            cls,
188            rename_all=rename_all,
189            reuse_instances_default=reuse_instances_default,
190            convert_sets_default=convert_sets_default,
191            transparent=transparent,
192            serializer=serializer,
193            deserializer=deserializer,
194            tagging=tagging,
195            type_check=type_check,
196            serialize_class_var=serialize_class_var,
197            class_deserializer=class_deserializer,
198            deny_unknown_fields=deny_unknown_fields,
199        )
200        return cls
201
202    if _cls is None:
203        return wrap
204
205    return wrap(_cls)
@dataclass_transform(field_specifiers=(field,))
def serde( _cls: Any = None, rename_all: str | None = None, reuse_instances_default: bool = True, convert_sets_default: bool = False, transparent: bool = False, serializer: Callable[[type[typing.Any], typing.Any], typing.Any] | None = None, deserializer: Callable[[type[typing.Any], typing.Any], typing.Any] | None = None, tagging: serde.core.Tagging = Tagging(tag=None, content=None, kind=<Kind.External: 1>), type_check: serde.core.TypeCheck = TypeCheck(kind=<Kind.Strict: 3>), serialize_class_var: bool = False, class_serializer: ClassSerializer | None = None, class_deserializer: ClassDeserializer | None = None, deny_unknown_fields: bool = False) -> Any:
151@dataclass_transform(field_specifiers=(field,))
152def serde(
153    _cls: Any = None,
154    rename_all: str | None = None,
155    reuse_instances_default: bool = True,
156    convert_sets_default: bool = False,
157    transparent: bool = False,
158    serializer: SerializeFunc | None = None,
159    deserializer: DeserializeFunc | None = None,
160    tagging: Tagging = DefaultTagging,
161    type_check: TypeCheck = strict,
162    serialize_class_var: bool = False,
163    class_serializer: ClassSerializer | None = None,
164    class_deserializer: ClassDeserializer | None = None,
165    deny_unknown_fields: bool = False,
166) -> Any:
167    """
168    serde decorator. Keyword arguments are passed in `serialize` and `deserialize`.
169    """
170
171    def wrap(cls: Any) -> Any:
172        if should_impl_dataclass(cls):
173            dataclass(cls)
174        serialize(
175            cls,
176            rename_all=rename_all,
177            reuse_instances_default=reuse_instances_default,
178            convert_sets_default=convert_sets_default,
179            transparent=transparent,
180            serializer=serializer,
181            deserializer=deserializer,
182            tagging=tagging,
183            type_check=type_check,
184            serialize_class_var=serialize_class_var,
185            class_serializer=class_serializer,
186        )
187        deserialize(
188            cls,
189            rename_all=rename_all,
190            reuse_instances_default=reuse_instances_default,
191            convert_sets_default=convert_sets_default,
192            transparent=transparent,
193            serializer=serializer,
194            deserializer=deserializer,
195            tagging=tagging,
196            type_check=type_check,
197            serialize_class_var=serialize_class_var,
198            class_deserializer=class_deserializer,
199            deny_unknown_fields=deny_unknown_fields,
200        )
201        return cls
202
203    if _cls is None:
204        return wrap
205
206    return wrap(_cls)

serde decorator. Keyword arguments are passed in serialize and deserialize.

@dataclass_transform()
def serialize( _cls: type[~T] | None = None, rename_all: str | None = None, reuse_instances_default: bool = False, convert_sets_default: bool = False, serializer: Callable[[type[typing.Any], typing.Any], typing.Any] | None = None, tagging: serde.core.Tagging = Tagging(tag=None, content=None, kind=<Kind.External: 1>), type_check: serde.core.TypeCheck = TypeCheck(kind=<Kind.Strict: 3>), serialize_class_var: bool = False, transparent: bool = False, class_serializer: ClassSerializer | None = None, **kwargs: Any) -> type[~T]:
196@dataclass_transform()
197def serialize(
198    _cls: type[T] | None = None,
199    rename_all: str | None = None,
200    reuse_instances_default: bool = False,
201    convert_sets_default: bool = False,
202    serializer: SerializeFunc | None = None,
203    tagging: Tagging = DefaultTagging,
204    type_check: TypeCheck = strict,
205    serialize_class_var: bool = False,
206    transparent: bool = False,
207    class_serializer: ClassSerializer | None = None,
208    **kwargs: Any,
209) -> type[T]:
210    """
211    A dataclass with this decorator is serializable into any of the data formats
212    supported by pyserde.
213
214    >>> from datetime import datetime
215    >>> from serde import serialize
216    >>> from serde.json import to_json
217    >>>
218    >>> @serialize
219    ... class Foo:
220    ...     i: int
221    ...     s: str
222    ...     f: float
223    ...     b: bool
224    >>>
225    >>> to_json(Foo(i=10, s='foo', f=100.0, b=True))
226    '{"i":10,"s":"foo","f":100.0,"b":true}'
227    """
228
229    def wrap(cls: type[T]) -> type[T]:
230        tagging.check()
231
232        # If no `dataclass` found in the class, dataclassify it automatically.
233        if not is_dataclass(cls):
234            dataclass(cls)
235
236        if transparent:
237            get_transparent_field(cls)
238
239        if type_check.is_strict():
240            serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError))
241            serde_beartype(cls)
242
243        g: dict[str, Any] = {}
244
245        # Create a scope storage used by serde.
246        # Each class should get own scope. Child classes can not share scope with parent class.
247        # That's why we need the "scope.cls is not cls" check.
248        scope: Scope | None = getattr(cls, SERDE_SCOPE, None)
249        if scope is None or scope.cls is not cls:
250            scope = Scope(
251                cls,
252                reuse_instances_default=reuse_instances_default,
253                convert_sets_default=convert_sets_default,
254            )
255            setattr(cls, SERDE_SCOPE, scope)
256        scope.transparent = transparent
257
258        class_serializers: list[ClassSerializer] = list(
259            itertools.chain(GLOBAL_CLASS_SERIALIZER, [class_serializer] if class_serializer else [])
260        )
261
262        # Set some globals for all generated functions
263        g["cls"] = cls
264        g["copy"] = copy
265        g["serde_scope"] = scope
266        g["SerdeError"] = SerdeError
267        g["raise_unsupported_type"] = raise_unsupported_type
268        g["enum_value"] = enum_value
269        g["is_dataclass"] = is_dataclass
270        g["typename"] = typename  # used in union functions
271        g["is_instance"] = is_instance  # used in union functions
272        g["to_obj"] = to_obj
273        g["typing"] = typing
274        g["Literal"] = Literal
275        g["TypeCheck"] = TypeCheck
276        g["disabled"] = disabled
277        g["coerce_object"] = coerce_object
278        g["class_serializers"] = class_serializers
279        if serializer:
280            g["serde_legacy_custom_class_serializer"] = functools.partial(
281                serde_legacy_custom_class_serializer, custom=serializer
282            )
283
284        # Collect types used in the generated code.
285        for typ in iter_types(cls):
286            # When we encounter a dataclass not marked with serialize, then also generate serialize
287            # functions for it.
288            if is_dataclass_without_se(typ) and typ is not cls:
289                # We call serialize and not wrap to make sure that we will use the default serde
290                # configuration for generating the serialization function.
291                serialize(typ)
292
293            if is_primitive(typ) and not is_enum(typ):
294                continue
295            g[typename(typ)] = typ
296
297        # render all union functions
298        for union in iter_unions(cls):
299            union_args = list(type_args(union))
300            union_key = union_func_name(UNION_SE_PREFIX, union_args)
301            add_func(scope, union_key, render_union_func(cls, union_args, tagging), g)
302            scope.union_se_args[union_key] = union_args
303
304        for f in sefields(cls, serialize_class_var):
305            if f.skip_if:
306                g[f.skip_if.name] = f.skip_if
307            if f.serializer:
308                g[f.serializer.name] = f.serializer
309
310        add_func(
311            scope,
312            TO_ITER,
313            render_to_tuple(cls, serializer, type_check, serialize_class_var, class_serializer),
314            g,
315        )
316        add_func(
317            scope,
318            TO_DICT,
319            render_to_dict(
320                cls, rename_all, serializer, type_check, serialize_class_var, class_serializer
321            ),
322            g,
323        )
324
325        logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}")
326
327        return cls
328
329    if _cls is None:
330        return wrap  # type: ignore
331
332    if _cls in GENERATION_STACK:
333        return _cls
334
335    GENERATION_STACK.append(_cls)
336    try:
337        return wrap(_cls)
338    finally:
339        GENERATION_STACK.pop()

A dataclass with this decorator is serializable into any of the data formats supported by pyserde.

>>> from datetime import datetime
>>> from serde import serialize
>>> from serde.json import to_json
>>>
>>> @serialize
... class Foo:
...     i: int
...     s: str
...     f: float
...     b: bool
>>>
>>> to_json(Foo(i=10, s='foo', f=100.0, b=True))
'{"i":10,"s":"foo","f":100.0,"b":true}'
@dataclass_transform()
def deserialize( _cls: type[~T] | None = None, rename_all: str | None = None, reuse_instances_default: bool = True, convert_sets_default: bool = False, deserializer: Callable[[type[typing.Any], typing.Any], typing.Any] | None = None, tagging: serde.core.Tagging = Tagging(tag=None, content=None, kind=<Kind.External: 1>), type_check: serde.core.TypeCheck = TypeCheck(kind=<Kind.Strict: 3>), class_deserializer: ClassDeserializer | None = None, deny_unknown_fields: bool = False, transparent: bool = False, **kwargs: Any) -> type[~T]:
205@dataclass_transform()
206def deserialize(
207    _cls: type[T] | None = None,
208    rename_all: str | None = None,
209    reuse_instances_default: bool = True,
210    convert_sets_default: bool = False,
211    deserializer: DeserializeFunc | None = None,
212    tagging: Tagging = DefaultTagging,
213    type_check: TypeCheck = strict,
214    class_deserializer: ClassDeserializer | None = None,
215    deny_unknown_fields: bool = False,
216    transparent: bool = False,
217    **kwargs: Any,
218) -> type[T]:
219    """
220    A dataclass with this decorator is deserializable from any of the data formats supported
221    by pyserde.
222
223    >>> from serde import deserialize
224    >>> from serde.json import from_json
225    >>>
226    >>> @deserialize
227    ... class Foo:
228    ...     i: int
229    ...     s: str
230    ...     f: float
231    ...     b: bool
232    >>>
233    >>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}')
234    Foo(i=10, s='foo', f=100.0, b=True)
235    """
236
237    stack = []
238
239    def wrap(cls: type[T]) -> type[T]:
240        if cls in stack:
241            return cls
242        stack.append(cls)
243
244        tagging.check()
245
246        # If no `dataclass` found in the class, dataclassify it automatically.
247        if not is_dataclass(cls):
248            dataclass(cls)
249
250        if transparent:
251            get_transparent_field(cls)
252
253        if type_check.is_strict():
254            serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError))
255            serde_beartype(cls)
256
257        g: dict[str, Any] = {}
258
259        # Create a scope storage used by serde.
260        # Each class should get own scope. Child classes can not share scope with parent class.
261        # That's why we need the "scope.cls is not cls" check.
262        scope: Scope | None = getattr(cls, SERDE_SCOPE, None)
263        if scope is None or scope.cls is not cls:
264            scope = Scope(
265                cls,
266                reuse_instances_default=reuse_instances_default,
267                convert_sets_default=convert_sets_default,
268            )
269            setattr(cls, SERDE_SCOPE, scope)
270        scope.transparent = transparent
271
272        class_deserializers: list[ClassDeserializer] = list(
273            itertools.chain(
274                GLOBAL_CLASS_DESERIALIZER, [class_deserializer] if class_deserializer else []
275            )
276        )
277
278        # Set some globals for all generated functions
279        g["cls"] = cls
280        g["serde_scope"] = scope
281        g["SerdeError"] = SerdeError
282        g["UserError"] = UserError
283        g["raise_unsupported_type"] = raise_unsupported_type
284        g["typename"] = typename
285        g["ensure"] = ensure
286        g["typing"] = typing
287        g["collections"] = collections
288        g["Literal"] = Literal
289        g["from_obj"] = from_obj
290        g["get_generic_arg"] = get_generic_arg
291        g["is_instance"] = is_instance
292        g["TypeCheck"] = TypeCheck
293        g["disabled"] = disabled
294        g["coerce_object"] = coerce_object
295        g["_exists_by_aliases"] = _exists_by_aliases
296        g["_get_by_aliases"] = _get_by_aliases
297        g["class_deserializers"] = class_deserializers
298        g["BeartypeCallHintParamViolation"] = BeartypeCallHintParamViolation
299        g["is_bearable"] = is_bearable
300        if deserializer:
301            g["serde_legacy_custom_class_deserializer"] = functools.partial(
302                serde_legacy_custom_class_deserializer, custom=deserializer
303            )
304
305        # Collect types used in the generated code.
306        for typ in iter_types(cls):
307            # When we encounter a dataclass not marked with deserialize, then also generate
308            # deserialize functions for it.
309            if is_dataclass_without_de(typ) and typ is not cls:
310                # We call deserialize and not wrap to make sure that we will use the default serde
311                # configuration for generating the deserialization function.
312                deserialize(typ)
313
314            # We don't want to add primitive class e.g "str" into the scope, but primitive
315            # compatible types such as IntEnum and a subclass of primitives are added,
316            # so that generated code can use those types.
317            if is_primitive(typ) and not is_enum(typ) and not is_primitive_subclass(typ):
318                continue
319
320            if is_generic(typ):
321                g[typename(typ)] = get_origin(typ)
322            else:
323                g[typename(typ)] = typ
324
325        # render all union functions
326        for union in iter_unions(cls):
327            union_args = type_args(union)
328            add_func(
329                scope,
330                union_func_name(UNION_DE_PREFIX, union_args),
331                render_union_func(cls, union_args, tagging),
332                g,
333            )
334
335        # render literal functions
336        for literal in iter_literals(cls):
337            literal_args = type_args(literal)
338            add_func(
339                scope, literal_func_name(literal_args), render_literal_func(cls, literal_args), g
340            )
341
342        # Collect default values and default factories used in the generated code.
343        for f in defields(cls):
344            assert f.name
345            if has_default(f):
346                scope.defaults[f.name] = f.default
347            elif has_default_factory(f):
348                scope.defaults[f.name] = f.default_factory
349            if f.deserializer:
350                g[f.deserializer.name] = f.deserializer
351
352        add_func(
353            scope,
354            FROM_ITER,
355            render_from_iter(cls, deserializer, type_check, class_deserializer=class_deserializer),
356            g,
357        )
358        add_func(
359            scope,
360            FROM_DICT,
361            render_from_dict(
362                cls,
363                rename_all,
364                deserializer,
365                type_check,
366                class_deserializer=class_deserializer,
367                deny_unknown_fields=deny_unknown_fields,
368            ),
369            g,
370        )
371
372        logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}")
373
374        stack.pop()
375        return cls
376
377    if _cls is None:
378        return wrap  # type: ignore
379
380    if _cls in GENERATION_STACK:
381        return _cls
382
383    GENERATION_STACK.append(_cls)
384    try:
385        return wrap(_cls)
386    finally:
387        GENERATION_STACK.pop()

A dataclass with this decorator is deserializable from any of the data formats supported by pyserde.

>>> from serde import deserialize
>>> from serde.json import from_json
>>>
>>> @deserialize
... class Foo:
...     i: int
...     s: str
...     f: float
...     b: bool
>>>
>>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}')
Foo(i=10, s='foo', f=100.0, b=True)
def is_serializable(instance_or_class: Any) -> bool:
342def is_serializable(instance_or_class: Any) -> bool:
343    """
344    Test if an instance or class is serializable.
345
346    >>> @serialize
347    ... class Foo:
348    ...     pass
349
350    Testing `Foo` class object returns `True`.
351    >>> is_serializable(Foo)
352    True
353
354    Testing `Foo` object also returns `True`.
355    >>> is_serializable(Foo())
356    True
357    """
358    return hasattr(instance_or_class, SERDE_SCOPE)

Test if an instance or class is serializable.

>>> @serialize
... class Foo:
...     pass

Testing Foo class object returns True.

>>> is_serializable(Foo)
True

Testing Foo object also returns True.

>>> is_serializable(Foo())
True
def is_deserializable(instance_or_class: Any) -> bool:
390def is_deserializable(instance_or_class: Any) -> bool:
391    """
392    Test if an instance or class is deserializable.
393
394    >>> @deserialize
395    ... class Foo:
396    ...     pass
397    >>>
398    >>> is_deserializable(Foo)
399    True
400    """
401    return hasattr(instance_or_class, SERDE_SCOPE)

Test if an instance or class is deserializable.

>>> @deserialize
... class Foo:
...     pass
>>>
>>> is_deserializable(Foo)
True
def to_dict( o: Any, c: type[typing.Any] | None = None, reuse_instances: bool | None = None, convert_sets: bool | None = None, skip_none: bool = False) -> dict[typing.Any, typing.Any]:
493def to_dict(
494    o: Any,
495    c: type[Any] | None = None,
496    reuse_instances: bool | None = None,
497    convert_sets: bool | None = None,
498    skip_none: bool = False,
499) -> dict[Any, Any]:
500    """
501    Serialize object into python dictionary. This function ensures that the dataclass's fields are
502    accurately represented as key-value pairs in the resulting dictionary.
503
504    * `o`: Any pyserde object that you want to convert to `dict`
505    * `c`: Optional class argument
506    * `reuse_instances`: pyserde will pass instances (e.g. Path, datetime) directly to serializer
507    instead of converting them to serializable representation e.g. string. This behaviour allows
508    to delegate serializtation to underlying data format packages e.g. `pyyaml` and potentially
509    improve performance.
510    * `convert_sets`: This option controls how sets are handled during serialization and
511    deserialization. When `convert_sets` is set to True, pyserde will convert sets to lists during
512    serialization and back to sets during deserialization. This is useful for data formats that
513    do not natively support sets.
514    * `skip_none`: When set to True, any field in the class with a None value is excluded from the
515    serialized output. Defaults to False.
516
517    >>> from serde import serde
518    >>> @serde
519    ... class Foo:
520    ...     i: int
521    ...     s: str = 'foo'
522    ...     f: float = 100.0
523    ...     b: bool = True
524    >>>
525    >>> to_dict(Foo(i=10))
526    {'i': 10, 's': 'foo', 'f': 100.0, 'b': True}
527
528    You can serialize not only pyserde objects but also objects of any supported types. For example,
529    the following example serializes list of pyserde objects into dict.
530
531    >>> lst = [Foo(i=10), Foo(i=20)]
532    >>> to_dict(lst)
533    [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
534    """
535    return to_obj(  # type: ignore
536        o,
537        named=True,
538        c=c,
539        reuse_instances=reuse_instances,
540        convert_sets=convert_sets,
541        skip_none=skip_none,
542    )

Serialize object into python dictionary. This function ensures that the dataclass's fields are accurately represented as key-value pairs in the resulting dictionary.

  • o: Any pyserde object that you want to convert to dict
  • c: Optional class argument
  • reuse_instances: pyserde will pass instances (e.g. Path, datetime) directly to serializer instead of converting them to serializable representation e.g. string. This behaviour allows to delegate serializtation to underlying data format packages e.g. pyyaml and potentially improve performance.
  • convert_sets: This option controls how sets are handled during serialization and deserialization. When convert_sets is set to True, pyserde will convert sets to lists during serialization and back to sets during deserialization. This is useful for data formats that do not natively support sets.
  • skip_none: When set to True, any field in the class with a None value is excluded from the serialized output. Defaults to False.
>>> from serde import serde
>>> @serde
... class Foo:
...     i: int
...     s: str = 'foo'
...     f: float = 100.0
...     b: bool = True
>>>
>>> to_dict(Foo(i=10))
{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}

You can serialize not only pyserde objects but also objects of any supported types. For example, the following example serializes list of pyserde objects into dict.

>>> lst = [Foo(i=10), Foo(i=20)]
>>> to_dict(lst)
[{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
def from_dict( cls: Any, o: dict[str, typing.Any], reuse_instances: bool | None = None, deserialize_numbers: Callable[[str | int], float] | None = None) -> Any:
590def from_dict(
591    cls: Any,
592    o: dict[str, Any],
593    reuse_instances: bool | None = None,
594    deserialize_numbers: Callable[[str | int], float] | None = None,
595) -> Any:
596    """
597    Deserialize dictionary into object.
598
599    >>> @deserialize
600    ... class Foo:
601    ...     i: int
602    ...     s: str = 'foo'
603    ...     f: float = 100.0
604    ...     b: bool = True
605    >>>
606    >>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True})
607    Foo(i=10, s='foo', f=100.0, b=True)
608
609    You can pass any type supported by pyserde. For example,
610
611    * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target
612      type is float (e.g. accept ints or numeric strings supplied by a parser).
613
614    >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
615    ...        {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
616    >>> from_dict(list[Foo], lst)
617    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
618    """
619    return from_obj(
620        cls,
621        o,
622        named=True,
623        reuse_instances=reuse_instances,
624        deserialize_numbers=deserialize_numbers,
625    )

Deserialize dictionary into object.

>>> @deserialize
... class Foo:
...     i: int
...     s: str = 'foo'
...     f: float = 100.0
...     b: bool = True
>>>
>>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True})
Foo(i=10, s='foo', f=100.0, b=True)

You can pass any type supported by pyserde. For example,

  • deserialize_numbers: Optional callable to coerce numeric input to floats when the target type is float (e.g. accept ints or numeric strings supplied by a parser).
>>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
...        {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
>>> from_dict(list[Foo], lst)
[Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
def to_tuple( o: Any, c: type[typing.Any] | None = None, reuse_instances: bool | None = None, convert_sets: bool | None = None, skip_none: bool = False) -> tuple[typing.Any, ...]:
450def to_tuple(
451    o: Any,
452    c: type[Any] | None = None,
453    reuse_instances: bool | None = None,
454    convert_sets: bool | None = None,
455    skip_none: bool = False,
456) -> tuple[Any, ...]:
457    """
458    Serialize object into tuple.
459
460    >>> @serialize
461    ... class Foo:
462    ...     i: int
463    ...     s: str = 'foo'
464    ...     f: float = 100.0
465    ...     b: bool = True
466    >>>
467    >>> to_tuple(Foo(i=10))
468    (10, 'foo', 100.0, True)
469
470    You can pass any type supported by pyserde. For example,
471
472    >>> lst = [Foo(i=10), Foo(i=20)]
473    >>> to_tuple(lst)
474    [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
475    """
476    return to_obj(  # type: ignore
477        o,
478        named=False,
479        c=c,
480        reuse_instances=reuse_instances,
481        convert_sets=convert_sets,
482        skip_none=skip_none,
483    )

Serialize object into tuple.

>>> @serialize
... class Foo:
...     i: int
...     s: str = 'foo'
...     f: float = 100.0
...     b: bool = True
>>>
>>> to_tuple(Foo(i=10))
(10, 'foo', 100.0, True)

You can pass any type supported by pyserde. For example,

>>> lst = [Foo(i=10), Foo(i=20)]
>>> to_tuple(lst)
[(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
def from_tuple( cls: Any, o: Any, reuse_instances: bool | None = None, deserialize_numbers: Callable[[str | int], float] | None = None) -> Any:
646def from_tuple(
647    cls: Any,
648    o: Any,
649    reuse_instances: bool | None = None,
650    deserialize_numbers: Callable[[str | int], float] | None = None,
651) -> Any:
652    """
653    Deserialize tuple into object.
654
655    >>> @deserialize
656    ... class Foo:
657    ...     i: int
658    ...     s: str = 'foo'
659    ...     f: float = 100.0
660    ...     b: bool = True
661    >>>
662    >>> from_tuple(Foo, (10, 'foo', 100.0, True))
663    Foo(i=10, s='foo', f=100.0, b=True)
664
665    You can pass any type supported by pyserde. For example,
666
667    * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target
668      type is float (e.g. accept ints or numeric strings supplied by a parser).
669
670    >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
671    >>> from_tuple(list[Foo], lst)
672    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
673    """
674    return from_obj(
675        cls,
676        o,
677        named=False,
678        reuse_instances=reuse_instances,
679        deserialize_numbers=deserialize_numbers,
680    )

Deserialize tuple into object.

>>> @deserialize
... class Foo:
...     i: int
...     s: str = 'foo'
...     f: float = 100.0
...     b: bool = True
>>>
>>> from_tuple(Foo, (10, 'foo', 100.0, True))
Foo(i=10, s='foo', f=100.0, b=True)

You can pass any type supported by pyserde. For example,

  • deserialize_numbers: Optional callable to coerce numeric input to floats when the target type is float (e.g. accept ints or numeric strings supplied by a parser).
>>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
>>> from_tuple(list[Foo], lst)
[Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
class SerdeError(builtins.Exception):
93class SerdeError(Exception):
94    """
95    Serde error class.
96    """

Serde error class.

class SerdeSkip(builtins.Exception):
108class SerdeSkip(Exception):
109    """
110    Skip a field in custom (de)serializer.
111    """

Skip a field in custom (de)serializer.

def AdjacentTagging( tag: str, content: str, cls: Optional[~T] = None) -> Union[serde.core.Tagging, serde.compat._WithTagging[~T]]:
938def AdjacentTagging(tag: str, content: str, cls: T | None = None) -> Tagging | _WithTagging[T]:
939    tagging = Tagging(tag, content, kind=Tagging.Kind.Adjacent)
940    if cls:
941        return tagging(cls)
942    else:
943        return tagging
ExternalTagging = Tagging(tag=None, content=None, kind=<Kind.External: 1>)
def InternalTagging( tag: str, cls: Optional[~T] = None) -> Union[serde.core.Tagging, serde.compat._WithTagging[~T]]:
922def InternalTagging(tag: str, cls: T | None = None) -> Tagging | _WithTagging[T]:
923    tagging = Tagging(tag, kind=Tagging.Kind.Internal)
924    if cls:
925        return tagging(cls)
926    else:
927        return tagging
Untagged = Tagging(tag=None, content=None, kind=<Kind.Untagged: 4>)
disabled = TypeCheck(kind=<Kind.Disabled: 1>)
strict = TypeCheck(kind=<Kind.Strict: 3>)
coerce = TypeCheck(kind=<Kind.Coerce: 2>)
def field( *args: Any, rename: str | None = None, alias: list[str] | None = None, skip: bool | None = None, skip_if: Callable[[typing.Any], typing.Any] | None = None, skip_if_false: bool | None = None, skip_if_default: bool | None = None, serializer: Callable[..., typing.Any] | None = None, deserializer: Callable[..., typing.Any] | None = None, flatten: serde.core.FlattenOpts | bool | None = None, metadata: dict[str, typing.Any] | None = None, **kwargs: Any) -> Any:
566def field(
567    *args: Any,
568    rename: str | None = None,
569    alias: list[str] | None = None,
570    skip: bool | None = None,
571    skip_if: Callable[[Any], Any] | None = None,
572    skip_if_false: bool | None = None,
573    skip_if_default: bool | None = None,
574    serializer: Callable[..., Any] | None = None,
575    deserializer: Callable[..., Any] | None = None,
576    flatten: FlattenOpts | bool | None = None,
577    metadata: dict[str, Any] | None = None,
578    **kwargs: Any,
579) -> Any:
580    """
581    Declare a field with parameters.
582    """
583    if not metadata:
584        metadata = {}
585
586    if rename is not None:
587        metadata["serde_rename"] = rename
588    if alias is not None:
589        metadata["serde_alias"] = alias
590    if skip is not None:
591        metadata["serde_skip"] = skip
592    if skip_if is not None:
593        metadata["serde_skip_if"] = skip_if
594    if skip_if_false is not None:
595        metadata["serde_skip_if_false"] = skip_if_false
596    if skip_if_default is not None:
597        metadata["serde_skip_if_default"] = skip_if_default
598    if serializer:
599        metadata["serde_serializer"] = serializer
600    if deserializer:
601        metadata["serde_deserializer"] = deserializer
602    if flatten is True:
603        metadata["serde_flatten"] = FlattenOpts()
604    elif flatten:
605        metadata["serde_flatten"] = flatten
606
607    return dataclasses.field(*args, metadata=metadata, **kwargs)

Declare a field with parameters.

def default_deserializer(_cls: type[typing.Any], obj: Any) -> Any:
144def default_deserializer(_cls: type[Any], obj: Any) -> Any:
145    """
146    Marker function to tell serde to use the default deserializer. It's used when custom
147    deserializer is specified at the class but you want to override a field with the default
148    deserializer.
149    """

Marker function to tell serde to use the default deserializer. It's used when custom deserializer is specified at the class but you want to override a field with the default deserializer.

def asdict(v: Any) -> dict[typing.Any, typing.Any]:
486def asdict(v: Any) -> dict[Any, Any]:
487    """
488    Serialize object into dictionary.
489    """
490    return to_dict(v, reuse_instances=False, convert_sets=False)

Serialize object into dictionary.

def astuple(v: Any) -> tuple[typing.Any, ...]:
443def astuple(v: Any) -> tuple[Any, ...]:
444    """
445    Serialize object into tuple.
446    """
447    return to_tuple(v, reuse_instances=False, convert_sets=False)

Serialize object into tuple.

def default_serializer(_cls: type[typing.Any], obj: Any) -> Any:
127def default_serializer(_cls: type[Any], obj: Any) -> Any:
128    """
129    Marker function to tell serde to use the default serializer. It's used when custom serializer
130    is specified at the class but you want to override a field with the default serializer.
131    """

Marker function to tell serde to use the default serializer. It's used when custom serializer is specified at the class but you want to override a field with the default serializer.

def init(debug: bool = False) -> None:
 99def init(debug: bool = False) -> None:
100    SETTINGS["debug"] = debug
logger = <Logger serde (WARNING)>
class ClassSerializer(typing.Protocol):
1085class ClassSerializer(Protocol):
1086    """
1087    Interface for custom class serializer.
1088
1089    This protocol is intended to be used for custom class serializer.
1090
1091    >>> from datetime import datetime
1092    >>> from serde import serde
1093    >>> from plum import dispatch
1094    >>> class MySerializer(ClassSerializer):
1095    ...     @dispatch
1096    ...     def serialize(self, value: datetime) -> str:
1097    ...         return value.strftime("%d/%m/%y")
1098    """
1099
1100    def serialize(self, value: Any) -> Any:
1101        pass

Interface for custom class serializer.

This protocol is intended to be used for custom class serializer.

>>> from datetime import datetime
>>> from serde import serde
>>> from plum import dispatch
>>> class MySerializer(ClassSerializer):
...     @dispatch
...     def serialize(self, value: datetime) -> str:
...         return value.strftime("%d/%m/%y")
ClassSerializer(*args, **kwargs)
1771def _no_init_or_replace_init(self, *args, **kwargs):
1772    cls = type(self)
1773
1774    if cls._is_protocol:
1775        raise TypeError('Protocols cannot be instantiated')
1776
1777    # Already using a custom `__init__`. No need to calculate correct
1778    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1779    if cls.__init__ is not _no_init_or_replace_init:
1780        return
1781
1782    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1783    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1784    # searches for a proper new `__init__` in the MRO. The new `__init__`
1785    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1786    # instantiation of the protocol subclass will thus use the new
1787    # `__init__` and no longer call `_no_init_or_replace_init`.
1788    for base in cls.__mro__:
1789        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1790        if init is not _no_init_or_replace_init:
1791            cls.__init__ = init
1792            break
1793    else:
1794        # should not happen
1795        cls.__init__ = object.__init__
1796
1797    cls.__init__(self, *args, **kwargs)
def serialize(self, value: Any) -> Any:
1100    def serialize(self, value: Any) -> Any:
1101        pass
class ClassDeserializer(typing.Protocol):
1104class ClassDeserializer(Protocol):
1105    """
1106    Interface for custom class deserializer.
1107
1108    This protocol is intended to be used for custom class deserializer.
1109
1110    >>> from datetime import datetime
1111    >>> from serde import serde
1112    >>> from plum import dispatch
1113    >>> class MyDeserializer(ClassDeserializer):
1114    ...     @dispatch
1115    ...     def deserialize(self, cls: type[datetime], value: Any) -> datetime:
1116    ...         return datetime.strptime(value, "%d/%m/%y")
1117    """
1118
1119    def deserialize(self, cls: Any, value: Any) -> Any:
1120        pass

Interface for custom class deserializer.

This protocol is intended to be used for custom class deserializer.

>>> from datetime import datetime
>>> from serde import serde
>>> from plum import dispatch
>>> class MyDeserializer(ClassDeserializer):
...     @dispatch
...     def deserialize(self, cls: type[datetime], value: Any) -> datetime:
...         return datetime.strptime(value, "%d/%m/%y")
ClassDeserializer(*args, **kwargs)
1771def _no_init_or_replace_init(self, *args, **kwargs):
1772    cls = type(self)
1773
1774    if cls._is_protocol:
1775        raise TypeError('Protocols cannot be instantiated')
1776
1777    # Already using a custom `__init__`. No need to calculate correct
1778    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1779    if cls.__init__ is not _no_init_or_replace_init:
1780        return
1781
1782    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1783    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1784    # searches for a proper new `__init__` in the MRO. The new `__init__`
1785    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1786    # instantiation of the protocol subclass will thus use the new
1787    # `__init__` and no longer call `_no_init_or_replace_init`.
1788    for base in cls.__mro__:
1789        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1790        if init is not _no_init_or_replace_init:
1791            cls.__init__ = init
1792            break
1793    else:
1794        # should not happen
1795        cls.__init__ = object.__init__
1796
1797    cls.__init__(self, *args, **kwargs)
def deserialize(self, cls: Any, value: Any) -> Any:
1119    def deserialize(self, cls: Any, value: Any) -> Any:
1120        pass
def add_serializer(serializer: ClassSerializer) -> None:
1128def add_serializer(serializer: ClassSerializer) -> None:
1129    """
1130    Register custom global serializer.
1131    """
1132    GLOBAL_CLASS_SERIALIZER.append(serializer)

Register custom global serializer.

def add_deserializer(deserializer: ClassDeserializer) -> None:
1135def add_deserializer(deserializer: ClassDeserializer) -> None:
1136    """
1137    Register custom global deserializer.
1138    """
1139    GLOBAL_CLASS_DESERIALIZER.append(deserializer)

Register custom global deserializer.