From 899bb7d7551872a8a3b430a4430bbc3c77673a80 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 6 Nov 2024 14:50:17 +0000 Subject: [PATCH] :octocat: Update docs for #603 --- api/serde.html | 204 ++--- api/serde/compat.html | 14 +- api/serde/core.html | 1972 +++++++++++++++++++++-------------------- api/serde/de.html | 4 +- api/serde/se.html | 4 +- 5 files changed, 1100 insertions(+), 1098 deletions(-) diff --git a/api/serde.html b/api/serde.html index fa0cc063..f97fe7ae 100644 --- a/api/serde.html +++ b/api/serde.html @@ -1553,14 +1553,14 @@

Modules

-
849def AdjacentTagging(
-850    tag: str, content: str, cls: Optional[T] = None
-851) -> Union[Tagging, _WithTagging[T]]:
-852    tagging = Tagging(tag, content, kind=Tagging.Kind.Adjacent)
-853    if cls:
-854        return tagging(cls)
-855    else:
-856        return tagging
+            
851def AdjacentTagging(
+852    tag: str, content: str, cls: Optional[T] = None
+853) -> Union[Tagging, _WithTagging[T]]:
+854    tagging = Tagging(tag, content, kind=Tagging.Kind.Adjacent)
+855    if cls:
+856        return tagging(cls)
+857    else:
+858        return tagging
 
@@ -1590,12 +1590,12 @@

Modules

-
833def InternalTagging(tag: str, cls: Optional[T] = None) -> Union[Tagging, _WithTagging[T]]:
-834    tagging = Tagging(tag, kind=Tagging.Kind.Internal)
-835    if cls:
-836        return tagging(cls)
-837    else:
-838        return tagging
+            
835def InternalTagging(tag: str, cls: Optional[T] = None) -> Union[Tagging, _WithTagging[T]]:
+836    tagging = Tagging(tag, kind=Tagging.Kind.Internal)
+837    if cls:
+838        return tagging(cls)
+839    else:
+840        return tagging
 
@@ -1661,48 +1661,48 @@

Modules

-
513def field(
-514    *args: Any,
-515    rename: Optional[str] = None,
-516    alias: Optional[list[str]] = None,
-517    skip: Optional[bool] = None,
-518    skip_if: Optional[Callable[[Any], Any]] = None,
-519    skip_if_false: Optional[bool] = None,
-520    skip_if_default: Optional[bool] = None,
-521    serializer: Optional[Callable[..., Any]] = None,
-522    deserializer: Optional[Callable[..., Any]] = None,
-523    flatten: Optional[Union[FlattenOpts, bool]] = None,
-524    metadata: Optional[dict[str, Any]] = None,
-525    **kwargs: Any,
-526) -> Any:
-527    """
-528    Declare a field with parameters.
-529    """
-530    if not metadata:
-531        metadata = {}
-532
-533    if rename is not None:
-534        metadata["serde_rename"] = rename
-535    if alias is not None:
-536        metadata["serde_alias"] = alias
-537    if skip is not None:
-538        metadata["serde_skip"] = skip
-539    if skip_if is not None:
-540        metadata["serde_skip_if"] = skip_if
-541    if skip_if_false is not None:
-542        metadata["serde_skip_if_false"] = skip_if_false
-543    if skip_if_default is not None:
-544        metadata["serde_skip_if_default"] = skip_if_default
-545    if serializer:
-546        metadata["serde_serializer"] = serializer
-547    if deserializer:
-548        metadata["serde_deserializer"] = deserializer
-549    if flatten is True:
-550        metadata["serde_flatten"] = FlattenOpts()
-551    elif flatten:
-552        metadata["serde_flatten"] = flatten
-553
-554    return dataclasses.field(*args, metadata=metadata, **kwargs)
+            
515def field(
+516    *args: Any,
+517    rename: Optional[str] = None,
+518    alias: Optional[list[str]] = None,
+519    skip: Optional[bool] = None,
+520    skip_if: Optional[Callable[[Any], Any]] = None,
+521    skip_if_false: Optional[bool] = None,
+522    skip_if_default: Optional[bool] = None,
+523    serializer: Optional[Callable[..., Any]] = None,
+524    deserializer: Optional[Callable[..., Any]] = None,
+525    flatten: Optional[Union[FlattenOpts, bool]] = None,
+526    metadata: Optional[dict[str, Any]] = None,
+527    **kwargs: Any,
+528) -> Any:
+529    """
+530    Declare a field with parameters.
+531    """
+532    if not metadata:
+533        metadata = {}
+534
+535    if rename is not None:
+536        metadata["serde_rename"] = rename
+537    if alias is not None:
+538        metadata["serde_alias"] = alias
+539    if skip is not None:
+540        metadata["serde_skip"] = skip
+541    if skip_if is not None:
+542        metadata["serde_skip_if"] = skip_if
+543    if skip_if_false is not None:
+544        metadata["serde_skip_if_false"] = skip_if_false
+545    if skip_if_default is not None:
+546        metadata["serde_skip_if_default"] = skip_if_default
+547    if serializer:
+548        metadata["serde_serializer"] = serializer
+549    if deserializer:
+550        metadata["serde_deserializer"] = deserializer
+551    if flatten is True:
+552        metadata["serde_flatten"] = FlattenOpts()
+553    elif flatten:
+554        metadata["serde_flatten"] = flatten
+555
+556    return dataclasses.field(*args, metadata=metadata, **kwargs)
 
@@ -1853,23 +1853,23 @@

Modules

-
 998class ClassSerializer(Protocol):
- 999    """
-1000    Interface for custom class serializer.
-1001
-1002    This protocol is intended to be used for custom class serializer.
+            
1000class ClassSerializer(Protocol):
+1001    """
+1002    Interface for custom class serializer.
 1003
-1004    >>> from datetime import datetime
-1005    >>> from serde import serde
-1006    >>> from plum import dispatch
-1007    >>> class MySerializer(ClassSerializer):
-1008    ...     @dispatch
-1009    ...     def serialize(self, value: datetime) -> str:
-1010    ...         return value.strftime("%d/%m/%y")
-1011    """
-1012
-1013    def serialize(self, value: Any) -> Any:
-1014        pass
+1004    This protocol is intended to be used for custom class serializer.
+1005
+1006    >>> from datetime import datetime
+1007    >>> from serde import serde
+1008    >>> from plum import dispatch
+1009    >>> class MySerializer(ClassSerializer):
+1010    ...     @dispatch
+1011    ...     def serialize(self, value: datetime) -> str:
+1012    ...         return value.strftime("%d/%m/%y")
+1013    """
+1014
+1015    def serialize(self, value: Any) -> Any:
+1016        pass
 
@@ -1944,8 +1944,8 @@

Modules

-
1013    def serialize(self, value: Any) -> Any:
-1014        pass
+            
1015    def serialize(self, value: Any) -> Any:
+1016        pass
 
@@ -1964,23 +1964,23 @@

Modules

-
1017class ClassDeserializer(Protocol):
-1018    """
-1019    Interface for custom class deserializer.
-1020
-1021    This protocol is intended to be used for custom class deserializer.
+            
1019class ClassDeserializer(Protocol):
+1020    """
+1021    Interface for custom class deserializer.
 1022
-1023    >>> from datetime import datetime
-1024    >>> from serde import serde
-1025    >>> from plum import dispatch
-1026    >>> class MyDeserializer(ClassDeserializer):
-1027    ...     @dispatch
-1028    ...     def deserialize(self, cls: type[datetime], value: Any) -> datetime:
-1029    ...         return datetime.strptime(value, "%d/%m/%y")
-1030    """
-1031
-1032    def deserialize(self, cls: Any, value: Any) -> Any:
-1033        pass
+1023    This protocol is intended to be used for custom class deserializer.
+1024
+1025    >>> from datetime import datetime
+1026    >>> from serde import serde
+1027    >>> from plum import dispatch
+1028    >>> class MyDeserializer(ClassDeserializer):
+1029    ...     @dispatch
+1030    ...     def deserialize(self, cls: type[datetime], value: Any) -> datetime:
+1031    ...         return datetime.strptime(value, "%d/%m/%y")
+1032    """
+1033
+1034    def deserialize(self, cls: Any, value: Any) -> Any:
+1035        pass
 
@@ -2055,8 +2055,8 @@

Modules

-
1032    def deserialize(self, cls: Any, value: Any) -> Any:
-1033        pass
+            
1034    def deserialize(self, cls: Any, value: Any) -> Any:
+1035        pass
 
@@ -2075,11 +2075,11 @@

Modules

-
1041def add_serializer(serializer: ClassSerializer) -> None:
-1042    """
-1043    Register custom global serializer.
-1044    """
-1045    GLOBAL_CLASS_SERIALIZER.append(serializer)
+            
1043def add_serializer(serializer: ClassSerializer) -> None:
+1044    """
+1045    Register custom global serializer.
+1046    """
+1047    GLOBAL_CLASS_SERIALIZER.append(serializer)
 
@@ -2099,11 +2099,11 @@

Modules

-
1048def add_deserializer(deserializer: ClassDeserializer) -> None:
-1049    """
-1050    Register custom global deserializer.
-1051    """
-1052    GLOBAL_CLASS_DESERIALIZER.append(deserializer)
+            
1050def add_deserializer(deserializer: ClassDeserializer) -> None:
+1051    """
+1052    Register custom global deserializer.
+1053    """
+1054    GLOBAL_CLASS_DESERIALIZER.append(deserializer)
 
diff --git a/api/serde/compat.html b/api/serde/compat.html index b0b1c69b..3fc76651 100644 --- a/api/serde/compat.html +++ b/api/serde/compat.html @@ -362,9 +362,9 @@

310 The correct return type is `Iterator[Union[Type, typing._specialform]], 311 but `typing._specialform` doesn't exist for python 3.6. Use `Any` instead. 312 """ -313 lst: set[type[Any]] = set() +313 lst: set[Union[type[Any], Any]] = set() 314 -315 def recursive(cls: type[Any]) -> None: +315 def recursive(cls: Union[type[Any], Any]) -> None: 316 if cls in lst: 317 return 318 @@ -375,12 +375,12 @@

323 elif isinstance(cls, str): 324 lst.add(cls) 325 elif is_opt(cls): -326 lst.add(Optional) # type: ignore +326 lst.add(Optional) 327 args = type_args(cls) 328 if args: 329 recursive(args[0]) 330 elif is_union(cls): -331 lst.add(Union) # type: ignore +331 lst.add(Union) 332 for arg in type_args(cls): 333 recursive(arg) 334 elif is_list(cls): @@ -455,9 +455,9 @@

403 """ 404 Iterate over all literals that are used in the dataclass 405 """ -406 lst: set[type[Any]] = set() +406 lst: set[Union[type[Any], Any]] = set() 407 -408 def recursive(cls: type[Any]) -> None: +408 def recursive(cls: Union[type[Any], Any]) -> None: 409 if cls in lst: 410 return 411 @@ -882,7 +882,7 @@

830 """ 831 Test if the type is `typing.Any`. 832 """ -833 return typ is Any +833 return typ is Any # type: ignore 834 835 836@cache diff --git a/api/serde/core.html b/api/serde/core.html index c5754983..14f38282 100644 --- a/api/serde/core.html +++ b/api/serde/core.html @@ -567,706 +567,708 @@

349 deeply check object against declared type. 350 """ 351 if dataclasses.is_dataclass(typ): - 352 return isinstance(obj, typ) - 353 elif is_opt(typ): - 354 return is_opt_instance(obj, typ) - 355 elif is_union(typ): - 356 return is_union_instance(obj, typ) - 357 elif is_list(typ): - 358 return is_list_instance(obj, typ) - 359 elif is_set(typ): - 360 return is_set_instance(obj, typ) - 361 elif is_tuple(typ): - 362 return is_tuple_instance(obj, typ) - 363 elif is_dict(typ): - 364 return is_dict_instance(obj, typ) - 365 elif is_generic(typ): - 366 return is_generic_instance(obj, typ) - 367 elif is_literal(typ): - 368 return True - 369 elif is_new_type_primitive(typ): - 370 inner = getattr(typ, "__supertype__", None) - 371 if type(inner) is type: - 372 return isinstance(obj, inner) - 373 else: - 374 return False - 375 elif is_any(typ): - 376 return True - 377 elif typ is Ellipsis: + 352 if not isinstance(typ, type): + 353 raise SerdeError("expect dataclass class but dataclass instance received") + 354 return isinstance(obj, typ) + 355 elif is_opt(typ): + 356 return is_opt_instance(obj, typ) + 357 elif is_union(typ): + 358 return is_union_instance(obj, typ) + 359 elif is_list(typ): + 360 return is_list_instance(obj, typ) + 361 elif is_set(typ): + 362 return is_set_instance(obj, typ) + 363 elif is_tuple(typ): + 364 return is_tuple_instance(obj, typ) + 365 elif is_dict(typ): + 366 return is_dict_instance(obj, typ) + 367 elif is_generic(typ): + 368 return is_generic_instance(obj, typ) + 369 elif is_literal(typ): + 370 return True + 371 elif is_new_type_primitive(typ): + 372 inner = getattr(typ, "__supertype__", None) + 373 if type(inner) is type: + 374 return isinstance(obj, inner) + 375 else: + 376 return False + 377 elif is_any(typ): 378 return True - 379 else: - 380 return is_bearable(obj, typ) - 381 - 382 - 383def is_opt_instance(obj: Any, typ: type[Any]) -> bool: - 384 if obj is None: - 385 return True - 386 opt_arg = type_args(typ)[0] - 387 return is_instance(obj, opt_arg) - 388 - 389 - 390def is_union_instance(obj: Any, typ: type[Any]) -> bool: - 391 for arg in type_args(typ): - 392 if is_instance(obj, arg): - 393 return True - 394 return False - 395 - 396 - 397def is_list_instance(obj: Any, typ: type[Any]) -> bool: - 398 if not isinstance(obj, list): - 399 return False - 400 if len(obj) == 0 or is_bare_list(typ): - 401 return True - 402 list_arg = type_args(typ)[0] - 403 # for speed reasons we just check the type of the 1st element - 404 return is_instance(obj[0], list_arg) - 405 - 406 - 407def is_set_instance(obj: Any, typ: type[Any]) -> bool: - 408 if not isinstance(obj, (set, frozenset)): - 409 return False - 410 if len(obj) == 0 or is_bare_set(typ): - 411 return True - 412 set_arg = type_args(typ)[0] - 413 # for speed reasons we just check the type of the 1st element - 414 return is_instance(next(iter(obj)), set_arg) - 415 - 416 - 417def is_tuple_instance(obj: Any, typ: type[Any]) -> bool: - 418 args = type_args(typ) - 419 - 420 if not isinstance(obj, tuple): - 421 return False - 422 - 423 # empty tuple - 424 if len(args) == 0 and len(obj) == 0: - 425 return True - 426 - 427 # In the form of tuple[T, ...] - 428 elif is_variable_tuple(typ): - 429 # Get the first type arg. Since tuple[T, ...] is homogeneous tuple, - 430 # all the elements should be of this type. - 431 arg = type_args(typ)[0] - 432 for v in obj: - 433 if not is_instance(v, arg): - 434 return False - 435 return True - 436 - 437 # bare tuple "tuple" is equivalent to tuple[Any, ...] - 438 if is_bare_tuple(typ) and isinstance(obj, tuple): - 439 return True - 440 - 441 # All the other tuples e.g. tuple[int, str] - 442 if len(obj) == len(args): - 443 for element, arg in zip(obj, args): - 444 if not is_instance(element, arg): - 445 return False - 446 else: - 447 return False - 448 - 449 return True + 379 elif typ is Ellipsis: + 380 return True + 381 else: + 382 return is_bearable(obj, typ) + 383 + 384 + 385def is_opt_instance(obj: Any, typ: type[Any]) -> bool: + 386 if obj is None: + 387 return True + 388 opt_arg = type_args(typ)[0] + 389 return is_instance(obj, opt_arg) + 390 + 391 + 392def is_union_instance(obj: Any, typ: type[Any]) -> bool: + 393 for arg in type_args(typ): + 394 if is_instance(obj, arg): + 395 return True + 396 return False + 397 + 398 + 399def is_list_instance(obj: Any, typ: type[Any]) -> bool: + 400 if not isinstance(obj, list): + 401 return False + 402 if len(obj) == 0 or is_bare_list(typ): + 403 return True + 404 list_arg = type_args(typ)[0] + 405 # for speed reasons we just check the type of the 1st element + 406 return is_instance(obj[0], list_arg) + 407 + 408 + 409def is_set_instance(obj: Any, typ: type[Any]) -> bool: + 410 if not isinstance(obj, (set, frozenset)): + 411 return False + 412 if len(obj) == 0 or is_bare_set(typ): + 413 return True + 414 set_arg = type_args(typ)[0] + 415 # for speed reasons we just check the type of the 1st element + 416 return is_instance(next(iter(obj)), set_arg) + 417 + 418 + 419def is_tuple_instance(obj: Any, typ: type[Any]) -> bool: + 420 args = type_args(typ) + 421 + 422 if not isinstance(obj, tuple): + 423 return False + 424 + 425 # empty tuple + 426 if len(args) == 0 and len(obj) == 0: + 427 return True + 428 + 429 # In the form of tuple[T, ...] + 430 elif is_variable_tuple(typ): + 431 # Get the first type arg. Since tuple[T, ...] is homogeneous tuple, + 432 # all the elements should be of this type. + 433 arg = type_args(typ)[0] + 434 for v in obj: + 435 if not is_instance(v, arg): + 436 return False + 437 return True + 438 + 439 # bare tuple "tuple" is equivalent to tuple[Any, ...] + 440 if is_bare_tuple(typ) and isinstance(obj, tuple): + 441 return True + 442 + 443 # All the other tuples e.g. tuple[int, str] + 444 if len(obj) == len(args): + 445 for element, arg in zip(obj, args): + 446 if not is_instance(element, arg): + 447 return False + 448 else: + 449 return False 450 - 451 - 452def is_dict_instance(obj: Any, typ: type[Any]) -> bool: - 453 if not isinstance(obj, dict): - 454 return False - 455 if len(obj) == 0 or is_bare_dict(typ): - 456 return True - 457 ktyp = type_args(typ)[0] - 458 vtyp = type_args(typ)[1] - 459 for k, v in obj.items(): - 460 # for speed reasons we just check the type of the 1st element - 461 return is_instance(k, ktyp) and is_instance(v, vtyp) - 462 return False - 463 - 464 - 465def is_generic_instance(obj: Any, typ: type[Any]) -> bool: - 466 return is_instance(obj, get_origin(typ)) - 467 - 468 - 469@dataclass - 470class Func: - 471 """ - 472 Function wrapper that provides `mangled` optional field. - 473 - 474 pyserde copies every function reference into global scope - 475 for code generation. Mangling function names is needed in - 476 order to avoid name conflict in the global scope when - 477 multiple fields receives `skip_if` attribute. - 478 """ - 479 - 480 inner: Callable[..., Any] - 481 """ Function to wrap in """ - 482 - 483 mangeld: str = "" - 484 """ Mangled function name """ - 485 - 486 def __call__(self, v: Any) -> None: - 487 return self.inner(v) # type: ignore - 488 - 489 @property - 490 def name(self) -> str: - 491 """ - 492 Mangled function name - 493 """ - 494 return self.mangeld - 495 - 496 - 497def skip_if_false(v: Any) -> Any: - 498 return not bool(v) - 499 - 500 - 501def skip_if_default(v: Any, default: Optional[Any] = None) -> Any: - 502 return v == default # Why return type is deduced to be Any? - 503 - 504 - 505@dataclass - 506class FlattenOpts: - 507 """ - 508 Flatten options. Currently not used. - 509 """ - 510 - 511 - 512def field( - 513 *args: Any, - 514 rename: Optional[str] = None, - 515 alias: Optional[list[str]] = None, - 516 skip: Optional[bool] = None, - 517 skip_if: Optional[Callable[[Any], Any]] = None, - 518 skip_if_false: Optional[bool] = None, - 519 skip_if_default: Optional[bool] = None, - 520 serializer: Optional[Callable[..., Any]] = None, - 521 deserializer: Optional[Callable[..., Any]] = None, - 522 flatten: Optional[Union[FlattenOpts, bool]] = None, - 523 metadata: Optional[dict[str, Any]] = None, - 524 **kwargs: Any, - 525) -> Any: - 526 """ - 527 Declare a field with parameters. - 528 """ - 529 if not metadata: - 530 metadata = {} - 531 - 532 if rename is not None: - 533 metadata["serde_rename"] = rename - 534 if alias is not None: - 535 metadata["serde_alias"] = alias - 536 if skip is not None: - 537 metadata["serde_skip"] = skip - 538 if skip_if is not None: - 539 metadata["serde_skip_if"] = skip_if - 540 if skip_if_false is not None: - 541 metadata["serde_skip_if_false"] = skip_if_false - 542 if skip_if_default is not None: - 543 metadata["serde_skip_if_default"] = skip_if_default - 544 if serializer: - 545 metadata["serde_serializer"] = serializer - 546 if deserializer: - 547 metadata["serde_deserializer"] = deserializer - 548 if flatten is True: - 549 metadata["serde_flatten"] = FlattenOpts() - 550 elif flatten: - 551 metadata["serde_flatten"] = flatten - 552 - 553 return dataclasses.field(*args, metadata=metadata, **kwargs) + 451 return True + 452 + 453 + 454def is_dict_instance(obj: Any, typ: type[Any]) -> bool: + 455 if not isinstance(obj, dict): + 456 return False + 457 if len(obj) == 0 or is_bare_dict(typ): + 458 return True + 459 ktyp = type_args(typ)[0] + 460 vtyp = type_args(typ)[1] + 461 for k, v in obj.items(): + 462 # for speed reasons we just check the type of the 1st element + 463 return is_instance(k, ktyp) and is_instance(v, vtyp) + 464 return False + 465 + 466 + 467def is_generic_instance(obj: Any, typ: type[Any]) -> bool: + 468 return is_instance(obj, get_origin(typ)) + 469 + 470 + 471@dataclass + 472class Func: + 473 """ + 474 Function wrapper that provides `mangled` optional field. + 475 + 476 pyserde copies every function reference into global scope + 477 for code generation. Mangling function names is needed in + 478 order to avoid name conflict in the global scope when + 479 multiple fields receives `skip_if` attribute. + 480 """ + 481 + 482 inner: Callable[..., Any] + 483 """ Function to wrap in """ + 484 + 485 mangeld: str = "" + 486 """ Mangled function name """ + 487 + 488 def __call__(self, v: Any) -> None: + 489 return self.inner(v) # type: ignore + 490 + 491 @property + 492 def name(self) -> str: + 493 """ + 494 Mangled function name + 495 """ + 496 return self.mangeld + 497 + 498 + 499def skip_if_false(v: Any) -> Any: + 500 return not bool(v) + 501 + 502 + 503def skip_if_default(v: Any, default: Optional[Any] = None) -> Any: + 504 return v == default # Why return type is deduced to be Any? + 505 + 506 + 507@dataclass + 508class FlattenOpts: + 509 """ + 510 Flatten options. Currently not used. + 511 """ + 512 + 513 + 514def field( + 515 *args: Any, + 516 rename: Optional[str] = None, + 517 alias: Optional[list[str]] = None, + 518 skip: Optional[bool] = None, + 519 skip_if: Optional[Callable[[Any], Any]] = None, + 520 skip_if_false: Optional[bool] = None, + 521 skip_if_default: Optional[bool] = None, + 522 serializer: Optional[Callable[..., Any]] = None, + 523 deserializer: Optional[Callable[..., Any]] = None, + 524 flatten: Optional[Union[FlattenOpts, bool]] = None, + 525 metadata: Optional[dict[str, Any]] = None, + 526 **kwargs: Any, + 527) -> Any: + 528 """ + 529 Declare a field with parameters. + 530 """ + 531 if not metadata: + 532 metadata = {} + 533 + 534 if rename is not None: + 535 metadata["serde_rename"] = rename + 536 if alias is not None: + 537 metadata["serde_alias"] = alias + 538 if skip is not None: + 539 metadata["serde_skip"] = skip + 540 if skip_if is not None: + 541 metadata["serde_skip_if"] = skip_if + 542 if skip_if_false is not None: + 543 metadata["serde_skip_if_false"] = skip_if_false + 544 if skip_if_default is not None: + 545 metadata["serde_skip_if_default"] = skip_if_default + 546 if serializer: + 547 metadata["serde_serializer"] = serializer + 548 if deserializer: + 549 metadata["serde_deserializer"] = deserializer + 550 if flatten is True: + 551 metadata["serde_flatten"] = FlattenOpts() + 552 elif flatten: + 553 metadata["serde_flatten"] = flatten 554 - 555 - 556@dataclass - 557class Field(Generic[T]): - 558 """ - 559 Field class is similar to `dataclasses.Field`. It provides pyserde specific options. - 560 - 561 `type`, `name`, `default` and `default_factory` are the same members as `dataclasses.Field`. - 562 """ - 563 - 564 type: type[T] - 565 """ Type of Field """ - 566 name: Optional[str] - 567 """ Name of Field """ - 568 default: Any = field(default_factory=dataclasses._MISSING_TYPE) - 569 """ Default value of Field """ - 570 default_factory: Any = field(default_factory=dataclasses._MISSING_TYPE) - 571 """ Default factory method of Field """ - 572 init: bool = field(default_factory=dataclasses._MISSING_TYPE) - 573 repr: Any = field(default_factory=dataclasses._MISSING_TYPE) - 574 hash: Any = field(default_factory=dataclasses._MISSING_TYPE) - 575 compare: Any = field(default_factory=dataclasses._MISSING_TYPE) - 576 metadata: Mapping[str, Any] = field(default_factory=dict) - 577 kw_only: bool = False - 578 case: Optional[str] = None - 579 alias: list[str] = field(default_factory=list) - 580 rename: Optional[str] = None - 581 skip: Optional[bool] = None - 582 skip_if: Optional[Func] = None - 583 skip_if_false: Optional[bool] = None - 584 skip_if_default: Optional[bool] = None - 585 serializer: Optional[Func] = None # Custom field serializer. - 586 deserializer: Optional[Func] = None # Custom field deserializer. - 587 flatten: Optional[FlattenOpts] = None - 588 parent: Optional[Any] = None - 589 type_args: Optional[list[str]] = None - 590 - 591 @classmethod - 592 def from_dataclass(cls, f: dataclasses.Field[T], parent: Optional[Any] = None) -> Field[T]: - 593 """ - 594 Create `Field` object from `dataclasses.Field`. - 595 """ - 596 skip_if_false_func: Optional[Func] = None - 597 if f.metadata.get("serde_skip_if_false"): - 598 skip_if_false_func = Func(skip_if_false, cls.mangle(f, "skip_if_false")) - 599 - 600 skip_if_default_func: Optional[Func] = None - 601 if f.metadata.get("serde_skip_if_default"): - 602 skip_if_def = functools.partial(skip_if_default, default=f.default) - 603 skip_if_default_func = Func(skip_if_def, cls.mangle(f, "skip_if_default")) - 604 - 605 skip_if: Optional[Func] = None - 606 if f.metadata.get("serde_skip_if"): - 607 func = f.metadata.get("serde_skip_if") - 608 if callable(func): - 609 skip_if = Func(func, cls.mangle(f, "skip_if")) - 610 - 611 serializer: Optional[Func] = None - 612 func = f.metadata.get("serde_serializer") - 613 if func: - 614 serializer = Func(func, cls.mangle(f, "serializer")) - 615 - 616 deserializer: Optional[Func] = None - 617 func = f.metadata.get("serde_deserializer") - 618 if func: - 619 deserializer = Func(func, cls.mangle(f, "deserializer")) - 620 - 621 flatten = f.metadata.get("serde_flatten") - 622 if flatten is True: - 623 flatten = FlattenOpts() - 624 if flatten and not (dataclasses.is_dataclass(f.type) or is_opt_dataclass(f.type)): - 625 raise SerdeError(f"pyserde does not support flatten attribute for {typename(f.type)}") - 626 - 627 kw_only = bool(f.kw_only) if sys.version_info >= (3, 10) else False + 555 return dataclasses.field(*args, metadata=metadata, **kwargs) + 556 + 557 + 558@dataclass + 559class Field(Generic[T]): + 560 """ + 561 Field class is similar to `dataclasses.Field`. It provides pyserde specific options. + 562 + 563 `type`, `name`, `default` and `default_factory` are the same members as `dataclasses.Field`. + 564 """ + 565 + 566 type: type[T] + 567 """ Type of Field """ + 568 name: Optional[str] + 569 """ Name of Field """ + 570 default: Any = field(default_factory=dataclasses._MISSING_TYPE) + 571 """ Default value of Field """ + 572 default_factory: Any = field(default_factory=dataclasses._MISSING_TYPE) + 573 """ Default factory method of Field """ + 574 init: bool = field(default_factory=dataclasses._MISSING_TYPE) + 575 repr: Any = field(default_factory=dataclasses._MISSING_TYPE) + 576 hash: Any = field(default_factory=dataclasses._MISSING_TYPE) + 577 compare: Any = field(default_factory=dataclasses._MISSING_TYPE) + 578 metadata: Mapping[str, Any] = field(default_factory=dict) + 579 kw_only: bool = False + 580 case: Optional[str] = None + 581 alias: list[str] = field(default_factory=list) + 582 rename: Optional[str] = None + 583 skip: Optional[bool] = None + 584 skip_if: Optional[Func] = None + 585 skip_if_false: Optional[bool] = None + 586 skip_if_default: Optional[bool] = None + 587 serializer: Optional[Func] = None # Custom field serializer. + 588 deserializer: Optional[Func] = None # Custom field deserializer. + 589 flatten: Optional[FlattenOpts] = None + 590 parent: Optional[Any] = None + 591 type_args: Optional[list[str]] = None + 592 + 593 @classmethod + 594 def from_dataclass(cls, f: dataclasses.Field[T], parent: Optional[Any] = None) -> Field[T]: + 595 """ + 596 Create `Field` object from `dataclasses.Field`. + 597 """ + 598 skip_if_false_func: Optional[Func] = None + 599 if f.metadata.get("serde_skip_if_false"): + 600 skip_if_false_func = Func(skip_if_false, cls.mangle(f, "skip_if_false")) + 601 + 602 skip_if_default_func: Optional[Func] = None + 603 if f.metadata.get("serde_skip_if_default"): + 604 skip_if_def = functools.partial(skip_if_default, default=f.default) + 605 skip_if_default_func = Func(skip_if_def, cls.mangle(f, "skip_if_default")) + 606 + 607 skip_if: Optional[Func] = None + 608 if f.metadata.get("serde_skip_if"): + 609 func = f.metadata.get("serde_skip_if") + 610 if callable(func): + 611 skip_if = Func(func, cls.mangle(f, "skip_if")) + 612 + 613 serializer: Optional[Func] = None + 614 func = f.metadata.get("serde_serializer") + 615 if func: + 616 serializer = Func(func, cls.mangle(f, "serializer")) + 617 + 618 deserializer: Optional[Func] = None + 619 func = f.metadata.get("serde_deserializer") + 620 if func: + 621 deserializer = Func(func, cls.mangle(f, "deserializer")) + 622 + 623 flatten = f.metadata.get("serde_flatten") + 624 if flatten is True: + 625 flatten = FlattenOpts() + 626 if flatten and not (dataclasses.is_dataclass(f.type) or is_opt_dataclass(f.type)): + 627 raise SerdeError(f"pyserde does not support flatten attribute for {typename(f.type)}") 628 - 629 return cls( - 630 f.type, - 631 f.name, - 632 default=f.default, - 633 default_factory=f.default_factory, - 634 init=f.init, - 635 repr=f.repr, - 636 hash=f.hash, - 637 compare=f.compare, - 638 metadata=f.metadata, - 639 rename=f.metadata.get("serde_rename"), - 640 alias=f.metadata.get("serde_alias", []), - 641 skip=f.metadata.get("serde_skip"), - 642 skip_if=skip_if or skip_if_false_func or skip_if_default_func, - 643 serializer=serializer, - 644 deserializer=deserializer, - 645 flatten=flatten, - 646 parent=parent, - 647 kw_only=kw_only, - 648 ) - 649 - 650 def to_dataclass(self) -> dataclasses.Field[T]: - 651 f = dataclasses.Field( - 652 default=self.default, - 653 default_factory=self.default_factory, - 654 init=self.init, - 655 repr=self.repr, - 656 hash=self.hash, - 657 compare=self.compare, - 658 metadata=self.metadata, - 659 kw_only=self.kw_only, - 660 ) - 661 assert self.name - 662 f.name = self.name - 663 f.type = self.type - 664 return f - 665 - 666 def is_self_referencing(self) -> bool: - 667 if self.type is None: - 668 return False - 669 if self.parent is None: + 629 kw_only = bool(f.kw_only) if sys.version_info >= (3, 10) else False + 630 + 631 return cls( + 632 f.type, # type: ignore + 633 f.name, + 634 default=f.default, + 635 default_factory=f.default_factory, + 636 init=f.init, + 637 repr=f.repr, + 638 hash=f.hash, + 639 compare=f.compare, + 640 metadata=f.metadata, + 641 rename=f.metadata.get("serde_rename"), + 642 alias=f.metadata.get("serde_alias", []), + 643 skip=f.metadata.get("serde_skip"), + 644 skip_if=skip_if or skip_if_false_func or skip_if_default_func, + 645 serializer=serializer, + 646 deserializer=deserializer, + 647 flatten=flatten, + 648 parent=parent, + 649 kw_only=kw_only, + 650 ) + 651 + 652 def to_dataclass(self) -> dataclasses.Field[T]: + 653 f = dataclasses.Field( + 654 default=self.default, + 655 default_factory=self.default_factory, + 656 init=self.init, + 657 repr=self.repr, + 658 hash=self.hash, + 659 compare=self.compare, + 660 metadata=self.metadata, + 661 kw_only=self.kw_only, + 662 ) + 663 assert self.name + 664 f.name = self.name + 665 f.type = self.type + 666 return f + 667 + 668 def is_self_referencing(self) -> bool: + 669 if self.type is None: 670 return False - 671 return self.type == self.parent # type: ignore - 672 - 673 @staticmethod - 674 def mangle(field: dataclasses.Field[Any], name: str) -> str: - 675 """ - 676 Get mangled name based on field name. - 677 """ - 678 return f"{field.name}_{name}" - 679 - 680 def conv_name(self, case: Optional[str] = None) -> str: - 681 """ - 682 Get an actual field name which `rename` and `rename_all` conversions - 683 are made. Use `name` property to get a field name before conversion. - 684 """ - 685 return conv(self, case or self.case) - 686 - 687 def supports_default(self) -> bool: - 688 return not getattr(self, "iterbased", False) and ( - 689 has_default(self) or has_default_factory(self) - 690 ) - 691 - 692 - 693F = TypeVar("F", bound=Field[Any]) + 671 if self.parent is None: + 672 return False + 673 return self.type == self.parent # type: ignore + 674 + 675 @staticmethod + 676 def mangle(field: dataclasses.Field[Any], name: str) -> str: + 677 """ + 678 Get mangled name based on field name. + 679 """ + 680 return f"{field.name}_{name}" + 681 + 682 def conv_name(self, case: Optional[str] = None) -> str: + 683 """ + 684 Get an actual field name which `rename` and `rename_all` conversions + 685 are made. Use `name` property to get a field name before conversion. + 686 """ + 687 return conv(self, case or self.case) + 688 + 689 def supports_default(self) -> bool: + 690 return not getattr(self, "iterbased", False) and ( + 691 has_default(self) or has_default_factory(self) + 692 ) + 693 694 - 695 - 696def fields(field_cls: type[F], cls: type[Any], serialize_class_var: bool = False) -> list[F]: - 697 """ - 698 Iterate fields of the dataclass and returns `serde.core.Field`. - 699 """ - 700 fields = [field_cls.from_dataclass(f, parent=cls) for f in dataclass_fields(cls)] - 701 - 702 if serialize_class_var: - 703 for name, typ in get_type_hints(cls).items(): - 704 if is_class_var(typ): - 705 fields.append(field_cls(typ, name, default=getattr(cls, name))) - 706 - 707 return fields # type: ignore + 695F = TypeVar("F", bound=Field[Any]) + 696 + 697 + 698def fields(field_cls: type[F], cls: type[Any], serialize_class_var: bool = False) -> list[F]: + 699 """ + 700 Iterate fields of the dataclass and returns `serde.core.Field`. + 701 """ + 702 fields = [field_cls.from_dataclass(f, parent=cls) for f in dataclass_fields(cls)] + 703 + 704 if serialize_class_var: + 705 for name, typ in get_type_hints(cls).items(): + 706 if is_class_var(typ): + 707 fields.append(field_cls(typ, name, default=getattr(cls, name))) 708 - 709 - 710def conv(f: Field[Any], case: Optional[str] = None) -> str: - 711 """ - 712 Convert field name. - 713 """ - 714 name = f.name - 715 if case: - 716 casef = getattr(casefy, case, None) - 717 if not casef: - 718 raise SerdeError( - 719 f"Unkown case type: {f.case}. Pass the name of case supported by 'casefy' package." - 720 ) - 721 name = casef(name) - 722 if f.rename: - 723 name = f.rename - 724 if name is None: - 725 raise SerdeError("Field name is None.") - 726 return name - 727 - 728 - 729def union_func_name(prefix: str, union_args: Sequence[Any]) -> str: - 730 """ - 731 Generate a function name that contains all union types - 732 - 733 * `prefix` prefix to distinguish between serializing and deserializing - 734 * `union_args`: type arguments of a Union - 735 - 736 >>> from ipaddress import IPv4Address - 737 >>> union_func_name("union_se", [int, list[str], IPv4Address]) - 738 'union_se_int_list_str__IPv4Address' - 739 """ - 740 return re.sub(r"[^A-Za-z0-9]", "_", f"{prefix}_{'_'.join([typename(e) for e in union_args])}") - 741 - 742 - 743def literal_func_name(literal_args: Sequence[Any]) -> str: - 744 """ - 745 Generate a function name with all literals and corresponding types specified with Literal[...] - 746 - 747 - 748 * `literal_args`: arguments of a Literal + 709 return fields # type: ignore + 710 + 711 + 712def conv(f: Field[Any], case: Optional[str] = None) -> str: + 713 """ + 714 Convert field name. + 715 """ + 716 name = f.name + 717 if case: + 718 casef = getattr(casefy, case, None) + 719 if not casef: + 720 raise SerdeError( + 721 f"Unkown case type: {f.case}. Pass the name of case supported by 'casefy' package." + 722 ) + 723 name = casef(name) + 724 if f.rename: + 725 name = f.rename + 726 if name is None: + 727 raise SerdeError("Field name is None.") + 728 return name + 729 + 730 + 731def union_func_name(prefix: str, union_args: Sequence[Any]) -> str: + 732 """ + 733 Generate a function name that contains all union types + 734 + 735 * `prefix` prefix to distinguish between serializing and deserializing + 736 * `union_args`: type arguments of a Union + 737 + 738 >>> from ipaddress import IPv4Address + 739 >>> union_func_name("union_se", [int, list[str], IPv4Address]) + 740 'union_se_int_list_str__IPv4Address' + 741 """ + 742 return re.sub(r"[^A-Za-z0-9]", "_", f"{prefix}_{'_'.join([typename(e) for e in union_args])}") + 743 + 744 + 745def literal_func_name(literal_args: Sequence[Any]) -> str: + 746 """ + 747 Generate a function name with all literals and corresponding types specified with Literal[...] + 748 749 - 750 >>> literal_func_name(["r", "w", "a", "x", "r+", "w+", "a+", "x+"]) - 751 'literal_de_r_str_w_str_a_str_x_str_r__str_w__str_a__str_x__str' - 752 """ - 753 return re.sub( - 754 r"[^A-Za-z0-9]", - 755 "_", - 756 f"{LITERAL_DE_PREFIX}_{'_'.join(f'{a}_{typename(type(a))}' for a in literal_args)}", - 757 ) - 758 - 759 - 760@dataclass(unsafe_hash=True) - 761class Tagging: - 762 """ - 763 Controls how union is (de)serialized. This is the same concept as in - 764 https://serde.rs/enum-representations.html - 765 """ - 766 - 767 class Kind(enum.Enum): - 768 External = enum.auto() - 769 Internal = enum.auto() - 770 Adjacent = enum.auto() - 771 Untagged = enum.auto() - 772 - 773 tag: Optional[str] = None - 774 content: Optional[str] = None - 775 kind: Kind = Kind.External - 776 - 777 def is_external(self) -> bool: - 778 return self.kind == self.Kind.External - 779 - 780 def is_internal(self) -> bool: - 781 return self.kind == self.Kind.Internal - 782 - 783 def is_adjacent(self) -> bool: - 784 return self.kind == self.Kind.Adjacent - 785 - 786 def is_untagged(self) -> bool: - 787 return self.kind == self.Kind.Untagged - 788 - 789 @classmethod - 790 def is_taggable(cls, typ: type[Any]) -> bool: - 791 return dataclasses.is_dataclass(typ) - 792 - 793 def check(self) -> None: - 794 if self.is_internal() and self.tag is None: - 795 raise SerdeError('"tag" must be specified in InternalTagging') - 796 if self.is_adjacent() and (self.tag is None or self.content is None): - 797 raise SerdeError('"tag" and "content" must be specified in AdjacentTagging') - 798 - 799 def produce_unique_class_name(self) -> str: - 800 """ - 801 Produce a unique class name for this tagging. The name is used for generated - 802 wrapper dataclass and stored in `Cache`. - 803 """ - 804 if self.is_internal(): - 805 tag = casefy.pascalcase(self.tag) # type: ignore - 806 if not tag: - 807 raise SerdeError('"tag" must be specified in InternalTagging') - 808 return f"Internal{tag}" - 809 elif self.is_adjacent(): - 810 tag = casefy.pascalcase(self.tag) # type: ignore - 811 content = casefy.pascalcase(self.content) # type: ignore - 812 if not tag: - 813 raise SerdeError('"tag" must be specified in AdjacentTagging') - 814 if not content: - 815 raise SerdeError('"content" must be specified in AdjacentTagging') - 816 return f"Adjacent{tag}{content}" - 817 else: - 818 return self.kind.name - 819 - 820 def __call__(self, cls: T) -> _WithTagging[T]: - 821 return _WithTagging(cls, self) - 822 - 823 - 824@overload - 825def InternalTagging(tag: str) -> Tagging: ... - 826 - 827 - 828@overload - 829def InternalTagging(tag: str, cls: T) -> _WithTagging[T]: ... - 830 - 831 - 832def InternalTagging(tag: str, cls: Optional[T] = None) -> Union[Tagging, _WithTagging[T]]: - 833 tagging = Tagging(tag, kind=Tagging.Kind.Internal) - 834 if cls: - 835 return tagging(cls) - 836 else: - 837 return tagging - 838 - 839 - 840@overload - 841def AdjacentTagging(tag: str, content: str) -> Tagging: ... - 842 - 843 - 844@overload - 845def AdjacentTagging(tag: str, content: str, cls: T) -> _WithTagging[T]: ... - 846 - 847 - 848def AdjacentTagging( - 849 tag: str, content: str, cls: Optional[T] = None - 850) -> Union[Tagging, _WithTagging[T]]: - 851 tagging = Tagging(tag, content, kind=Tagging.Kind.Adjacent) - 852 if cls: - 853 return tagging(cls) - 854 else: - 855 return tagging - 856 - 857 - 858ExternalTagging = Tagging() + 750 * `literal_args`: arguments of a Literal + 751 + 752 >>> literal_func_name(["r", "w", "a", "x", "r+", "w+", "a+", "x+"]) + 753 'literal_de_r_str_w_str_a_str_x_str_r__str_w__str_a__str_x__str' + 754 """ + 755 return re.sub( + 756 r"[^A-Za-z0-9]", + 757 "_", + 758 f"{LITERAL_DE_PREFIX}_{'_'.join(f'{a}_{typename(type(a))}' for a in literal_args)}", + 759 ) + 760 + 761 + 762@dataclass(unsafe_hash=True) + 763class Tagging: + 764 """ + 765 Controls how union is (de)serialized. This is the same concept as in + 766 https://serde.rs/enum-representations.html + 767 """ + 768 + 769 class Kind(enum.Enum): + 770 External = enum.auto() + 771 Internal = enum.auto() + 772 Adjacent = enum.auto() + 773 Untagged = enum.auto() + 774 + 775 tag: Optional[str] = None + 776 content: Optional[str] = None + 777 kind: Kind = Kind.External + 778 + 779 def is_external(self) -> bool: + 780 return self.kind == self.Kind.External + 781 + 782 def is_internal(self) -> bool: + 783 return self.kind == self.Kind.Internal + 784 + 785 def is_adjacent(self) -> bool: + 786 return self.kind == self.Kind.Adjacent + 787 + 788 def is_untagged(self) -> bool: + 789 return self.kind == self.Kind.Untagged + 790 + 791 @classmethod + 792 def is_taggable(cls, typ: type[Any]) -> bool: + 793 return dataclasses.is_dataclass(typ) + 794 + 795 def check(self) -> None: + 796 if self.is_internal() and self.tag is None: + 797 raise SerdeError('"tag" must be specified in InternalTagging') + 798 if self.is_adjacent() and (self.tag is None or self.content is None): + 799 raise SerdeError('"tag" and "content" must be specified in AdjacentTagging') + 800 + 801 def produce_unique_class_name(self) -> str: + 802 """ + 803 Produce a unique class name for this tagging. The name is used for generated + 804 wrapper dataclass and stored in `Cache`. + 805 """ + 806 if self.is_internal(): + 807 tag = casefy.pascalcase(self.tag) # type: ignore + 808 if not tag: + 809 raise SerdeError('"tag" must be specified in InternalTagging') + 810 return f"Internal{tag}" + 811 elif self.is_adjacent(): + 812 tag = casefy.pascalcase(self.tag) # type: ignore + 813 content = casefy.pascalcase(self.content) # type: ignore + 814 if not tag: + 815 raise SerdeError('"tag" must be specified in AdjacentTagging') + 816 if not content: + 817 raise SerdeError('"content" must be specified in AdjacentTagging') + 818 return f"Adjacent{tag}{content}" + 819 else: + 820 return self.kind.name + 821 + 822 def __call__(self, cls: T) -> _WithTagging[T]: + 823 return _WithTagging(cls, self) + 824 + 825 + 826@overload + 827def InternalTagging(tag: str) -> Tagging: ... + 828 + 829 + 830@overload + 831def InternalTagging(tag: str, cls: T) -> _WithTagging[T]: ... + 832 + 833 + 834def InternalTagging(tag: str, cls: Optional[T] = None) -> Union[Tagging, _WithTagging[T]]: + 835 tagging = Tagging(tag, kind=Tagging.Kind.Internal) + 836 if cls: + 837 return tagging(cls) + 838 else: + 839 return tagging + 840 + 841 + 842@overload + 843def AdjacentTagging(tag: str, content: str) -> Tagging: ... + 844 + 845 + 846@overload + 847def AdjacentTagging(tag: str, content: str, cls: T) -> _WithTagging[T]: ... + 848 + 849 + 850def AdjacentTagging( + 851 tag: str, content: str, cls: Optional[T] = None + 852) -> Union[Tagging, _WithTagging[T]]: + 853 tagging = Tagging(tag, content, kind=Tagging.Kind.Adjacent) + 854 if cls: + 855 return tagging(cls) + 856 else: + 857 return tagging + 858 859 - 860Untagged = Tagging(kind=Tagging.Kind.Untagged) + 860ExternalTagging = Tagging() 861 - 862 - 863DefaultTagging = ExternalTagging + 862Untagged = Tagging(kind=Tagging.Kind.Untagged) + 863 864 - 865 - 866def ensure(expr: Any, description: str) -> None: - 867 if not expr: - 868 raise Exception(description) - 869 - 870 - 871def should_impl_dataclass(cls: type[Any]) -> bool: - 872 """ - 873 Test if class doesn't have @dataclass. - 874 - 875 `dataclasses.is_dataclass` returns True even Derived class doesn't actually @dataclass. - 876 >>> @dataclasses.dataclass - 877 ... class Base: - 878 ... a: int - 879 >>> class Derived(Base): - 880 ... b: int - 881 >>> dataclasses.is_dataclass(Derived) - 882 True - 883 - 884 This function tells the class actually have @dataclass or not. - 885 >>> should_impl_dataclass(Base) - 886 False - 887 >>> should_impl_dataclass(Derived) - 888 True - 889 """ - 890 if not dataclasses.is_dataclass(cls): - 891 return True - 892 - 893 # Checking is_dataclass is not enough in such a case that the class is inherited - 894 # from another dataclass. To do it correctly, check if all fields in __annotations__ - 895 # are present as dataclass fields. - 896 annotations = getattr(cls, "__annotations__", {}) - 897 if not annotations: - 898 return False - 899 - 900 field_names = [field.name for field in dataclass_fields(cls)] - 901 for field_name, annotation in annotations.items(): - 902 # Omit InitVar field because it doesn't appear in dataclass fields. - 903 if is_instance(annotation, dataclasses.InitVar): - 904 continue - 905 # This field in __annotations__ is not a part of dataclass fields. - 906 # This means this class does not implement dataclass directly. - 907 if field_name not in field_names: - 908 return True - 909 - 910 # If all of the fields in __annotation__ are present as dataclass fields, - 911 # the class already implemented dataclass, thus returns False. - 912 return False - 913 - 914 - 915@dataclass - 916class TypeCheck: - 917 """ - 918 Specify type check flavors. - 919 """ - 920 - 921 class Kind(enum.Enum): - 922 Disabled = enum.auto() - 923 """ No check performed """ - 924 - 925 Coerce = enum.auto() - 926 """ Value is coerced into the declared type """ - 927 Strict = enum.auto() - 928 """ Value are strictly checked against the declared type """ - 929 - 930 kind: Kind + 865DefaultTagging = ExternalTagging + 866 + 867 + 868def ensure(expr: Any, description: str) -> None: + 869 if not expr: + 870 raise Exception(description) + 871 + 872 + 873def should_impl_dataclass(cls: type[Any]) -> bool: + 874 """ + 875 Test if class doesn't have @dataclass. + 876 + 877 `dataclasses.is_dataclass` returns True even Derived class doesn't actually @dataclass. + 878 >>> @dataclasses.dataclass + 879 ... class Base: + 880 ... a: int + 881 >>> class Derived(Base): + 882 ... b: int + 883 >>> dataclasses.is_dataclass(Derived) + 884 True + 885 + 886 This function tells the class actually have @dataclass or not. + 887 >>> should_impl_dataclass(Base) + 888 False + 889 >>> should_impl_dataclass(Derived) + 890 True + 891 """ + 892 if not dataclasses.is_dataclass(cls): + 893 return True + 894 + 895 # Checking is_dataclass is not enough in such a case that the class is inherited + 896 # from another dataclass. To do it correctly, check if all fields in __annotations__ + 897 # are present as dataclass fields. + 898 annotations = getattr(cls, "__annotations__", {}) + 899 if not annotations: + 900 return False + 901 + 902 field_names = [field.name for field in dataclass_fields(cls)] + 903 for field_name, annotation in annotations.items(): + 904 # Omit InitVar field because it doesn't appear in dataclass fields. + 905 if is_instance(annotation, dataclasses.InitVar): + 906 continue + 907 # This field in __annotations__ is not a part of dataclass fields. + 908 # This means this class does not implement dataclass directly. + 909 if field_name not in field_names: + 910 return True + 911 + 912 # If all of the fields in __annotation__ are present as dataclass fields, + 913 # the class already implemented dataclass, thus returns False. + 914 return False + 915 + 916 + 917@dataclass + 918class TypeCheck: + 919 """ + 920 Specify type check flavors. + 921 """ + 922 + 923 class Kind(enum.Enum): + 924 Disabled = enum.auto() + 925 """ No check performed """ + 926 + 927 Coerce = enum.auto() + 928 """ Value is coerced into the declared type """ + 929 Strict = enum.auto() + 930 """ Value are strictly checked against the declared type """ 931 - 932 def is_strict(self) -> bool: - 933 return self.kind == self.Kind.Strict - 934 - 935 def is_coerce(self) -> bool: - 936 return self.kind == self.Kind.Coerce - 937 - 938 def __call__(self, **kwargs: Any) -> TypeCheck: - 939 # TODO - 940 return self - 941 - 942 - 943disabled = TypeCheck(kind=TypeCheck.Kind.Disabled) + 932 kind: Kind + 933 + 934 def is_strict(self) -> bool: + 935 return self.kind == self.Kind.Strict + 936 + 937 def is_coerce(self) -> bool: + 938 return self.kind == self.Kind.Coerce + 939 + 940 def __call__(self, **kwargs: Any) -> TypeCheck: + 941 # TODO + 942 return self + 943 944 - 945coerce = TypeCheck(kind=TypeCheck.Kind.Coerce) + 945disabled = TypeCheck(kind=TypeCheck.Kind.Disabled) 946 - 947strict = TypeCheck(kind=TypeCheck.Kind.Strict) + 947coerce = TypeCheck(kind=TypeCheck.Kind.Coerce) 948 - 949 - 950def coerce_object(cls: str, field: str, typ: type[Any], obj: Any) -> Any: - 951 try: - 952 return typ(obj) if is_coercible(typ, obj) else obj - 953 except Exception as e: - 954 raise SerdeError( - 955 f"failed to coerce the field {cls}.{field} value {obj} into {typename(typ)}: {e}" - 956 ) - 957 - 958 - 959def is_coercible(typ: type[Any], obj: Any) -> bool: - 960 if obj is None: - 961 return False - 962 return True - 963 - 964 - 965def has_default(field: Field[Any]) -> bool: - 966 """ - 967 Test if the field has default value. - 968 - 969 >>> @dataclasses.dataclass - 970 ... class C: - 971 ... a: int - 972 ... d: int = 10 - 973 >>> has_default(dataclasses.fields(C)[0]) - 974 False - 975 >>> has_default(dataclasses.fields(C)[1]) - 976 True - 977 """ - 978 return not isinstance(field.default, dataclasses._MISSING_TYPE) - 979 - 980 - 981def has_default_factory(field: Field[Any]) -> bool: - 982 """ - 983 Test if the field has default factory. - 984 - 985 >>> @dataclasses.dataclass - 986 ... class C: - 987 ... a: int - 988 ... d: dict = dataclasses.field(default_factory=dict) - 989 >>> has_default_factory(dataclasses.fields(C)[0]) - 990 False - 991 >>> has_default_factory(dataclasses.fields(C)[1]) - 992 True - 993 """ - 994 return not isinstance(field.default_factory, dataclasses._MISSING_TYPE) - 995 - 996 - 997class ClassSerializer(Protocol): - 998 """ - 999 Interface for custom class serializer. -1000 -1001 This protocol is intended to be used for custom class serializer. + 949strict = TypeCheck(kind=TypeCheck.Kind.Strict) + 950 + 951 + 952def coerce_object(cls: str, field: str, typ: type[Any], obj: Any) -> Any: + 953 try: + 954 return typ(obj) if is_coercible(typ, obj) else obj + 955 except Exception as e: + 956 raise SerdeError( + 957 f"failed to coerce the field {cls}.{field} value {obj} into {typename(typ)}: {e}" + 958 ) + 959 + 960 + 961def is_coercible(typ: type[Any], obj: Any) -> bool: + 962 if obj is None: + 963 return False + 964 return True + 965 + 966 + 967def has_default(field: Field[Any]) -> bool: + 968 """ + 969 Test if the field has default value. + 970 + 971 >>> @dataclasses.dataclass + 972 ... class C: + 973 ... a: int + 974 ... d: int = 10 + 975 >>> has_default(dataclasses.fields(C)[0]) + 976 False + 977 >>> has_default(dataclasses.fields(C)[1]) + 978 True + 979 """ + 980 return not isinstance(field.default, dataclasses._MISSING_TYPE) + 981 + 982 + 983def has_default_factory(field: Field[Any]) -> bool: + 984 """ + 985 Test if the field has default factory. + 986 + 987 >>> @dataclasses.dataclass + 988 ... class C: + 989 ... a: int + 990 ... d: dict = dataclasses.field(default_factory=dict) + 991 >>> has_default_factory(dataclasses.fields(C)[0]) + 992 False + 993 >>> has_default_factory(dataclasses.fields(C)[1]) + 994 True + 995 """ + 996 return not isinstance(field.default_factory, dataclasses._MISSING_TYPE) + 997 + 998 + 999class ClassSerializer(Protocol): +1000 """ +1001 Interface for custom class serializer. 1002 -1003 >>> from datetime import datetime -1004 >>> from serde import serde -1005 >>> from plum import dispatch -1006 >>> class MySerializer(ClassSerializer): -1007 ... @dispatch -1008 ... def serialize(self, value: datetime) -> str: -1009 ... return value.strftime("%d/%m/%y") -1010 """ -1011 -1012 def serialize(self, value: Any) -> Any: -1013 pass -1014 -1015 -1016class ClassDeserializer(Protocol): -1017 """ -1018 Interface for custom class deserializer. -1019 -1020 This protocol is intended to be used for custom class deserializer. +1003 This protocol is intended to be used for custom class serializer. +1004 +1005 >>> from datetime import datetime +1006 >>> from serde import serde +1007 >>> from plum import dispatch +1008 >>> class MySerializer(ClassSerializer): +1009 ... @dispatch +1010 ... def serialize(self, value: datetime) -> str: +1011 ... return value.strftime("%d/%m/%y") +1012 """ +1013 +1014 def serialize(self, value: Any) -> Any: +1015 pass +1016 +1017 +1018class ClassDeserializer(Protocol): +1019 """ +1020 Interface for custom class deserializer. 1021 -1022 >>> from datetime import datetime -1023 >>> from serde import serde -1024 >>> from plum import dispatch -1025 >>> class MyDeserializer(ClassDeserializer): -1026 ... @dispatch -1027 ... def deserialize(self, cls: type[datetime], value: Any) -> datetime: -1028 ... return datetime.strptime(value, "%d/%m/%y") -1029 """ -1030 -1031 def deserialize(self, cls: Any, value: Any) -> Any: -1032 pass -1033 -1034 -1035GLOBAL_CLASS_SERIALIZER: list[ClassSerializer] = [] +1022 This protocol is intended to be used for custom class deserializer. +1023 +1024 >>> from datetime import datetime +1025 >>> from serde import serde +1026 >>> from plum import dispatch +1027 >>> class MyDeserializer(ClassDeserializer): +1028 ... @dispatch +1029 ... def deserialize(self, cls: type[datetime], value: Any) -> datetime: +1030 ... return datetime.strptime(value, "%d/%m/%y") +1031 """ +1032 +1033 def deserialize(self, cls: Any, value: Any) -> Any: +1034 pass +1035 1036 -1037GLOBAL_CLASS_DESERIALIZER: list[ClassDeserializer] = [] +1037GLOBAL_CLASS_SERIALIZER: list[ClassSerializer] = [] 1038 -1039 -1040def add_serializer(serializer: ClassSerializer) -> None: -1041 """ -1042 Register custom global serializer. -1043 """ -1044 GLOBAL_CLASS_SERIALIZER.append(serializer) -1045 -1046 -1047def add_deserializer(deserializer: ClassDeserializer) -> None: -1048 """ -1049 Register custom global deserializer. -1050 """ -1051 GLOBAL_CLASS_DESERIALIZER.append(deserializer) +1039GLOBAL_CLASS_DESERIALIZER: list[ClassDeserializer] = [] +1040 +1041 +1042def add_serializer(serializer: ClassSerializer) -> None: +1043 """ +1044 Register custom global serializer. +1045 """ +1046 GLOBAL_CLASS_SERIALIZER.append(serializer) +1047 +1048 +1049def add_deserializer(deserializer: ClassDeserializer) -> None: +1050 """ +1051 Register custom global deserializer. +1052 """ +1053 GLOBAL_CLASS_DESERIALIZER.append(deserializer)

@@ -1554,32 +1556,32 @@

-
470@dataclass
-471class Func:
-472    """
-473    Function wrapper that provides `mangled` optional field.
-474
-475    pyserde copies every function reference into global scope
-476    for code generation. Mangling function names is needed in
-477    order to avoid name conflict in the global scope when
-478    multiple fields receives `skip_if` attribute.
-479    """
-480
-481    inner: Callable[..., Any]
-482    """ Function to wrap in """
-483
-484    mangeld: str = ""
-485    """ Mangled function name """
-486
-487    def __call__(self, v: Any) -> None:
-488        return self.inner(v)  # type: ignore
-489
-490    @property
-491    def name(self) -> str:
-492        """
-493        Mangled function name
-494        """
-495        return self.mangeld
+            
472@dataclass
+473class Func:
+474    """
+475    Function wrapper that provides `mangled` optional field.
+476
+477    pyserde copies every function reference into global scope
+478    for code generation. Mangling function names is needed in
+479    order to avoid name conflict in the global scope when
+480    multiple fields receives `skip_if` attribute.
+481    """
+482
+483    inner: Callable[..., Any]
+484    """ Function to wrap in """
+485
+486    mangeld: str = ""
+487    """ Mangled function name """
+488
+489    def __call__(self, v: Any) -> None:
+490        return self.inner(v)  # type: ignore
+491
+492    @property
+493    def name(self) -> str:
+494        """
+495        Mangled function name
+496        """
+497        return self.mangeld
 
@@ -1640,12 +1642,12 @@

-
490    @property
-491    def name(self) -> str:
-492        """
-493        Mangled function name
-494        """
-495        return self.mangeld
+            
492    @property
+493    def name(self) -> str:
+494        """
+495        Mangled function name
+496        """
+497        return self.mangeld
 
@@ -1667,141 +1669,141 @@

-
557@dataclass
-558class Field(Generic[T]):
-559    """
-560    Field class is similar to `dataclasses.Field`. It provides pyserde specific options.
-561
-562    `type`, `name`, `default` and `default_factory` are the same members as `dataclasses.Field`.
-563    """
-564
-565    type: type[T]
-566    """ Type of Field """
-567    name: Optional[str]
-568    """ Name of Field """
-569    default: Any = field(default_factory=dataclasses._MISSING_TYPE)
-570    """ Default value of Field """
-571    default_factory: Any = field(default_factory=dataclasses._MISSING_TYPE)
-572    """ Default factory method of Field """
-573    init: bool = field(default_factory=dataclasses._MISSING_TYPE)
-574    repr: Any = field(default_factory=dataclasses._MISSING_TYPE)
-575    hash: Any = field(default_factory=dataclasses._MISSING_TYPE)
-576    compare: Any = field(default_factory=dataclasses._MISSING_TYPE)
-577    metadata: Mapping[str, Any] = field(default_factory=dict)
-578    kw_only: bool = False
-579    case: Optional[str] = None
-580    alias: list[str] = field(default_factory=list)
-581    rename: Optional[str] = None
-582    skip: Optional[bool] = None
-583    skip_if: Optional[Func] = None
-584    skip_if_false: Optional[bool] = None
-585    skip_if_default: Optional[bool] = None
-586    serializer: Optional[Func] = None  # Custom field serializer.
-587    deserializer: Optional[Func] = None  # Custom field deserializer.
-588    flatten: Optional[FlattenOpts] = None
-589    parent: Optional[Any] = None
-590    type_args: Optional[list[str]] = None
-591
-592    @classmethod
-593    def from_dataclass(cls, f: dataclasses.Field[T], parent: Optional[Any] = None) -> Field[T]:
-594        """
-595        Create `Field` object from `dataclasses.Field`.
-596        """
-597        skip_if_false_func: Optional[Func] = None
-598        if f.metadata.get("serde_skip_if_false"):
-599            skip_if_false_func = Func(skip_if_false, cls.mangle(f, "skip_if_false"))
-600
-601        skip_if_default_func: Optional[Func] = None
-602        if f.metadata.get("serde_skip_if_default"):
-603            skip_if_def = functools.partial(skip_if_default, default=f.default)
-604            skip_if_default_func = Func(skip_if_def, cls.mangle(f, "skip_if_default"))
-605
-606        skip_if: Optional[Func] = None
-607        if f.metadata.get("serde_skip_if"):
-608            func = f.metadata.get("serde_skip_if")
-609            if callable(func):
-610                skip_if = Func(func, cls.mangle(f, "skip_if"))
-611
-612        serializer: Optional[Func] = None
-613        func = f.metadata.get("serde_serializer")
-614        if func:
-615            serializer = Func(func, cls.mangle(f, "serializer"))
-616
-617        deserializer: Optional[Func] = None
-618        func = f.metadata.get("serde_deserializer")
-619        if func:
-620            deserializer = Func(func, cls.mangle(f, "deserializer"))
-621
-622        flatten = f.metadata.get("serde_flatten")
-623        if flatten is True:
-624            flatten = FlattenOpts()
-625        if flatten and not (dataclasses.is_dataclass(f.type) or is_opt_dataclass(f.type)):
-626            raise SerdeError(f"pyserde does not support flatten attribute for {typename(f.type)}")
-627
-628        kw_only = bool(f.kw_only) if sys.version_info >= (3, 10) else False
+            
559@dataclass
+560class Field(Generic[T]):
+561    """
+562    Field class is similar to `dataclasses.Field`. It provides pyserde specific options.
+563
+564    `type`, `name`, `default` and `default_factory` are the same members as `dataclasses.Field`.
+565    """
+566
+567    type: type[T]
+568    """ Type of Field """
+569    name: Optional[str]
+570    """ Name of Field """
+571    default: Any = field(default_factory=dataclasses._MISSING_TYPE)
+572    """ Default value of Field """
+573    default_factory: Any = field(default_factory=dataclasses._MISSING_TYPE)
+574    """ Default factory method of Field """
+575    init: bool = field(default_factory=dataclasses._MISSING_TYPE)
+576    repr: Any = field(default_factory=dataclasses._MISSING_TYPE)
+577    hash: Any = field(default_factory=dataclasses._MISSING_TYPE)
+578    compare: Any = field(default_factory=dataclasses._MISSING_TYPE)
+579    metadata: Mapping[str, Any] = field(default_factory=dict)
+580    kw_only: bool = False
+581    case: Optional[str] = None
+582    alias: list[str] = field(default_factory=list)
+583    rename: Optional[str] = None
+584    skip: Optional[bool] = None
+585    skip_if: Optional[Func] = None
+586    skip_if_false: Optional[bool] = None
+587    skip_if_default: Optional[bool] = None
+588    serializer: Optional[Func] = None  # Custom field serializer.
+589    deserializer: Optional[Func] = None  # Custom field deserializer.
+590    flatten: Optional[FlattenOpts] = None
+591    parent: Optional[Any] = None
+592    type_args: Optional[list[str]] = None
+593
+594    @classmethod
+595    def from_dataclass(cls, f: dataclasses.Field[T], parent: Optional[Any] = None) -> Field[T]:
+596        """
+597        Create `Field` object from `dataclasses.Field`.
+598        """
+599        skip_if_false_func: Optional[Func] = None
+600        if f.metadata.get("serde_skip_if_false"):
+601            skip_if_false_func = Func(skip_if_false, cls.mangle(f, "skip_if_false"))
+602
+603        skip_if_default_func: Optional[Func] = None
+604        if f.metadata.get("serde_skip_if_default"):
+605            skip_if_def = functools.partial(skip_if_default, default=f.default)
+606            skip_if_default_func = Func(skip_if_def, cls.mangle(f, "skip_if_default"))
+607
+608        skip_if: Optional[Func] = None
+609        if f.metadata.get("serde_skip_if"):
+610            func = f.metadata.get("serde_skip_if")
+611            if callable(func):
+612                skip_if = Func(func, cls.mangle(f, "skip_if"))
+613
+614        serializer: Optional[Func] = None
+615        func = f.metadata.get("serde_serializer")
+616        if func:
+617            serializer = Func(func, cls.mangle(f, "serializer"))
+618
+619        deserializer: Optional[Func] = None
+620        func = f.metadata.get("serde_deserializer")
+621        if func:
+622            deserializer = Func(func, cls.mangle(f, "deserializer"))
+623
+624        flatten = f.metadata.get("serde_flatten")
+625        if flatten is True:
+626            flatten = FlattenOpts()
+627        if flatten and not (dataclasses.is_dataclass(f.type) or is_opt_dataclass(f.type)):
+628            raise SerdeError(f"pyserde does not support flatten attribute for {typename(f.type)}")
 629
-630        return cls(
-631            f.type,
-632            f.name,
-633            default=f.default,
-634            default_factory=f.default_factory,
-635            init=f.init,
-636            repr=f.repr,
-637            hash=f.hash,
-638            compare=f.compare,
-639            metadata=f.metadata,
-640            rename=f.metadata.get("serde_rename"),
-641            alias=f.metadata.get("serde_alias", []),
-642            skip=f.metadata.get("serde_skip"),
-643            skip_if=skip_if or skip_if_false_func or skip_if_default_func,
-644            serializer=serializer,
-645            deserializer=deserializer,
-646            flatten=flatten,
-647            parent=parent,
-648            kw_only=kw_only,
-649        )
-650
-651    def to_dataclass(self) -> dataclasses.Field[T]:
-652        f = dataclasses.Field(
-653            default=self.default,
-654            default_factory=self.default_factory,
-655            init=self.init,
-656            repr=self.repr,
-657            hash=self.hash,
-658            compare=self.compare,
-659            metadata=self.metadata,
-660            kw_only=self.kw_only,
-661        )
-662        assert self.name
-663        f.name = self.name
-664        f.type = self.type
-665        return f
-666
-667    def is_self_referencing(self) -> bool:
-668        if self.type is None:
-669            return False
-670        if self.parent is None:
+630        kw_only = bool(f.kw_only) if sys.version_info >= (3, 10) else False
+631
+632        return cls(
+633            f.type,  # type: ignore
+634            f.name,
+635            default=f.default,
+636            default_factory=f.default_factory,
+637            init=f.init,
+638            repr=f.repr,
+639            hash=f.hash,
+640            compare=f.compare,
+641            metadata=f.metadata,
+642            rename=f.metadata.get("serde_rename"),
+643            alias=f.metadata.get("serde_alias", []),
+644            skip=f.metadata.get("serde_skip"),
+645            skip_if=skip_if or skip_if_false_func or skip_if_default_func,
+646            serializer=serializer,
+647            deserializer=deserializer,
+648            flatten=flatten,
+649            parent=parent,
+650            kw_only=kw_only,
+651        )
+652
+653    def to_dataclass(self) -> dataclasses.Field[T]:
+654        f = dataclasses.Field(
+655            default=self.default,
+656            default_factory=self.default_factory,
+657            init=self.init,
+658            repr=self.repr,
+659            hash=self.hash,
+660            compare=self.compare,
+661            metadata=self.metadata,
+662            kw_only=self.kw_only,
+663        )
+664        assert self.name
+665        f.name = self.name
+666        f.type = self.type
+667        return f
+668
+669    def is_self_referencing(self) -> bool:
+670        if self.type is None:
 671            return False
-672        return self.type == self.parent  # type: ignore
-673
-674    @staticmethod
-675    def mangle(field: dataclasses.Field[Any], name: str) -> str:
-676        """
-677        Get mangled name based on field name.
-678        """
-679        return f"{field.name}_{name}"
-680
-681    def conv_name(self, case: Optional[str] = None) -> str:
-682        """
-683        Get an actual field name which `rename` and `rename_all` conversions
-684        are made. Use `name` property to get a field name before conversion.
-685        """
-686        return conv(self, case or self.case)
-687
-688    def supports_default(self) -> bool:
-689        return not getattr(self, "iterbased", False) and (
-690            has_default(self) or has_default_factory(self)
-691        )
+672        if self.parent is None:
+673            return False
+674        return self.type == self.parent  # type: ignore
+675
+676    @staticmethod
+677    def mangle(field: dataclasses.Field[Any], name: str) -> str:
+678        """
+679        Get mangled name based on field name.
+680        """
+681        return f"{field.name}_{name}"
+682
+683    def conv_name(self, case: Optional[str] = None) -> str:
+684        """
+685        Get an actual field name which `rename` and `rename_all` conversions
+686        are made. Use `name` property to get a field name before conversion.
+687        """
+688        return conv(self, case or self.case)
+689
+690    def supports_default(self) -> bool:
+691        return not getattr(self, "iterbased", False) and (
+692            has_default(self) or has_default_factory(self)
+693        )
 
@@ -2097,64 +2099,64 @@

-
592    @classmethod
-593    def from_dataclass(cls, f: dataclasses.Field[T], parent: Optional[Any] = None) -> Field[T]:
-594        """
-595        Create `Field` object from `dataclasses.Field`.
-596        """
-597        skip_if_false_func: Optional[Func] = None
-598        if f.metadata.get("serde_skip_if_false"):
-599            skip_if_false_func = Func(skip_if_false, cls.mangle(f, "skip_if_false"))
-600
-601        skip_if_default_func: Optional[Func] = None
-602        if f.metadata.get("serde_skip_if_default"):
-603            skip_if_def = functools.partial(skip_if_default, default=f.default)
-604            skip_if_default_func = Func(skip_if_def, cls.mangle(f, "skip_if_default"))
-605
-606        skip_if: Optional[Func] = None
-607        if f.metadata.get("serde_skip_if"):
-608            func = f.metadata.get("serde_skip_if")
-609            if callable(func):
-610                skip_if = Func(func, cls.mangle(f, "skip_if"))
-611
-612        serializer: Optional[Func] = None
-613        func = f.metadata.get("serde_serializer")
-614        if func:
-615            serializer = Func(func, cls.mangle(f, "serializer"))
-616
-617        deserializer: Optional[Func] = None
-618        func = f.metadata.get("serde_deserializer")
-619        if func:
-620            deserializer = Func(func, cls.mangle(f, "deserializer"))
-621
-622        flatten = f.metadata.get("serde_flatten")
-623        if flatten is True:
-624            flatten = FlattenOpts()
-625        if flatten and not (dataclasses.is_dataclass(f.type) or is_opt_dataclass(f.type)):
-626            raise SerdeError(f"pyserde does not support flatten attribute for {typename(f.type)}")
-627
-628        kw_only = bool(f.kw_only) if sys.version_info >= (3, 10) else False
+            
594    @classmethod
+595    def from_dataclass(cls, f: dataclasses.Field[T], parent: Optional[Any] = None) -> Field[T]:
+596        """
+597        Create `Field` object from `dataclasses.Field`.
+598        """
+599        skip_if_false_func: Optional[Func] = None
+600        if f.metadata.get("serde_skip_if_false"):
+601            skip_if_false_func = Func(skip_if_false, cls.mangle(f, "skip_if_false"))
+602
+603        skip_if_default_func: Optional[Func] = None
+604        if f.metadata.get("serde_skip_if_default"):
+605            skip_if_def = functools.partial(skip_if_default, default=f.default)
+606            skip_if_default_func = Func(skip_if_def, cls.mangle(f, "skip_if_default"))
+607
+608        skip_if: Optional[Func] = None
+609        if f.metadata.get("serde_skip_if"):
+610            func = f.metadata.get("serde_skip_if")
+611            if callable(func):
+612                skip_if = Func(func, cls.mangle(f, "skip_if"))
+613
+614        serializer: Optional[Func] = None
+615        func = f.metadata.get("serde_serializer")
+616        if func:
+617            serializer = Func(func, cls.mangle(f, "serializer"))
+618
+619        deserializer: Optional[Func] = None
+620        func = f.metadata.get("serde_deserializer")
+621        if func:
+622            deserializer = Func(func, cls.mangle(f, "deserializer"))
+623
+624        flatten = f.metadata.get("serde_flatten")
+625        if flatten is True:
+626            flatten = FlattenOpts()
+627        if flatten and not (dataclasses.is_dataclass(f.type) or is_opt_dataclass(f.type)):
+628            raise SerdeError(f"pyserde does not support flatten attribute for {typename(f.type)}")
 629
-630        return cls(
-631            f.type,
-632            f.name,
-633            default=f.default,
-634            default_factory=f.default_factory,
-635            init=f.init,
-636            repr=f.repr,
-637            hash=f.hash,
-638            compare=f.compare,
-639            metadata=f.metadata,
-640            rename=f.metadata.get("serde_rename"),
-641            alias=f.metadata.get("serde_alias", []),
-642            skip=f.metadata.get("serde_skip"),
-643            skip_if=skip_if or skip_if_false_func or skip_if_default_func,
-644            serializer=serializer,
-645            deserializer=deserializer,
-646            flatten=flatten,
-647            parent=parent,
-648            kw_only=kw_only,
-649        )
+630        kw_only = bool(f.kw_only) if sys.version_info >= (3, 10) else False
+631
+632        return cls(
+633            f.type,  # type: ignore
+634            f.name,
+635            default=f.default,
+636            default_factory=f.default_factory,
+637            init=f.init,
+638            repr=f.repr,
+639            hash=f.hash,
+640            compare=f.compare,
+641            metadata=f.metadata,
+642            rename=f.metadata.get("serde_rename"),
+643            alias=f.metadata.get("serde_alias", []),
+644            skip=f.metadata.get("serde_skip"),
+645            skip_if=skip_if or skip_if_false_func or skip_if_default_func,
+646            serializer=serializer,
+647            deserializer=deserializer,
+648            flatten=flatten,
+649            parent=parent,
+650            kw_only=kw_only,
+651        )
 
@@ -2174,21 +2176,21 @@

-
651    def to_dataclass(self) -> dataclasses.Field[T]:
-652        f = dataclasses.Field(
-653            default=self.default,
-654            default_factory=self.default_factory,
-655            init=self.init,
-656            repr=self.repr,
-657            hash=self.hash,
-658            compare=self.compare,
-659            metadata=self.metadata,
-660            kw_only=self.kw_only,
-661        )
-662        assert self.name
-663        f.name = self.name
-664        f.type = self.type
-665        return f
+            
653    def to_dataclass(self) -> dataclasses.Field[T]:
+654        f = dataclasses.Field(
+655            default=self.default,
+656            default_factory=self.default_factory,
+657            init=self.init,
+658            repr=self.repr,
+659            hash=self.hash,
+660            compare=self.compare,
+661            metadata=self.metadata,
+662            kw_only=self.kw_only,
+663        )
+664        assert self.name
+665        f.name = self.name
+666        f.type = self.type
+667        return f
 
@@ -2206,12 +2208,12 @@

-
667    def is_self_referencing(self) -> bool:
-668        if self.type is None:
-669            return False
-670        if self.parent is None:
+            
669    def is_self_referencing(self) -> bool:
+670        if self.type is None:
 671            return False
-672        return self.type == self.parent  # type: ignore
+672        if self.parent is None:
+673            return False
+674        return self.type == self.parent  # type: ignore
 
@@ -2230,12 +2232,12 @@

-
674    @staticmethod
-675    def mangle(field: dataclasses.Field[Any], name: str) -> str:
-676        """
-677        Get mangled name based on field name.
-678        """
-679        return f"{field.name}_{name}"
+            
676    @staticmethod
+677    def mangle(field: dataclasses.Field[Any], name: str) -> str:
+678        """
+679        Get mangled name based on field name.
+680        """
+681        return f"{field.name}_{name}"
 
@@ -2255,12 +2257,12 @@

-
681    def conv_name(self, case: Optional[str] = None) -> str:
-682        """
-683        Get an actual field name which `rename` and `rename_all` conversions
-684        are made. Use `name` property to get a field name before conversion.
-685        """
-686        return conv(self, case or self.case)
+            
683    def conv_name(self, case: Optional[str] = None) -> str:
+684        """
+685        Get an actual field name which `rename` and `rename_all` conversions
+686        are made. Use `name` property to get a field name before conversion.
+687        """
+688        return conv(self, case or self.case)
 
@@ -2281,10 +2283,10 @@

-
688    def supports_default(self) -> bool:
-689        return not getattr(self, "iterbased", False) and (
-690            has_default(self) or has_default_factory(self)
-691        )
+            
690    def supports_default(self) -> bool:
+691        return not getattr(self, "iterbased", False) and (
+692            has_default(self) or has_default_factory(self)
+693        )
 
@@ -2303,18 +2305,18 @@

-
697def fields(field_cls: type[F], cls: type[Any], serialize_class_var: bool = False) -> list[F]:
-698    """
-699    Iterate fields of the dataclass and returns `serde.core.Field`.
-700    """
-701    fields = [field_cls.from_dataclass(f, parent=cls) for f in dataclass_fields(cls)]
-702
-703    if serialize_class_var:
-704        for name, typ in get_type_hints(cls).items():
-705            if is_class_var(typ):
-706                fields.append(field_cls(typ, name, default=getattr(cls, name)))
-707
-708    return fields  # type: ignore
+            
699def fields(field_cls: type[F], cls: type[Any], serialize_class_var: bool = False) -> list[F]:
+700    """
+701    Iterate fields of the dataclass and returns `serde.core.Field`.
+702    """
+703    fields = [field_cls.from_dataclass(f, parent=cls) for f in dataclass_fields(cls)]
+704
+705    if serialize_class_var:
+706        for name, typ in get_type_hints(cls).items():
+707            if is_class_var(typ):
+708                fields.append(field_cls(typ, name, default=getattr(cls, name)))
+709
+710    return fields  # type: ignore
 
@@ -2335,11 +2337,11 @@

-
506@dataclass
-507class FlattenOpts:
-508    """
-509    Flatten options. Currently not used.
-510    """
+            
508@dataclass
+509class FlattenOpts:
+510    """
+511    Flatten options. Currently not used.
+512    """
 
@@ -2359,23 +2361,23 @@

-
711def conv(f: Field[Any], case: Optional[str] = None) -> str:
-712    """
-713    Convert field name.
-714    """
-715    name = f.name
-716    if case:
-717        casef = getattr(casefy, case, None)
-718        if not casef:
-719            raise SerdeError(
-720                f"Unkown case type: {f.case}. Pass the name of case supported by 'casefy' package."
-721            )
-722        name = casef(name)
-723    if f.rename:
-724        name = f.rename
-725    if name is None:
-726        raise SerdeError("Field name is None.")
-727    return name
+            
713def conv(f: Field[Any], case: Optional[str] = None) -> str:
+714    """
+715    Convert field name.
+716    """
+717    name = f.name
+718    if case:
+719        casef = getattr(casefy, case, None)
+720        if not casef:
+721            raise SerdeError(
+722                f"Unkown case type: {f.case}. Pass the name of case supported by 'casefy' package."
+723            )
+724        name = casef(name)
+725    if f.rename:
+726        name = f.rename
+727    if name is None:
+728        raise SerdeError("Field name is None.")
+729    return name
 
@@ -2395,18 +2397,18 @@

-
730def union_func_name(prefix: str, union_args: Sequence[Any]) -> str:
-731    """
-732    Generate a function name that contains all union types
-733
-734    * `prefix` prefix to distinguish between serializing and deserializing
-735    * `union_args`: type arguments of a Union
-736
-737    >>> from ipaddress import IPv4Address
-738    >>> union_func_name("union_se", [int, list[str], IPv4Address])
-739    'union_se_int_list_str__IPv4Address'
-740    """
-741    return re.sub(r"[^A-Za-z0-9]", "_", f"{prefix}_{'_'.join([typename(e) for e in union_args])}")
+            
732def union_func_name(prefix: str, union_args: Sequence[Any]) -> str:
+733    """
+734    Generate a function name that contains all union types
+735
+736    * `prefix` prefix to distinguish between serializing and deserializing
+737    * `union_args`: type arguments of a Union
+738
+739    >>> from ipaddress import IPv4Address
+740    >>> union_func_name("union_se", [int, list[str], IPv4Address])
+741    'union_se_int_list_str__IPv4Address'
+742    """
+743    return re.sub(r"[^A-Za-z0-9]", "_", f"{prefix}_{'_'.join([typename(e) for e in union_args])}")
 
diff --git a/api/serde/de.html b/api/serde/de.html index 9ab6cfbe..374ad639 100644 --- a/api/serde/de.html +++ b/api/serde/de.html @@ -789,8 +789,8 @@

720 GLOBAL_CLASS_DESERIALIZER, [self.class_deserializer] if self.class_deserializer else [] 721 ) 722 for n, class_deserializer in enumerate(class_deserializers): - 723 for sig in class_deserializer.__class__.deserialize.methods: # type: ignore - 724 implemented_methods[get_args(sig.types[1])[0]] = n + 723 for method in class_deserializer.__class__.deserialize.methods: # type: ignore + 724 implemented_methods[get_args(method.signature.types[1])[0]] = n 725 726 custom_deserializer_available = arg.type in implemented_methods 727 if custom_deserializer_available and not arg.deserializer: diff --git a/api/serde/se.html b/api/serde/se.html index 274ec929..8bfb11d8 100644 --- a/api/serde/se.html +++ b/api/serde/se.html @@ -820,8 +820,8 @@

751 GLOBAL_CLASS_SERIALIZER, [self.class_serializer] if self.class_serializer else [] 752 ) 753 for n, class_serializer in enumerate(class_serializers): -754 for sig in class_serializer.__class__.serialize.methods: # type: ignore -755 implemented_methods[sig.types[1]] = n +754 for method in class_serializer.__class__.serialize.methods: # type: ignore +755 implemented_methods[method.signature.types[1]] = n 756 757 custom_serializer_available = arg.type in implemented_methods 758 if custom_serializer_available and not arg.serializer: