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