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