Edit on GitHub

serde.de

This module provides deserialize, is_deserializable from_dict, from_tuple and classes and functions associated with deserialization.

   1"""
   2This module provides `deserialize`, `is_deserializable` `from_dict`, `from_tuple` and classes
   3and functions associated with deserialization.
   4"""
   5
   6from __future__ import annotations
   7import abc
   8import itertools
   9import collections
  10import dataclasses
  11import functools
  12import typing
  13import jinja2
  14from collections.abc import Callable, Sequence, Iterable
  15
  16from beartype import beartype, BeartypeConf
  17from beartype.door import is_bearable
  18from beartype.roar import BeartypeCallHintParamViolation
  19from dataclasses import dataclass, is_dataclass
  20from typing import overload, TypeVar, Generic, Any, Optional, Union, Literal, Iterator, cast
  21from typing_extensions import dataclass_transform
  22
  23from .compat import (
  24    SerdeError,
  25    SerdeSkip,
  26    T,
  27    UserError,
  28    find_generic_arg,
  29    get_args,
  30    get_generic_arg,
  31    get_origin,
  32    get_type_var_names,
  33    is_any,
  34    is_bare_dict,
  35    is_bare_list,
  36    is_bare_set,
  37    is_bare_tuple,
  38    is_datetime,
  39    is_default_dict,
  40    is_dict,
  41    is_ellipsis,
  42    is_enum,
  43    is_frozen_set,
  44    is_generic,
  45    is_list,
  46    is_literal,
  47    is_none,
  48    is_opt,
  49    is_primitive,
  50    is_primitive_subclass,
  51    is_set,
  52    is_str_serializable,
  53    is_tuple,
  54    is_union,
  55    is_variable_tuple,
  56    is_pep695_type_alias,
  57    iter_literals,
  58    iter_types,
  59    iter_unions,
  60    type_args,
  61    typename,
  62)
  63from .core import (
  64    GLOBAL_CLASS_DESERIALIZER,
  65    ClassDeserializer,
  66    FROM_DICT,
  67    FROM_ITER,
  68    SERDE_SCOPE,
  69    CACHE,
  70    UNION_DE_PREFIX,
  71    DefaultTagging,
  72    Field,
  73    disabled,
  74    Scope,
  75    Tagging,
  76    TypeCheck,
  77    add_func,
  78    coerce_object,
  79    strict,
  80    has_default,
  81    has_default_factory,
  82    ensure,
  83    fields,
  84    is_instance,
  85    literal_func_name,
  86    logger,
  87    raise_unsupported_type,
  88    union_func_name,
  89)
  90
  91# Lazy numpy imports to improve startup time
  92
  93__all__ = ["deserialize", "is_deserializable", "from_dict", "from_tuple"]
  94
  95
  96# Lazy numpy import wrappers to improve startup time
  97def _is_numpy_array(typ: Any) -> bool:
  98    from .numpy import is_numpy_array
  99
 100    return is_numpy_array(typ)
 101
 102
 103def _is_numpy_scalar(typ: Any) -> bool:
 104    from .numpy import is_numpy_scalar
 105
 106    return is_numpy_scalar(typ)
 107
 108
 109def _is_numpy_jaxtyping(typ: Any) -> bool:
 110    from .numpy import is_numpy_jaxtyping
 111
 112    return is_numpy_jaxtyping(typ)
 113
 114
 115DeserializeFunc = Callable[[type[Any], Any], Any]
 116""" Interface of Custom deserialize function. """
 117
 118
 119def serde_legacy_custom_class_deserializer(
 120    cls: type[Any], datavar: Any, value: Any, custom: DeserializeFunc, default: Callable[[], Any]
 121) -> Any:
 122    """
 123    Handle custom deserialization. Use default deserialization logic if it receives `SerdeSkip`
 124    exception.
 125
 126    :param cls: Type of the field.
 127    :param datavar: The whole variable to deserialize from. e.g. "data"
 128    :param value: The value for the field. e.g. "data['i']"
 129    :param custom: Custom deserialize function.
 130    :param default: Default deserialize function.
 131    """
 132    try:
 133        return custom(cls, value)
 134    except SerdeSkip:
 135        return default()
 136
 137
 138def default_deserializer(_cls: type[Any], obj: Any) -> Any:
 139    """
 140    Marker function to tell serde to use the default deserializer. It's used when custom
 141    deserializer is specified at the class but you want to override a field with the default
 142    deserializer.
 143    """
 144
 145
 146def _get_by_aliases(
 147    d: dict[str, str], aliases: list[str], raise_error: bool = True
 148) -> Optional[str]:
 149    if not aliases:
 150        if raise_error:
 151            raise KeyError("Tried all aliases, but key not found")
 152        else:
 153            return None
 154    if aliases[0] in d:
 155        return d[aliases[0]]
 156    else:
 157        return _get_by_aliases(d, aliases[1:], raise_error=raise_error)
 158
 159
 160def _exists_by_aliases(d: dict[str, str], aliases: list[str]) -> bool:
 161    for alias in aliases:
 162        if alias in d:
 163            return True
 164    return False
 165
 166
 167def _make_deserialize(
 168    cls_name: str,
 169    fields: list[Any],
 170    *args: Any,
 171    rename_all: Optional[str] = None,
 172    reuse_instances_default: bool = True,
 173    convert_sets_default: bool = False,
 174    deserializer: Optional[DeserializeFunc] = None,
 175    type_check: TypeCheck = strict,
 176    class_deserializer: Optional[ClassDeserializer] = None,
 177    **kwargs: Any,
 178) -> type[Any]:
 179    """
 180    Create a deserializable class programatically.
 181    """
 182    C: type[Any] = dataclasses.make_dataclass(cls_name, fields, *args, **kwargs)
 183    C = deserialize(
 184        C,
 185        rename_all=rename_all,
 186        reuse_instances_default=reuse_instances_default,
 187        convert_sets_default=convert_sets_default,
 188        **kwargs,
 189    )
 190    return C
 191
 192
 193# The `deserialize` function can call itself recursively when it needs to generate code for
 194# unmarked dataclasses. To avoid infinite recursion, this array remembers types for which code is
 195# currently being generated.
 196GENERATION_STACK = []
 197
 198
 199@dataclass_transform()
 200def deserialize(
 201    _cls: Optional[type[T]] = None,
 202    rename_all: Optional[str] = None,
 203    reuse_instances_default: bool = True,
 204    convert_sets_default: bool = False,
 205    deserializer: Optional[DeserializeFunc] = None,
 206    tagging: Tagging = DefaultTagging,
 207    type_check: TypeCheck = strict,
 208    class_deserializer: Optional[ClassDeserializer] = None,
 209    deny_unknown_fields: bool = False,
 210    **kwargs: Any,
 211) -> type[T]:
 212    """
 213    A dataclass with this decorator is deserializable from any of the data formats supported
 214    by pyserde.
 215
 216    >>> from serde import deserialize
 217    >>> from serde.json import from_json
 218    >>>
 219    >>> @deserialize
 220    ... class Foo:
 221    ...     i: int
 222    ...     s: str
 223    ...     f: float
 224    ...     b: bool
 225    >>>
 226    >>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}')
 227    Foo(i=10, s='foo', f=100.0, b=True)
 228    """
 229
 230    stack = []
 231
 232    def wrap(cls: type[T]) -> type[T]:
 233        if cls in stack:
 234            return cls
 235        stack.append(cls)
 236
 237        tagging.check()
 238
 239        # If no `dataclass` found in the class, dataclassify it automatically.
 240        if not is_dataclass(cls):
 241            dataclass(cls)
 242
 243        if type_check.is_strict():
 244            serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError))
 245            serde_beartype(cls)
 246
 247        g: dict[str, Any] = {}
 248
 249        # Create a scope storage used by serde.
 250        # Each class should get own scope. Child classes can not share scope with parent class.
 251        # That's why we need the "scope.cls is not cls" check.
 252        scope: Optional[Scope] = getattr(cls, SERDE_SCOPE, None)
 253        if scope is None or scope.cls is not cls:
 254            scope = Scope(cls, reuse_instances_default=reuse_instances_default)
 255            setattr(cls, SERDE_SCOPE, scope)
 256
 257        class_deserializers: list[ClassDeserializer] = list(
 258            itertools.chain(
 259                GLOBAL_CLASS_DESERIALIZER, [class_deserializer] if class_deserializer else []
 260            )
 261        )
 262
 263        # Set some globals for all generated functions
 264        g["cls"] = cls
 265        g["serde_scope"] = scope
 266        g["SerdeError"] = SerdeError
 267        g["UserError"] = UserError
 268        g["raise_unsupported_type"] = raise_unsupported_type
 269        g["typename"] = typename
 270        g["ensure"] = ensure
 271        g["typing"] = typing
 272        g["collections"] = collections
 273        g["Literal"] = Literal
 274        g["from_obj"] = from_obj
 275        g["get_generic_arg"] = get_generic_arg
 276        g["is_instance"] = is_instance
 277        g["TypeCheck"] = TypeCheck
 278        g["disabled"] = disabled
 279        g["coerce_object"] = coerce_object
 280        g["_exists_by_aliases"] = _exists_by_aliases
 281        g["_get_by_aliases"] = _get_by_aliases
 282        g["class_deserializers"] = class_deserializers
 283        g["BeartypeCallHintParamViolation"] = BeartypeCallHintParamViolation
 284        g["is_bearable"] = is_bearable
 285        if deserializer:
 286            g["serde_legacy_custom_class_deserializer"] = functools.partial(
 287                serde_legacy_custom_class_deserializer, custom=deserializer
 288            )
 289
 290        # Collect types used in the generated code.
 291        for typ in iter_types(cls):
 292            # When we encounter a dataclass not marked with deserialize, then also generate
 293            # deserialize functions for it.
 294            if is_dataclass_without_de(typ) and typ is not cls:
 295                # We call deserialize and not wrap to make sure that we will use the default serde
 296                # configuration for generating the deserialization function.
 297                deserialize(typ)
 298
 299            # We don't want to add primitive class e.g "str" into the scope, but primitive
 300            # compatible types such as IntEnum and a subclass of primitives are added,
 301            # so that generated code can use those types.
 302            if is_primitive(typ) and not is_enum(typ) and not is_primitive_subclass(typ):
 303                continue
 304
 305            if is_generic(typ):
 306                g[typename(typ)] = get_origin(typ)
 307            else:
 308                g[typename(typ)] = typ
 309
 310        # render all union functions
 311        for union in iter_unions(cls):
 312            union_args = type_args(union)
 313            add_func(
 314                scope,
 315                union_func_name(UNION_DE_PREFIX, union_args),
 316                render_union_func(cls, union_args, tagging),
 317                g,
 318            )
 319
 320        # render literal functions
 321        for literal in iter_literals(cls):
 322            literal_args = type_args(literal)
 323            add_func(
 324                scope, literal_func_name(literal_args), render_literal_func(cls, literal_args), g
 325            )
 326
 327        # Collect default values and default factories used in the generated code.
 328        for f in defields(cls):
 329            assert f.name
 330            if has_default(f):
 331                scope.defaults[f.name] = f.default
 332            elif has_default_factory(f):
 333                scope.defaults[f.name] = f.default_factory
 334            if f.deserializer:
 335                g[f.deserializer.name] = f.deserializer
 336
 337        add_func(
 338            scope,
 339            FROM_ITER,
 340            render_from_iter(cls, deserializer, type_check, class_deserializer=class_deserializer),
 341            g,
 342        )
 343        add_func(
 344            scope,
 345            FROM_DICT,
 346            render_from_dict(
 347                cls,
 348                rename_all,
 349                deserializer,
 350                type_check,
 351                class_deserializer=class_deserializer,
 352                deny_unknown_fields=deny_unknown_fields,
 353            ),
 354            g,
 355        )
 356
 357        logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}")
 358
 359        stack.pop()
 360        return cls
 361
 362    if _cls is None:
 363        return wrap  # type: ignore
 364
 365    if _cls in GENERATION_STACK:
 366        return _cls
 367
 368    GENERATION_STACK.append(_cls)
 369    try:
 370        return wrap(_cls)
 371    finally:
 372        GENERATION_STACK.pop()
 373
 374
 375def is_deserializable(instance_or_class: Any) -> bool:
 376    """
 377    Test if an instance or class is deserializable.
 378
 379    >>> @deserialize
 380    ... class Foo:
 381    ...     pass
 382    >>>
 383    >>> is_deserializable(Foo)
 384    True
 385    """
 386    return hasattr(instance_or_class, SERDE_SCOPE)
 387
 388
 389def is_dataclass_without_de(cls: type[Any]) -> bool:
 390    if not dataclasses.is_dataclass(cls):
 391        return False
 392    if not hasattr(cls, SERDE_SCOPE):
 393        return True
 394    scope: Optional[Scope] = getattr(cls, SERDE_SCOPE)
 395    if not scope:
 396        return True
 397    return FROM_DICT not in scope.funcs
 398
 399
 400class Deserializer(Generic[T], metaclass=abc.ABCMeta):
 401    """
 402    `Deserializer` base class. Subclass this to customize deserialize behaviour.
 403
 404    See `serde.json.JsonDeserializer` and `serde.msgpack.MsgPackDeserializer` for example usage.
 405    """
 406
 407    @classmethod
 408    @abc.abstractmethod
 409    def deserialize(cls, data: T, **opts: Any) -> Any:
 410        """
 411        deserialize `data` into an object typically `dict`, `list` or `tuple`.
 412
 413        For example, `serde.json.JsonDeserializer` takes json string and deserialize
 414        into an object. `serde.msgpack.MsgPackDeserializer` takes msgpack bytes and
 415        deserialize into an object.
 416        """
 417        raise NotImplementedError
 418
 419
 420def from_obj(
 421    c: type[T],
 422    o: Any,
 423    named: bool,
 424    reuse_instances: Optional[bool],
 425    deserialize_numbers: Optional[Callable[[Union[str, int]], float]] = None,
 426) -> T:
 427    """
 428    Deserialize from an object into an instance of the type specified as arg `c`.
 429    `c` can be either primitive type, `list`, `tuple`, `dict` or `deserialize` class.
 430
 431    * `deserialize_numbers`: Optional callable to coerce numeric input into float (and subclasses)
 432      when a float target is encountered. Useful for callers that want to treat ints or strings as
 433      floats.
 434    """
 435
 436    res: Any
 437
 438    # It is possible that the parser already generates the correct data type requested
 439    # by the caller. Hence, it should be checked early to avoid doing anymore work.
 440    if type(o) is c:
 441        return o
 442
 443    def deserializable_to_obj(cls: type[T]) -> T:
 444        serde_scope: Scope = getattr(cls, SERDE_SCOPE)
 445        func_name = FROM_DICT if named else FROM_ITER
 446        res = serde_scope.funcs[func_name](
 447            cls,
 448            maybe_generic=maybe_generic,
 449            data=o,
 450            reuse_instances=reuse_instances,
 451            deserialize_numbers=deserialize_numbers,
 452        )
 453        return res  # type: ignore
 454
 455    if is_union(c) and not is_opt(c):
 456        # If a class in the argument is a non-dataclass class e.g. Union[Foo, Bar],
 457        # pyserde generates a wrapper (de)serializable dataclass on the fly,
 458        # and use it to deserialize into the object.
 459        return CACHE.deserialize_union(c, o, deserialize_numbers=deserialize_numbers)
 460
 461    if is_generic(c):
 462        # Store subscripted generic type such as Foo[Bar] in "maybe_generic",
 463        # and store origin type such as Foo in "c". Since subscripted generics
 464        # are not a subclass of "type", use "c" for type inspection, and pass
 465        # "maybe_generic" in deserialize functions.
 466        maybe_generic = c
 467        c = get_origin(c)  # type: ignore
 468    else:
 469        maybe_generic = c
 470    try:
 471        thisfunc = functools.partial(
 472            from_obj,
 473            named=named,
 474            reuse_instances=reuse_instances,
 475            deserialize_numbers=deserialize_numbers,
 476        )
 477        if is_dataclass_without_de(c):
 478            # Do not automatically implement beartype if dataclass without serde decorator
 479            # is passed, because it is surprising for users
 480            # See https://github.com/yukinarit/pyserde/issues/480
 481            deserialize(c, type_check=disabled)
 482            res = deserializable_to_obj(c)
 483        elif is_deserializable(c):
 484            res = deserializable_to_obj(c)
 485        elif is_opt(c):
 486            if o is None:
 487                res = None
 488            else:
 489                res = thisfunc(type_args(c)[0], o)
 490        elif is_list(c):
 491            if is_bare_list(c):
 492                res = list(o)
 493            else:
 494                res = [thisfunc(type_args(c)[0], e) for e in o]
 495        elif is_set(c):
 496            if is_bare_set(c):
 497                res = set(o)
 498            elif is_frozen_set(c):
 499                res = frozenset(thisfunc(type_args(c)[0], e) for e in o)
 500            else:
 501                res = {thisfunc(type_args(c)[0], e) for e in o}
 502        elif is_tuple(c):
 503            if is_bare_tuple(c) or is_variable_tuple(c):
 504                res = tuple(e for e in o)
 505            else:
 506                res = tuple(thisfunc(type_args(c)[i], e) for i, e in enumerate(o))
 507        elif is_dict(c):
 508            if is_bare_dict(c):
 509                res = o
 510            elif is_default_dict(c):
 511                f = DeField(c, "")
 512                v = f.value_field()
 513                origin = get_origin(v.type)
 514                res = collections.defaultdict(
 515                    origin if origin else v.type,
 516                    {
 517                        thisfunc(type_args(c)[0], k): thisfunc(type_args(c)[1], v)
 518                        for k, v in o.items()
 519                    },
 520                )
 521            else:
 522                res = {
 523                    thisfunc(type_args(c)[0], k): thisfunc(type_args(c)[1], v) for k, v in o.items()
 524                }
 525        elif _is_numpy_array(c):
 526            from .numpy import deserialize_numpy_array_direct
 527
 528            res = deserialize_numpy_array_direct(c, o)
 529        elif is_datetime(c):
 530            res = c.fromisoformat(o)
 531        elif isinstance(c, type) and issubclass(c, float):
 532            res = deserialize_numbers(o) if deserialize_numbers else c(o)
 533        elif is_any(c) or is_ellipsis(c):
 534            res = o
 535        else:
 536            res = cast(Any, c)(o)
 537
 538        return cast(T, res)
 539
 540    except UserError as e:
 541        raise e.inner from None
 542
 543    except Exception as e:
 544        raise SerdeError(e) from None
 545
 546
 547@overload
 548def from_dict(
 549    cls: type[T],
 550    o: dict[str, Any],
 551    reuse_instances: Optional[bool] = None,
 552    deserialize_numbers: Optional[Callable[[Union[str, int]], float]] = None,
 553) -> T: ...
 554
 555
 556@overload
 557def from_dict(
 558    cls: Any,
 559    o: dict[str, Any],
 560    reuse_instances: Optional[bool] = None,
 561    deserialize_numbers: Optional[Callable[[Union[str, int]], float]] = None,
 562) -> Any: ...
 563
 564
 565def from_dict(
 566    cls: Any,
 567    o: dict[str, Any],
 568    reuse_instances: Optional[bool] = None,
 569    deserialize_numbers: Optional[Callable[[Union[str, int]], float]] = None,
 570) -> Any:
 571    """
 572    Deserialize dictionary into object.
 573
 574    >>> @deserialize
 575    ... class Foo:
 576    ...     i: int
 577    ...     s: str = 'foo'
 578    ...     f: float = 100.0
 579    ...     b: bool = True
 580    >>>
 581    >>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True})
 582    Foo(i=10, s='foo', f=100.0, b=True)
 583
 584    You can pass any type supported by pyserde. For example,
 585
 586    * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target
 587      type is float (e.g. accept ints or numeric strings supplied by a parser).
 588
 589    >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
 590    ...        {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
 591    >>> from_dict(list[Foo], lst)
 592    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
 593    """
 594    return from_obj(
 595        cls,
 596        o,
 597        named=True,
 598        reuse_instances=reuse_instances,
 599        deserialize_numbers=deserialize_numbers,
 600    )
 601
 602
 603@overload
 604def from_tuple(
 605    cls: type[T],
 606    o: Any,
 607    reuse_instances: Optional[bool] = None,
 608    deserialize_numbers: Optional[Callable[[Union[str, int]], float]] = None,
 609) -> T: ...
 610
 611
 612@overload
 613def from_tuple(
 614    cls: Any,
 615    o: Any,
 616    reuse_instances: Optional[bool] = None,
 617    deserialize_numbers: Optional[Callable[[Union[str, int]], float]] = None,
 618) -> Any: ...
 619
 620
 621def from_tuple(
 622    cls: Any,
 623    o: Any,
 624    reuse_instances: Optional[bool] = None,
 625    deserialize_numbers: Optional[Callable[[Union[str, int]], float]] = None,
 626) -> Any:
 627    """
 628    Deserialize tuple into object.
 629
 630    >>> @deserialize
 631    ... class Foo:
 632    ...     i: int
 633    ...     s: str = 'foo'
 634    ...     f: float = 100.0
 635    ...     b: bool = True
 636    >>>
 637    >>> from_tuple(Foo, (10, 'foo', 100.0, True))
 638    Foo(i=10, s='foo', f=100.0, b=True)
 639
 640    You can pass any type supported by pyserde. For example,
 641
 642    * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target
 643      type is float (e.g. accept ints or numeric strings supplied by a parser).
 644
 645    >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
 646    >>> from_tuple(list[Foo], lst)
 647    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
 648    """
 649    return from_obj(
 650        cls,
 651        o,
 652        named=False,
 653        reuse_instances=reuse_instances,
 654        deserialize_numbers=deserialize_numbers,
 655    )
 656
 657
 658@dataclass
 659class DeField(Field[T]):
 660    """
 661    Represents a field of dataclass.
 662    """
 663
 664    datavar: Optional[str] = None
 665    """ Name of variable which is passed in the deserialize API """
 666
 667    index: int = 0
 668    """ Field index """
 669
 670    iterbased: bool = False
 671    """ Iterater based deserializer or not """
 672
 673    def __getitem__(self, n: int) -> Union[DeField[Any], InnerField[Any]]:
 674        """
 675        Get inner `Field` from current `Field`.
 676
 677        `InnerField` is returned if self is of any standard collection e.g. list.
 678        `DeField` is returned if self is Optional.
 679        """
 680        typ = type_args(self.type)[n]
 681        opts: dict[str, Any] = {
 682            "kw_only": self.kw_only,
 683            "case": self.case,
 684            "alias": self.alias,
 685            "rename": self.rename,
 686            "skip": self.skip,
 687            "skip_if": self.skip_if,
 688            "skip_if_false": self.skip_if_false,
 689            "skip_if_default": self.skip_if_default,
 690            "serializer": self.serializer,
 691            "deserializer": self.deserializer,
 692            "flatten": self.flatten,
 693            "parent": self.parent,
 694        }
 695        if is_list(self.type) or is_dict(self.type) or is_set(self.type):
 696            return InnerField(typ, "v", datavar="v", **opts)
 697        elif is_tuple(self.type):
 698            return InnerField(typ, f"{self.data}[{n}]", datavar=f"{self.data}[{n}]", **opts)
 699        else:
 700            # For Optional etc.
 701            return DeField(
 702                typ,
 703                self.name,
 704                datavar=self.datavar,
 705                index=self.index,
 706                iterbased=self.iterbased,
 707                **opts,
 708            )
 709
 710    def key_field(self) -> DeField[Any]:
 711        """
 712        Get inner key field for dict like class.
 713        """
 714        k = self[0]
 715        k.name = "k"
 716        k.datavar = "k"
 717        return k
 718
 719    def value_field(self) -> DeField[Any]:
 720        """
 721        Get inner value field for dict like class.
 722        """
 723        return self[1]
 724
 725    @property
 726    def data(self) -> str:
 727        """
 728        Renders the variable name that possesses the data.
 729
 730        e.g. tuple
 731            * datavar property returns "data"
 732            * data property returns "data[0]".
 733        e.g. Optional
 734            * datavar property returns "data"
 735            * data property returns "data.get("field_name")".
 736        For other types
 737            * datavar property returns "data"
 738            * data property returns "data["field_name"]".
 739        """
 740
 741        if self.iterbased:
 742            return f"{self.datavar}[{self.index}]"
 743        elif is_union(self.type) and type(None) in get_args(self.type):
 744            return f'{self.datavar}.get("{self.conv_name()}")'
 745        else:
 746            return f'{self.datavar}["{self.conv_name()}"]'
 747
 748    @data.setter
 749    def data(self, d: str) -> None:
 750        self.datavar = d
 751
 752    def data_or(self) -> str:
 753        if self.iterbased:
 754            return self.data
 755        else:
 756            return f'{self.datavar}.get("{self.conv_name()}")'
 757
 758
 759@dataclass
 760class InnerField(DeField[T]):
 761    """
 762    Field of Inner type. The purpose of this class is to override "data" method
 763    for inner type codegen.
 764
 765    e.g.
 766      T of list[T]
 767      V of dict[K, V]
 768      T of Optional[T]
 769    """
 770
 771    @property
 772    def data(self) -> str:
 773        return self.datavar or ""
 774
 775    @data.setter
 776    def data(self, d: str) -> None:
 777        self.datavar = d
 778
 779
 780def defields(cls: type[Any]) -> list[DeField[Any]]:
 781    return fields(DeField, cls)
 782
 783
 784@dataclass
 785class Renderer:
 786    """
 787    Render rvalue for code generation.
 788    """
 789
 790    func: str
 791    cls: Optional[type[Any]] = None
 792    legacy_class_deserializer: Optional[DeserializeFunc] = None
 793    import_numpy: bool = False
 794    suppress_coerce: bool = False
 795    """ Disable type coercing in codegen """
 796    class_deserializer: Optional[ClassDeserializer] = None
 797    class_name: Optional[str] = None
 798
 799    def render(self, arg: DeField[Any]) -> str:
 800        """
 801        Render rvalue
 802        """
 803        implemented_methods: dict[type[Any], int] = {}
 804        class_deserializers: Iterable[ClassDeserializer] = itertools.chain(
 805            GLOBAL_CLASS_DESERIALIZER, [self.class_deserializer] if self.class_deserializer else []
 806        )
 807        for n, class_deserializer in enumerate(class_deserializers):
 808            for method in class_deserializer.__class__.deserialize.methods:  # type: ignore
 809                implemented_methods[get_args(method.signature.types[1])[0]] = n
 810
 811        custom_deserializer_available = arg.type in implemented_methods
 812        if custom_deserializer_available and not arg.deserializer:
 813            res = (
 814                f"class_deserializers[{implemented_methods[arg.type]}].deserialize("
 815                f"{typename(arg.type)}, {arg.data})"
 816            )
 817        elif arg.deserializer and arg.deserializer.inner is not default_deserializer:
 818            res = self.custom_field_deserializer(arg)
 819        elif is_generic(arg.type):
 820            arg.type_args = list(get_args(arg.type))
 821            origin = get_origin(arg.type)
 822            assert origin
 823            arg.type = origin
 824            res = self.render(arg)
 825        elif is_dataclass(arg.type):
 826            res = self.dataclass(arg)
 827        elif is_opt(arg.type):
 828            res = self.opt(arg)
 829        elif is_list(arg.type):
 830            res = self.list(arg)
 831        elif is_set(arg.type):
 832            res = self.set(arg)
 833        elif is_dict(arg.type):
 834            res = self.dict(arg)
 835        elif is_tuple(arg.type):
 836            res = self.tuple(arg)
 837        elif is_enum(arg.type):
 838            res = self.enum(arg)
 839        elif _is_numpy_scalar(arg.type):
 840            from .numpy import deserialize_numpy_scalar
 841
 842            self.import_numpy = True
 843            res = deserialize_numpy_scalar(arg)
 844        elif _is_numpy_array(arg.type):
 845            from .numpy import deserialize_numpy_array
 846
 847            self.import_numpy = True
 848            res = deserialize_numpy_array(arg)
 849        elif _is_numpy_jaxtyping(arg.type):
 850            from .numpy import deserialize_numpy_jaxtyping_array
 851
 852            self.import_numpy = True
 853            res = deserialize_numpy_jaxtyping_array(arg)
 854        elif is_union(arg.type):
 855            res = self.union_func(arg)
 856        elif is_str_serializable(arg.type):
 857            res = f"({self.c_tor_with_check(arg)}) if reuse_instances else {self.c_tor(arg)}"
 858        elif is_datetime(arg.type):
 859            from_iso = f"{typename(arg.type)}.fromisoformat({arg.data})"
 860            res = f"({arg.data} if isinstance({arg.data}, {typename(arg.type)}) else {from_iso}) \
 861                    if reuse_instances else {from_iso}"
 862        elif is_none(arg.type):
 863            res = "None"
 864        elif is_any(arg.type) or is_ellipsis(arg.type):
 865            res = arg.data
 866        elif is_pep695_type_alias(arg.type):
 867            res = self.render(DeField(name=arg.name, type=arg.type.__value__, datavar=arg.datavar))
 868        elif is_primitive(arg.type):
 869            # For subclasses for primitives e.g. class FooStr(str), coercing is always enabled
 870            res = self.primitive(arg, not is_primitive_subclass(arg.type))
 871        elif isinstance(arg.type, TypeVar):
 872            if not self.cls:
 873                raise SerdeError("Missing cls")
 874            index = find_generic_arg(self.cls, arg.type)
 875            res = (
 876                f"from_obj(get_generic_arg(maybe_generic, maybe_generic_type_vars, "
 877                f"variable_type_args, {index}), {arg.data}, named={not arg.iterbased}, "
 878                "reuse_instances=reuse_instances, deserialize_numbers=deserialize_numbers)"
 879            )
 880        elif is_literal(arg.type):
 881            res = self.literal(arg)
 882        else:
 883            return f"raise_unsupported_type({arg.data})"
 884
 885        if arg.supports_default():
 886            res = self.default(arg, res)
 887
 888        if (
 889            self.legacy_class_deserializer
 890            and not arg.deserializer
 891            and not custom_deserializer_available
 892        ):
 893            # Rerender the code for default deserializer.
 894            default = Renderer(
 895                self.func, self.cls, None, suppress_coerce=self.suppress_coerce
 896            ).render(arg)
 897            return self.custom_class_deserializer(arg, default)
 898        else:
 899            return res
 900
 901    def custom_field_deserializer(self, arg: DeField[Any]) -> str:
 902        """
 903        Render rvalue for the field with custom deserializer.
 904        """
 905        if not arg.deserializer:
 906            raise SerdeError("Missing custom field deserializer")
 907        return f"{arg.deserializer.name}({arg.data})"
 908
 909    def custom_class_deserializer(self, arg: DeField[Any], code: str) -> str:
 910        """
 911        Render custom class deserializer.
 912        """
 913        # The function takes a closure in order to execute the default value lazily.
 914        return (
 915            "serde_legacy_custom_class_deserializer("
 916            f"{typename(arg.type)}, "
 917            f"{arg.datavar}, "
 918            f"{arg.data_or()}, "
 919            f"default=lambda: {code})"
 920        )
 921
 922    def dataclass(self, arg: DeField[Any]) -> str:
 923        if not arg.flatten:
 924            # e.g. "data['field']" will be used as variable name.
 925            var = arg.data
 926        else:
 927            # Because the field is flattened
 928            # e.g. "data" will be used as variable name.
 929            assert arg.datavar
 930            if arg.iterbased:
 931                var = f"{arg.datavar}[{arg.index}:]"
 932            else:
 933                var = arg.datavar
 934
 935        type_args_str = [str(t).lstrip("~") for t in arg.type_args] if arg.type_args else None
 936
 937        opts = (
 938            "maybe_generic=maybe_generic, maybe_generic_type_vars=maybe_generic_type_vars, "
 939            f"variable_type_args={type_args_str}, reuse_instances=reuse_instances, "
 940            "deserialize_numbers=deserialize_numbers"
 941        )
 942
 943        if arg.is_self_referencing():
 944            class_name = "cls"
 945        else:
 946            class_name = typename(arg.type)
 947
 948        return f"{class_name}.{SERDE_SCOPE}.funcs['{self.func}'](data={var}, {opts})"
 949
 950    def opt(self, arg: DeField[Any]) -> str:
 951        """
 952        Render rvalue for Optional.
 953        """
 954        inner = arg[0]
 955        if arg.iterbased:
 956            exists = f"{arg.data} is not None"
 957        elif arg.flatten:
 958            # Check nullabilities of all nested fields.
 959            exists = " and ".join(
 960                [
 961                    f'{arg.datavar}.get("{f.name}") is not None'
 962                    for f in dataclasses.fields(inner.type)
 963                ]
 964            )
 965        else:
 966            name = arg.conv_name()
 967            if arg.alias:
 968                aliases = (f'"{s}"' for s in [name, *arg.alias])
 969                get = f"_get_by_aliases(data, [{','.join(aliases)}], raise_error=False)"
 970            else:
 971                get = f'{arg.datavar}.get("{name}")'
 972            exists = f"{get} is not None"
 973        return f"({self.render(inner)}) if {exists} else None"
 974
 975    def list(self, arg: DeField[Any]) -> str:
 976        """
 977        Render rvalue for list.
 978        """
 979        if is_bare_list(arg.type):
 980            return f"list({arg.data})"
 981        else:
 982            return f"[{self.render(arg[0])} for v in {arg.data}]"
 983
 984    def set(self, arg: DeField[Any]) -> str:
 985        """
 986        Render rvalue for set.
 987        """
 988        if is_bare_set(arg.type):
 989            return f"set({arg.data})"
 990        elif is_frozen_set(arg.type):
 991            return f"frozenset({self.render(arg[0])} for v in {arg.data})"
 992        else:
 993            return f"set({self.render(arg[0])} for v in {arg.data})"
 994
 995    def tuple(self, arg: DeField[Any]) -> str:
 996        """
 997        Render rvalue for tuple.
 998        """
 999        if is_bare_tuple(arg.type):
1000            return f"tuple({arg.data})"
1001        elif is_variable_tuple(arg.type):
1002            earg = arg[0]
1003            earg.datavar = "v"
1004            return f"tuple({self.render(earg)} for v in {arg.data})"
1005        else:
1006            values = []
1007            for i, _typ in enumerate(type_args(arg.type)):
1008                inner = arg[i]
1009                values.append(self.render(inner))
1010            return f'({", ".join(values)},)'  # trailing , is required for single element tuples
1011
1012    def dict(self, arg: DeField[Any]) -> str:
1013        """
1014        Render rvalue for dict.
1015        """
1016        if is_bare_dict(arg.type):
1017            return arg.data
1018        elif is_default_dict(arg.type):
1019            k = arg.key_field()
1020            v = arg.value_field()
1021            origin = get_origin(v.type)
1022            if origin:
1023                # When the callable type is of generic type e.g list.
1024                # Get origin type "list" from "list[X]".
1025                callable = origin.__name__
1026            else:
1027                # When the callable type is non generic type e.g int, Foo.
1028                callable = v.type.__name__
1029            return f"collections.defaultdict({callable}, \
1030                    {{{self.render(k)}: {self.render(v)} for k, v in {arg.data}.items()}})"
1031        else:
1032            k = arg.key_field()
1033            v = arg.value_field()
1034            return f"{{{self.render(k)}: {self.render(v)} for k, v in {arg.data}.items()}}"
1035
1036    def enum(self, arg: DeField[Any]) -> str:
1037        return f"{typename(arg.type)}({self.primitive(arg)})"
1038
1039    def primitive(self, arg: DeField[Any], suppress_coerce: bool = False) -> str:
1040        """
1041        Render rvalue for primitives.
1042
1043        * `suppress_coerce`: Overrides "suppress_coerce" in the Renderer's field
1044        """
1045        typ = typename(arg.type)
1046        dat = arg.data
1047        if arg.alias:
1048            aliases = (f'"{s}"' for s in [arg.name, *arg.alias])
1049            dat = f"_get_by_aliases(data, [{','.join(aliases)}])"
1050        if isinstance(arg.type, type) and issubclass(arg.type, float):
1051            if self.suppress_coerce and suppress_coerce:
1052                return f"deserialize_numbers({dat}) if deserialize_numbers else {dat}"
1053            else:
1054                assert arg.name
1055                escaped_arg_name = arg.name.replace('"', '\\"')
1056                return (
1057                    f"deserialize_numbers({dat}) if deserialize_numbers else "
1058                    f'coerce_object("{self.class_name}", "{escaped_arg_name}", {typ}, {dat})'
1059                )
1060        if self.suppress_coerce and suppress_coerce:
1061            return dat
1062        else:
1063            assert arg.name
1064            escaped_arg_name = arg.name.replace('"', '\\"')
1065            return f'coerce_object("{self.class_name}", "{escaped_arg_name}", {typ}, {dat})'
1066
1067    def c_tor(self, arg: DeField[Any]) -> str:
1068        return f"{typename(arg.type)}({arg.data})"
1069
1070    def c_tor_with_check(self, arg: DeField[Any], ctor: Optional[str] = None) -> str:
1071        if ctor is None:
1072            ctor = self.c_tor(arg)
1073        return f"{arg.data} if isinstance({arg.data}, {typename(arg.type)}) else {ctor}"
1074
1075    def union_func(self, arg: DeField[Any]) -> str:
1076        func_name = union_func_name(UNION_DE_PREFIX, type_args(arg.type))
1077        return (
1078            f"serde_scope.funcs['{func_name}']("
1079            "cls=cls, "
1080            f"data={arg.data}, "
1081            "reuse_instances=reuse_instances, "
1082            "deserialize_numbers=deserialize_numbers)"
1083        )
1084
1085    def literal(self, arg: DeField[Any]) -> str:
1086        func_name = literal_func_name(type_args(arg.type))
1087        return (
1088            f"serde_scope.funcs['{func_name}']("
1089            "cls=cls, "
1090            f"data={arg.data}, "
1091            "reuse_instances=reuse_instances, "
1092            "deserialize_numbers=deserialize_numbers)"
1093        )
1094
1095    def default(self, arg: DeField[Any], code: str) -> str:
1096        """
1097        Renders supplying default value during deserialization.
1098        """
1099
1100        def get_aliased_fields(arg: Field[Any]) -> Iterator[str]:
1101            return (f'"{s}"' for s in [arg.name, *arg.alias])
1102
1103        if arg.flatten:
1104            # When a field has the `flatten` attribute, iterate over its dataclass fields.
1105            # This ensures that the code checks keys in the data while considering aliases.
1106            flattened = []
1107            for subarg in defields(arg.type):
1108                if subarg.alias:
1109                    aliases = get_aliased_fields(subarg)
1110                    flattened.append(f'_exists_by_aliases({arg.datavar}, [{",".join(aliases)}])')
1111                else:
1112                    flattened.append(f'"{subarg.name}" in {arg.datavar}')
1113            exists = " and ".join(flattened)
1114        else:
1115            if arg.alias:
1116                aliases = get_aliased_fields(arg)
1117                exists = f'_exists_by_aliases({arg.datavar}, [{",".join(aliases)}])'
1118            else:
1119                exists = f'"{arg.conv_name()}" in {arg.datavar}'
1120
1121        if has_default(arg):
1122            return f'({code}) if {exists} else serde_scope.defaults["{arg.name}"]'
1123        elif has_default_factory(arg):
1124            return f'({code}) if {exists} else serde_scope.defaults["{arg.name}"]()'
1125        else:
1126            return code
1127
1128
1129def to_arg(f: DeField[T], index: int, rename_all: Optional[str] = None) -> DeField[T]:
1130    f.index = index
1131    f.data = "data"
1132    f.case = f.case or rename_all
1133    return f
1134
1135
1136def to_iter_arg(f: DeField[T], *args: Any, **kwargs: Any) -> DeField[T]:
1137    f = to_arg(f, *args, **kwargs)
1138    f.iterbased = True
1139    return f
1140
1141
1142def renderable(f: DeField[Any]) -> bool:
1143    return f.init
1144
1145
1146jinja2_env = jinja2.Environment(
1147    loader=jinja2.DictLoader(
1148        {
1149            "iter": """
1150def {{func}}(cls=cls, maybe_generic=None, maybe_generic_type_vars=None, data=None,
1151             variable_type_args=None, reuse_instances=None, deserialize_numbers=None):
1152  if reuse_instances is None:
1153    reuse_instances = {{serde_scope.reuse_instances_default}}
1154
1155  maybe_generic_type_vars = maybe_generic_type_vars or {{cls_type_vars}}
1156
1157  {% for f in fields %}
1158  __{{f.name}} = {{rvalue(arg(f,loop.index-1))}}
1159  {% endfor %}
1160
1161  try:
1162    return cls(
1163      {% for f in fields %}
1164      __{{f.name}},
1165      {% endfor %}
1166    )
1167  except BeartypeCallHintParamViolation as e:
1168    raise SerdeError(e)
1169  except Exception as e:
1170    raise UserError(e)
1171""",
1172            "dict": """
1173def {{func}}(cls=cls, maybe_generic=None, maybe_generic_type_vars=None, data=None,
1174             variable_type_args=None, reuse_instances=None, deserialize_numbers=None):
1175  if reuse_instances is None:
1176    reuse_instances = {{serde_scope.reuse_instances_default}}
1177
1178  {% if deny_unknown_fields %}
1179  known_fields = {{ known_fields }}
1180  unknown_fields = set((data or {}).keys()) - known_fields
1181  if unknown_fields:
1182    raise SerdeError(f'unknown fields: {unknown_fields}, expected one of {known_fields}')
1183  {% endif %}
1184
1185  maybe_generic_type_vars = maybe_generic_type_vars or {{cls_type_vars}}
1186
1187  {% for f in fields %}
1188  __{{f.name}} = {{rvalue(arg(f,loop.index-1))}}
1189  {% endfor %}
1190
1191  try:
1192    return cls(
1193    {% for f in fields %}
1194    {% if f.kw_only %}
1195    {{f.name}}=__{{f.name}},
1196    {% else %}
1197    __{{f.name}},
1198    {% endif %}
1199    {% endfor %}
1200    )
1201  except BeartypeCallHintParamViolation as e:
1202    raise SerdeError(e)
1203  except Exception as e:
1204    raise UserError(e)
1205""",
1206            "union": """
1207def {{func}}(cls=cls, maybe_generic=None, maybe_generic_type_vars=None, data=None,
1208             variable_type_args=None, reuse_instances = {{serde_scope.reuse_instances_default}},
1209             deserialize_numbers=None):
1210  errors = []
1211  {% for t in union_args %}
1212  try:
1213    # create fake dict so we can reuse the normal render function
1214    {% if tagging.is_external() and is_taggable(t)  %}
1215    ensure("{{typename(t)}}" in data , "'{{typename(t)}}' key is not present")
1216    fake_dict = {"fake_key": data["{{typename(t)}}"]}
1217
1218    {% elif tagging.is_internal() and is_taggable(t) %}
1219    ensure("{{tagging.tag}}" in data , "'{{tagging.tag}}' key is not present")
1220    ensure("{{typename(t)}}" == data["{{tagging.tag}}"], "tag '{{typename(t)}}' isn't found")
1221    fake_dict = {"fake_key": data}
1222
1223    {% elif tagging.is_adjacent() and is_taggable(t) %}
1224    ensure("{{tagging.tag}}" in data , "'{{tagging.tag}}' key is not present")
1225    ensure("{{tagging.content}}" in data , "'{{tagging.content}}' key is not present")
1226    ensure("{{typename(t)}}" == data["{{tagging.tag}}"], "tag '{{typename(t)}}' isn't found")
1227    fake_dict = {"fake_key": data["{{tagging.content}}"]}
1228
1229    {% else %}
1230    fake_dict = {"fake_key": data}
1231    {% endif %}
1232
1233    {% if is_primitive(t) or is_none(t) %}
1234    if not isinstance(fake_dict["fake_key"], {{typename(t)}}):
1235        raise Exception("Not a type of {{typename(t)}}")
1236    {% endif %}
1237    res = {{rvalue(arg(t))}}
1238    ensure(is_bearable(res, {{typename(t)}}), "object is not of type '{{typename(t)}}'")
1239    return res
1240  except Exception as e:
1241    errors.append(f" Failed to deserialize into {{typename(t)}}: {e}")
1242  {% endfor %}
1243  raise SerdeError("Can not deserialize " + repr(data) + " of type " + \
1244          typename(type(data)) + " into {{union_name}}.\\nReasons:\\n" + "\\n".join(errors))
1245""",
1246            "literal": """
1247def {{func}}(cls=cls, maybe_generic=None, maybe_generic_type_vars=None, data=None,
1248             variable_type_args=None, reuse_instances = {{serde_scope.reuse_instances_default}},
1249             deserialize_numbers=None):
1250  if data in ({%- for v in literal_args -%}{{repr(v)}},{%- endfor -%}):
1251    return data
1252  raise SerdeError("Can not deserialize " + repr(data) + " as {{literal_name}}.")
1253  """,
1254        }
1255    )
1256)
1257
1258
1259def render_from_iter(
1260    cls: type[Any],
1261    legacy_class_deserializer: Optional[DeserializeFunc] = None,
1262    type_check: TypeCheck = strict,
1263    class_deserializer: Optional[ClassDeserializer] = None,
1264) -> str:
1265    renderer = Renderer(
1266        FROM_ITER,
1267        cls=cls,
1268        legacy_class_deserializer=legacy_class_deserializer,
1269        suppress_coerce=(not type_check.is_coerce()),
1270        class_deserializer=class_deserializer,
1271        class_name=typename(cls),
1272    )
1273    fields = list(filter(renderable, defields(cls)))
1274    res = jinja2_env.get_template("iter").render(
1275        func=FROM_ITER,
1276        serde_scope=getattr(cls, SERDE_SCOPE),
1277        fields=fields,
1278        cls_type_vars=get_type_var_names(cls),
1279        rvalue=renderer.render,
1280        arg=to_iter_arg,
1281    )
1282
1283    if renderer.import_numpy:
1284        res = "import numpy\n" + res
1285
1286    return res
1287
1288
1289def get_known_fields(f: DeField[Any], rename_all: Optional[str]) -> list[str]:
1290    names: list[str] = [f.conv_name(rename_all)]
1291    return names + f.alias
1292
1293
1294def render_from_dict(
1295    cls: type[Any],
1296    rename_all: Optional[str] = None,
1297    legacy_class_deserializer: Optional[DeserializeFunc] = None,
1298    type_check: TypeCheck = strict,
1299    class_deserializer: Optional[ClassDeserializer] = None,
1300    deny_unknown_fields: bool = False,
1301) -> str:
1302    renderer = Renderer(
1303        FROM_DICT,
1304        cls=cls,
1305        legacy_class_deserializer=legacy_class_deserializer,
1306        suppress_coerce=(not type_check.is_coerce()),
1307        class_deserializer=class_deserializer,
1308        class_name=typename(cls),
1309    )
1310    fields = list(filter(renderable, defields(cls)))
1311    known_fields = set(
1312        itertools.chain.from_iterable([get_known_fields(f, rename_all) for f in fields])
1313    )
1314    res = jinja2_env.get_template("dict").render(
1315        func=FROM_DICT,
1316        serde_scope=getattr(cls, SERDE_SCOPE),
1317        fields=fields,
1318        type_check=type_check,
1319        cls_type_vars=get_type_var_names(cls),
1320        rvalue=renderer.render,
1321        arg=functools.partial(to_arg, rename_all=rename_all),
1322        deny_unknown_fields=deny_unknown_fields,
1323        known_fields=known_fields,
1324    )
1325
1326    if renderer.import_numpy:
1327        res = "import numpy\n" + res
1328
1329    return res
1330
1331
1332def render_union_func(
1333    cls: type[Any], union_args: Sequence[type[Any]], tagging: Tagging = DefaultTagging
1334) -> str:
1335    union_name = f"Union[{', '.join([typename(a) for a in union_args])}]"
1336
1337    renderer = Renderer(FROM_DICT, cls=cls, suppress_coerce=True)
1338    return jinja2_env.get_template("union").render(
1339        func=union_func_name(UNION_DE_PREFIX, union_args),
1340        serde_scope=getattr(cls, SERDE_SCOPE),
1341        union_args=union_args,
1342        union_name=union_name,
1343        tagging=tagging,
1344        is_taggable=Tagging.is_taggable,
1345        arg=lambda x: DeField(x, datavar="fake_dict", name="fake_key"),
1346        rvalue=renderer.render,
1347        is_primitive=is_primitive,
1348        is_none=is_none,
1349        typename=typename,
1350    )
1351
1352
1353def render_literal_func(
1354    cls: type[Any], literal_args: Sequence[Any], tagging: Tagging = DefaultTagging
1355) -> str:
1356    literal_name = f"Literal[{', '.join([repr(a) for a in literal_args])}]"
1357    return jinja2_env.get_template("literal").render(
1358        func=literal_func_name(literal_args),
1359        serde_scope=getattr(cls, SERDE_SCOPE),
1360        literal_args=literal_args,
1361        literal_name=literal_name,
1362        tagging=tagging,
1363        is_taggable=Tagging.is_taggable,
1364        repr=repr,
1365        type=type,
1366    )
@dataclass_transform()
def deserialize( _cls: Optional[type[~T]] = None, rename_all: Optional[str] = None, reuse_instances_default: bool = True, convert_sets_default: bool = False, deserializer: Optional[Callable[[type[Any], Any], Any]] = 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: Optional[serde.ClassDeserializer] = None, deny_unknown_fields: bool = False, **kwargs: Any) -> type[~T]:
200@dataclass_transform()
201def deserialize(
202    _cls: Optional[type[T]] = None,
203    rename_all: Optional[str] = None,
204    reuse_instances_default: bool = True,
205    convert_sets_default: bool = False,
206    deserializer: Optional[DeserializeFunc] = None,
207    tagging: Tagging = DefaultTagging,
208    type_check: TypeCheck = strict,
209    class_deserializer: Optional[ClassDeserializer] = None,
210    deny_unknown_fields: bool = False,
211    **kwargs: Any,
212) -> type[T]:
213    """
214    A dataclass with this decorator is deserializable from any of the data formats supported
215    by pyserde.
216
217    >>> from serde import deserialize
218    >>> from serde.json import from_json
219    >>>
220    >>> @deserialize
221    ... class Foo:
222    ...     i: int
223    ...     s: str
224    ...     f: float
225    ...     b: bool
226    >>>
227    >>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}')
228    Foo(i=10, s='foo', f=100.0, b=True)
229    """
230
231    stack = []
232
233    def wrap(cls: type[T]) -> type[T]:
234        if cls in stack:
235            return cls
236        stack.append(cls)
237
238        tagging.check()
239
240        # If no `dataclass` found in the class, dataclassify it automatically.
241        if not is_dataclass(cls):
242            dataclass(cls)
243
244        if type_check.is_strict():
245            serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError))
246            serde_beartype(cls)
247
248        g: dict[str, Any] = {}
249
250        # Create a scope storage used by serde.
251        # Each class should get own scope. Child classes can not share scope with parent class.
252        # That's why we need the "scope.cls is not cls" check.
253        scope: Optional[Scope] = getattr(cls, SERDE_SCOPE, None)
254        if scope is None or scope.cls is not cls:
255            scope = Scope(cls, reuse_instances_default=reuse_instances_default)
256            setattr(cls, SERDE_SCOPE, scope)
257
258        class_deserializers: list[ClassDeserializer] = list(
259            itertools.chain(
260                GLOBAL_CLASS_DESERIALIZER, [class_deserializer] if class_deserializer else []
261            )
262        )
263
264        # Set some globals for all generated functions
265        g["cls"] = cls
266        g["serde_scope"] = scope
267        g["SerdeError"] = SerdeError
268        g["UserError"] = UserError
269        g["raise_unsupported_type"] = raise_unsupported_type
270        g["typename"] = typename
271        g["ensure"] = ensure
272        g["typing"] = typing
273        g["collections"] = collections
274        g["Literal"] = Literal
275        g["from_obj"] = from_obj
276        g["get_generic_arg"] = get_generic_arg
277        g["is_instance"] = is_instance
278        g["TypeCheck"] = TypeCheck
279        g["disabled"] = disabled
280        g["coerce_object"] = coerce_object
281        g["_exists_by_aliases"] = _exists_by_aliases
282        g["_get_by_aliases"] = _get_by_aliases
283        g["class_deserializers"] = class_deserializers
284        g["BeartypeCallHintParamViolation"] = BeartypeCallHintParamViolation
285        g["is_bearable"] = is_bearable
286        if deserializer:
287            g["serde_legacy_custom_class_deserializer"] = functools.partial(
288                serde_legacy_custom_class_deserializer, custom=deserializer
289            )
290
291        # Collect types used in the generated code.
292        for typ in iter_types(cls):
293            # When we encounter a dataclass not marked with deserialize, then also generate
294            # deserialize functions for it.
295            if is_dataclass_without_de(typ) and typ is not cls:
296                # We call deserialize and not wrap to make sure that we will use the default serde
297                # configuration for generating the deserialization function.
298                deserialize(typ)
299
300            # We don't want to add primitive class e.g "str" into the scope, but primitive
301            # compatible types such as IntEnum and a subclass of primitives are added,
302            # so that generated code can use those types.
303            if is_primitive(typ) and not is_enum(typ) and not is_primitive_subclass(typ):
304                continue
305
306            if is_generic(typ):
307                g[typename(typ)] = get_origin(typ)
308            else:
309                g[typename(typ)] = typ
310
311        # render all union functions
312        for union in iter_unions(cls):
313            union_args = type_args(union)
314            add_func(
315                scope,
316                union_func_name(UNION_DE_PREFIX, union_args),
317                render_union_func(cls, union_args, tagging),
318                g,
319            )
320
321        # render literal functions
322        for literal in iter_literals(cls):
323            literal_args = type_args(literal)
324            add_func(
325                scope, literal_func_name(literal_args), render_literal_func(cls, literal_args), g
326            )
327
328        # Collect default values and default factories used in the generated code.
329        for f in defields(cls):
330            assert f.name
331            if has_default(f):
332                scope.defaults[f.name] = f.default
333            elif has_default_factory(f):
334                scope.defaults[f.name] = f.default_factory
335            if f.deserializer:
336                g[f.deserializer.name] = f.deserializer
337
338        add_func(
339            scope,
340            FROM_ITER,
341            render_from_iter(cls, deserializer, type_check, class_deserializer=class_deserializer),
342            g,
343        )
344        add_func(
345            scope,
346            FROM_DICT,
347            render_from_dict(
348                cls,
349                rename_all,
350                deserializer,
351                type_check,
352                class_deserializer=class_deserializer,
353                deny_unknown_fields=deny_unknown_fields,
354            ),
355            g,
356        )
357
358        logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}")
359
360        stack.pop()
361        return cls
362
363    if _cls is None:
364        return wrap  # type: ignore
365
366    if _cls in GENERATION_STACK:
367        return _cls
368
369    GENERATION_STACK.append(_cls)
370    try:
371        return wrap(_cls)
372    finally:
373        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_deserializable(instance_or_class: Any) -> bool:
376def is_deserializable(instance_or_class: Any) -> bool:
377    """
378    Test if an instance or class is deserializable.
379
380    >>> @deserialize
381    ... class Foo:
382    ...     pass
383    >>>
384    >>> is_deserializable(Foo)
385    True
386    """
387    return hasattr(instance_or_class, SERDE_SCOPE)

Test if an instance or class is deserializable.

>>> @deserialize
... class Foo:
...     pass
>>>
>>> is_deserializable(Foo)
True
def from_dict( cls: Any, o: dict[str, typing.Any], reuse_instances: Optional[bool] = None, deserialize_numbers: Optional[Callable[[Union[str, int]], float]] = None) -> Any:
566def from_dict(
567    cls: Any,
568    o: dict[str, Any],
569    reuse_instances: Optional[bool] = None,
570    deserialize_numbers: Optional[Callable[[Union[str, int]], float]] = None,
571) -> Any:
572    """
573    Deserialize dictionary into object.
574
575    >>> @deserialize
576    ... class Foo:
577    ...     i: int
578    ...     s: str = 'foo'
579    ...     f: float = 100.0
580    ...     b: bool = True
581    >>>
582    >>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True})
583    Foo(i=10, s='foo', f=100.0, b=True)
584
585    You can pass any type supported by pyserde. For example,
586
587    * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target
588      type is float (e.g. accept ints or numeric strings supplied by a parser).
589
590    >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
591    ...        {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
592    >>> from_dict(list[Foo], lst)
593    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
594    """
595    return from_obj(
596        cls,
597        o,
598        named=True,
599        reuse_instances=reuse_instances,
600        deserialize_numbers=deserialize_numbers,
601    )

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 from_tuple( cls: Any, o: Any, reuse_instances: Optional[bool] = None, deserialize_numbers: Optional[Callable[[Union[str, int]], float]] = None) -> Any:
622def from_tuple(
623    cls: Any,
624    o: Any,
625    reuse_instances: Optional[bool] = None,
626    deserialize_numbers: Optional[Callable[[Union[str, int]], float]] = None,
627) -> Any:
628    """
629    Deserialize tuple into object.
630
631    >>> @deserialize
632    ... class Foo:
633    ...     i: int
634    ...     s: str = 'foo'
635    ...     f: float = 100.0
636    ...     b: bool = True
637    >>>
638    >>> from_tuple(Foo, (10, 'foo', 100.0, True))
639    Foo(i=10, s='foo', f=100.0, b=True)
640
641    You can pass any type supported by pyserde. For example,
642
643    * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target
644      type is float (e.g. accept ints or numeric strings supplied by a parser).
645
646    >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
647    >>> from_tuple(list[Foo], lst)
648    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
649    """
650    return from_obj(
651        cls,
652        o,
653        named=False,
654        reuse_instances=reuse_instances,
655        deserialize_numbers=deserialize_numbers,
656    )

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)]