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