Coverage for pydantic/json.py: 100.00%
47 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-15 13:26 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-15 13:26 +0000
1import datetime 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
2from collections import deque 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
3from decimal import Decimal 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
4from enum import Enum 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
5from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
6from pathlib import Path 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
7from re import Pattern 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
8from types import GeneratorType 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
9from typing import Any, Callable, Dict, Type, Union 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
10from uuid import UUID 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
12from pydantic.color import Color 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
13from pydantic.networks import NameEmail 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
14from pydantic.types import SecretBytes, SecretStr 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
16__all__ = 'pydantic_encoder', 'custom_pydantic_encoder', 'timedelta_isoformat' 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
19def isoformat(o: Union[datetime.date, datetime.time]) -> str: 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
20 return o.isoformat() 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
23def decimal_encoder(dec_value: Decimal) -> Union[int, float]: 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
24 """
25 Encodes a Decimal as int of there's no exponent, otherwise float
27 This is useful when we use ConstrainedDecimal to represent Numeric(x,0)
28 where a integer (but not int typed) is used. Encoding this as a float
29 results in failed round-tripping between encode and parse.
30 Our Id type is a prime example of this.
32 >>> decimal_encoder(Decimal("1.0"))
33 1.0
35 >>> decimal_encoder(Decimal("1"))
36 1
37 """
38 if dec_value.as_tuple().exponent >= 0: 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
39 return int(dec_value) 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
40 else:
41 return float(dec_value) 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
44ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = { 1abcdefghijklmnopqrstPQRSTUuvwxyzABCD
45 bytes: lambda o: o.decode(),
46 Color: str,
47 datetime.date: isoformat,
48 datetime.datetime: isoformat,
49 datetime.time: isoformat,
50 datetime.timedelta: lambda td: td.total_seconds(),
51 Decimal: decimal_encoder,
52 Enum: lambda o: o.value,
53 frozenset: list,
54 deque: list,
55 GeneratorType: list,
56 IPv4Address: str,
57 IPv4Interface: str,
58 IPv4Network: str,
59 IPv6Address: str,
60 IPv6Interface: str,
61 IPv6Network: str,
62 NameEmail: str,
63 Path: str,
64 Pattern: lambda o: o.pattern,
65 SecretBytes: str,
66 SecretStr: str,
67 set: list,
68 UUID: str,
69}
72def pydantic_encoder(obj: Any) -> Any: 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
73 from dataclasses import asdict, is_dataclass 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
75 from pydantic.main import BaseModel 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
77 if isinstance(obj, BaseModel): 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
78 return obj.dict() 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
79 elif is_dataclass(obj): 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
80 return asdict(obj) 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
82 # Check the class type and its superclasses for a matching encoder
83 for base in obj.__class__.__mro__[:-1]: 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
84 try: 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
85 encoder = ENCODERS_BY_TYPE[base] 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
86 except KeyError: 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
87 continue 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
88 return encoder(obj) 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
89 else: # We have exited the for loop without finding a suitable encoder
90 raise TypeError(f"Object of type '{obj.__class__.__name__}' is not JSON serializable") 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
93def custom_pydantic_encoder(type_encoders: Dict[Any, Callable[[Type[Any]], Any]], obj: Any) -> Any: 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
94 # Check the class type and its superclasses for a matching encoder
95 for base in obj.__class__.__mro__[:-1]: 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
96 try: 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
97 encoder = type_encoders[base] 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
98 except KeyError: 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
99 continue 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
101 return encoder(obj) 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
102 else: # We have exited the for loop without finding a suitable encoder
103 return pydantic_encoder(obj) 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
106def timedelta_isoformat(td: datetime.timedelta) -> str: 1EFabcdefghijGHklmnopqrstIJKLPQRSTUMNOuvwxyzABCD
107 """
108 ISO 8601 encoding for Python timedelta object.
109 """
110 minutes, seconds = divmod(td.seconds, 60) 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
111 hours, minutes = divmod(minutes, 60) 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD
112 return f'{"-" if td.days < 0 else ""}P{abs(td.days)}DT{hours:d}H{minutes:d}M{seconds:d}.{td.microseconds:06d}S' 1EFabcdefghijGHklmnopqrstIJKLMNOuvwxyzABCD