Skip to content

Conversation

@ax3l
Copy link
Member

@ax3l ax3l commented Jul 9, 2025

Overwrite validation and model dump.

Alternate implementation of #13 that does not make the Python API harder to use.

@ax3l ax3l requested a review from EZoni July 9, 2025 21:06
@ax3l
Copy link
Member Author

ax3l commented Jul 9, 2025

Also creates:

kind: Line
line:
- drift1:
    kind: Drift
    length: 0.25
- quad1:
    MagneticMultipoleP:
      Bn1: 1.0
    kind: Quadrupole
    length: 1.0
- drift2:
    kind: Drift
    length: 0.5
- quad2:
    MagneticMultipoleP:
      Bn1: -1.0
    kind: Quadrupole
    length: 1.0
- drift3:
    kind: Drift
    length: 0.5

but keeps the Python API user friendly :)

Overwrite validation and model dump.
@EZoni
Copy link
Member

EZoni commented Jul 10, 2025

Due to the fact that we override model_dump only for Line, creating and serializing an element outside a line, e.g.,

element = DriftElement(name="drift1", length=0.25)
yaml_data = yaml.dump(element.model_dump(), default_flow_style=False)
print(yaml_data)

produces

kind: Drift
length: 0.25
name: drift1

which is the default YAML output without custom serialization.

Instead, do we want this to also produce the PALS-like output,

- drift1:
    kind: Drift
    length: 0.25

as in https://github.com/campa-consortium/pals/blob/main/examples/fodo.pals.yaml?

I think it would work if we override model_dump of BaseElement with

    def model_dump(self, *args, **kwargs):
        """This makes sure the element name property is moved out and up to a one-key dictionary"""
        elem_dict = super().model_dump(*args, **kwargs)
        name = elem_dict.pop("name", None)
        if name is None:
            raise ValueError("Element missing 'name' attribute")
        data = [{name: elem_dict}]
        return data

and also override model_dump of Line with

    def model_dump(self, *args, **kwargs):
        """This makes sure the element name property is moved out and up to a one-key dictionary"""
        # Use default dump for non-line fields
        data = super().model_dump(*args, **kwargs)
        # Reformat 'line' field as list of single-key dicts
        new_line = []
        for elem in self.line:
            #  Use custom dump for each line element
            elem_dict = elem.model_dump(**kwargs)[0]
            new_line.append(elem_dict)
        data["line"] = new_line
        return data

Here's the git diff that seems to be working for me, what do you think?

$ git diff
diff --git a/schema/BaseElement.py b/schema/BaseElement.py
index 43b63b6..9b361b2 100644
--- a/schema/BaseElement.py
+++ b/schema/BaseElement.py
@@ -14,3 +14,12 @@ class BaseElement(BaseModel):
 
     # element name
     name: str
+
+    def model_dump(self, *args, **kwargs):
+        """This makes sure the element name property is moved out and up to a one-key dictionary"""
+        elem_dict = super().model_dump(*args, **kwargs)
+        name = elem_dict.pop("name", None)
+        if name is None:
+            raise ValueError("Element missing 'name' attribute")
+        data = [{name: elem_dict}]
+        return data
diff --git a/schema/Line.py b/schema/Line.py
index 48c1580..d8472ee 100644
--- a/schema/Line.py
+++ b/schema/Line.py
@@ -69,17 +69,12 @@ class Line(BaseModel):
         """This makes sure the element name property is moved out and up to a one-key dictionary"""
         # Use default dump for non-line fields
         data = super().model_dump(*args, **kwargs)
-
         # Reformat 'line' field as list of single-key dicts
         new_line = []
         for elem in self.line:
-            # The element's name is the dict key; dump all other fields except 'name'
-            elem_dict = elem.model_dump(exclude={"name"}, **kwargs)
-            name = getattr(elem, "name", None)
-            if name is None:
-                raise ValueError("Element missing 'name' attribute")
-            new_line.append({name: elem_dict})
-
+            #  Use custom dump for each line element
+            elem_dict = elem.model_dump(**kwargs)[0]
+            new_line.append(elem_dict)
         data["line"] = new_line
         return data

@ax3l
Copy link
Member Author

ax3l commented Jul 16, 2025

Yes, that would be perfect! I did initially to to implement something along these lines but did not find a way, let's do your update!

Co-authored-by: Edoardo Zoni <ezoni@lbl.gov>
@ax3l ax3l merged commit 0d7fea5 into main Jul 16, 2025
3 checks passed
@ax3l ax3l deleted the line-of-dict branch July 16, 2025 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants