-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathschemas_improved.py
93 lines (79 loc) · 2.65 KB
/
schemas_improved.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
from pydantic import (
BeforeValidator,
TypeAdapter,
constr,
field_validator,
)
from pydantic_core import PydanticOmit
from typing_extensions import Annotated, NotRequired, TypedDict
not_required_fields = [
"description",
"price",
"variety",
"winery",
"designation",
"country",
"province",
"region_1",
"region_2",
"taster_name",
"taster_twitter_handle",
]
def exclude_none(s: str | None) -> str:
if s is None:
# since we want `exclude_none=True` in the end,
# just omit it if it's `None` during validation
raise PydanticOmit
else:
return s
ExcludeNoneStr = Annotated[str, BeforeValidator(exclude_none)]
class Wine(TypedDict):
id: int
points: int
title: str
description: NotRequired[str]
price: NotRequired[float]
variety: NotRequired[str]
winery: NotRequired[str]
designation: NotRequired[constr(strip_whitespace=True)]
country: NotRequired[str]
province: NotRequired[str]
region_1: NotRequired[str]
region_2: NotRequired[str]
taster_name: NotRequired[str]
taster_twitter_handle: NotRequired[str]
@field_validator(*not_required_fields, mode="before")
def omit_null_none(cls, v):
# type: ignore
if v is None or v == "null":
raise PydanticOmit
else:
return v
@field_validator("country", mode="before")
def country_unknown(cls, s: str | None) -> str:
# type: ignore
if s is None or s == "null":
return "Unknown"
else:
return s
WinesTypeAdapter = TypeAdapter(list[Wine])
if __name__ == "__main__":
sample_data = {
"id": 45100,
"points": 85.0, # Intentionally not an int to test coercion
"title": "Balduzzi 2012 Reserva Merlot (Maule Valley)",
"description": "Ripe in color and aromas, this chunky wine delivers heavy baked-berry and raisin aromas in front of a jammy, extracted palate. Raisin and cooked berry flavors finish plump, with earthy notes.",
"price": 10, # Intentionally not a float to test coercion
"variety": "Merlot",
"winery": "Balduzzi",
"country": "null", # Test null handling with 'Unknown' fallback
"province": "Maule Valley",
"region_1": "null", # Test null handling
# "region_2": "null" # Test missing value handling
"taster_name": "Michael Schachner",
"taster_twitter_handle": "@wineschach",
"designation": " The Vineyard ", # Test whitespace stripping
}
wines = WinesTypeAdapter.validate_python([sample_data])
from pprint import pprint
pprint(wines, sort_dicts=False)