Repository: hanjinliu/magic-class (language: python)

Issue:
`skip_if` not passed down in container hierarchies
Hey @hanjinliu,
I wanted to use `magicclass.serialize.serialize()` to save widget states in a container, which contains nested containers. Some of these have Image widgets, and I wanted to skip these in serialization using the `skip_if` argument. The problem is that the value of `skip_if` is not passed down in the recursive call. I believe that adding it to lines 80 & 93 in `magicclass.serialize.py` would solve this issue.


Discussion / hints:
Hi @bauerdavid , thank you for reporting this issue!
Sorry for the late reply. I'll address this hopely in the next week.

Fix patch:
diff --git a/magicclass/serialize.py b/magicclass/serialize.py
index 95e00580..ff09a484 100644
--- a/magicclass/serialize.py
+++ b/magicclass/serialize.py
@@ -77,7 +77,12 @@ def _serialize_value(widget: ValueWidget | WidgetAction):
                 if (_value := _serialize_value(child)) is not _missing:
                     out[child.name] = _value
             else:
-                ser = serialize(child, skip_empty=skip_empty, skip_null=skip_null)
+                ser = serialize(
+                    child,
+                    skip_empty=skip_empty,
+                    skip_null=skip_null,
+                    skip_if=skip_if,
+                )
                 if len(ser) > 0 or not skip_empty:
                     out[child.name] = ser
             processed.add(id(child))
@@ -90,7 +95,10 @@ def _serialize_value(widget: ValueWidget | WidgetAction):
                 out[widget.name] = _value
         elif isinstance(widget, (Container, MenuGuiBase, ToolBarGui)):
             out[widget.name] = serialize(
-                widget, skip_empty=skip_empty, skip_null=skip_null
+                widget,
+                skip_empty=skip_empty,
+                skip_null=skip_null,
+                skip_if=skip_if,
             )
         elif isinstance(widget, WidgetAction) and widget.support_value:
             if (_value := _serialize_value(widget.widget)) is not _missing:
@@ -154,7 +162,9 @@ def deserialize(
                 if _is_value_widget_like(child):
                     child.value = val
                 else:
-                    deserialize(child, val)
+                    deserialize(
+                        child, val, missing_ok=missing_ok, record=record, emit=emit
+                    )
 
     for widget in ui:
         if isinstance(widget, (PushButton, Action)):
@@ -164,7 +174,9 @@ def deserialize(
                 widget.value = value
         elif isinstance(widget, (Container, MenuGuiBase, ToolBarGui)):
             if (val := _dict_get(data, widget.name, missing_ok)) is not _missing:
-                deserialize(widget, val)
+                deserialize(
+                    widget, val, missing_ok=missing_ok, record=record, emit=emit
+                )
         elif isinstance(widget, WidgetAction) and widget.support_value:
             if (value := _dict_get(data, widget.name, missing_ok)) is not _missing:
                 widget.value = value


Test patch:
diff --git a/tests/test_serialize.py b/tests/test_serialize.py
index b2b27375..dc34156c 100644
--- a/tests/test_serialize.py
+++ b/tests/test_serialize.py
@@ -198,3 +198,18 @@ class Main(MagicTemplate):
     assert ui.p.y == "c"
     assert ui.x.x == 4
     assert ui.x.y == "d"
+
+def test_serialize_with_skip_if():
+    @magicclass
+    class A(MagicTemplate):
+        x = vfield(3)
+        y = vfield("aa")
+
+        @magicclass
+        class B(MagicTemplate):
+            z = vfield(4)
+            w = vfield("bb")
+
+    ui = A()
+    d = serialize(ui, skip_if=lambda x: isinstance(x, str))
+    assert d == {"x": 3, "B": {"z": 4}}