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