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