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