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