Skip to content

Commit 0d7fea5

Browse files
ax3lEZoni
andauthored
Line of Dict of Elements (pals-project#14)
Overwrite validation and model dump. Alternate implementation of pals-project#13 that does not make the Python API harder to use. --------- Co-authored-by: Edoardo Zoni <[email protected]>
1 parent ac4f4bd commit 0d7fea5

File tree

2 files changed

+64
-4
lines changed

2 files changed

+64
-4
lines changed

schema/BaseElement.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from pydantic import BaseModel, ConfigDict
2-
from typing import Literal, Optional
2+
from typing import Literal
33

44

55
class BaseElement(BaseModel):
@@ -12,5 +12,14 @@ class BaseElement(BaseModel):
1212
# not only when an instance of BaseElement is created
1313
model_config = ConfigDict(validate_assignment=True)
1414

15-
# Unique element name
16-
name: Optional[str] = None
15+
# element name
16+
name: str
17+
18+
def model_dump(self, *args, **kwargs):
19+
"""This makes sure the element name property is moved out and up to a one-key dictionary"""
20+
elem_dict = super().model_dump(*args, **kwargs)
21+
name = elem_dict.pop("name", None)
22+
if name is None:
23+
raise ValueError("Element missing 'name' attribute")
24+
data = [{name: elem_dict}]
25+
return data

schema/Line.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from pydantic import BaseModel, ConfigDict, Field
1+
from pydantic import BaseModel, ConfigDict, Field, field_validator
22
from typing import Annotated, List, Literal, Union
33

44
from schema.BaseElement import BaseElement
@@ -29,6 +29,57 @@ class Line(BaseModel):
2929
]
3030
]
3131

32+
@field_validator("line", mode="before")
33+
@classmethod
34+
def parse_list_of_dicts(cls, value):
35+
"""This method inserts the key of the one-key dictionary into
36+
the name attribute of the elements"""
37+
if not isinstance(value, list):
38+
raise TypeError("line must be a list")
39+
40+
if value and isinstance(value[0], BaseModel):
41+
# Already a list of models; nothing to do
42+
return value
43+
44+
# we expect a list of dicts or strings
45+
elements = []
46+
for item_dict in value:
47+
# an element is either a reference string to another element or a dict
48+
if isinstance(item_dict, str):
49+
raise RuntimeError("Reference/alias elements not yet implemented")
50+
51+
elif isinstance(item_dict, dict):
52+
if not (isinstance(item_dict, dict) and len(item_dict) == 1):
53+
raise ValueError(
54+
f"Each line element must be a dict with exactly one key, the name of the element, but we got: {item_dict!r}"
55+
)
56+
[(name, fields)] = item_dict.items()
57+
58+
if not isinstance(fields, dict):
59+
raise ValueError(
60+
f"Value for element key '{name}' must be a dict (got {fields!r})"
61+
)
62+
63+
# Insert the name into the fields dict
64+
fields["name"] = name
65+
elements.append(fields)
66+
return elements
67+
68+
def model_dump(self, *args, **kwargs):
69+
"""This makes sure the element name property is moved out and up to a one-key dictionary"""
70+
# Use default dump for non-line fields
71+
data = super().model_dump(*args, **kwargs)
72+
73+
# Reformat 'line' field as list of single-key dicts
74+
new_line = []
75+
for elem in self.line:
76+
# Use custom dump for each line element
77+
elem_dict = elem.model_dump(**kwargs)[0]
78+
new_line.append(elem_dict)
79+
80+
data["line"] = new_line
81+
return data
82+
3283

3384
# Avoid circular import issues
3485
Line.model_rebuild()

0 commit comments

Comments
 (0)