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