Repository: caikit/caikit
Pull request #523 (2023-10-12 15:29:41)

Fix DataObjects with Sequence in names
**What this PR does / why we need it**:
Addresses: https://github.com/caikit/caikit/issues/443

Adds some additional checks in `DataBase.from_proto()` to ensure that we only apply sequence-specific logic to sequences that were generated by `py_to_proto` not data objects which happen to have names ending in "sequence".

Some unit tests were added to test the added functionality on edge cases which previously failed.

**If applicable**:
- [ ] this PR contains documentation
- [X] this PR contains unit tests
- [ ] this PR has been tested for backwards compatibility



Patch:
diff --git a/caikit/core/data_model/base.py b/caikit/core/data_model/base.py
index 1715825b..49d03b60 100644
--- a/caikit/core/data_model/base.py
+++ b/caikit/core/data_model/base.py
@@ -710,7 +710,17 @@ class DataBase(metaclass=_DataBaseMetaClass):
                         oneof = cls._fields_to_oneof[field]
                         contained_class = cls.get_class_for_proto(proto_attr)
                         contained_obj = contained_class.from_proto(proto_attr)
-                        kwargs[oneof] = getattr(contained_obj, "values")
+                        if hasattr(contained_obj, "values") and (
+                            contained_class.__module__.startswith(
+                                "caikit.core.data_model"
+                            )
+                            or contained_class.__module__.startswith(
+                                "caikit.interfaces.common.data_model"
+                            )
+                        ):
+                            kwargs[oneof] = getattr(contained_obj, "values")
+                        else:
+                            kwargs[oneof] = contained_obj
                     else:
                         contained_class = cls.get_class_for_proto(proto_attr)
                         contained_obj = contained_class.from_proto(proto_attr)


Test patch:
diff --git a/tests/core/data_model/test_dataobject.py b/tests/core/data_model/test_dataobject.py
index 644cffd3..89da7607 100644
--- a/tests/core/data_model/test_dataobject.py
+++ b/tests/core/data_model/test_dataobject.py
@@ -1250,6 +1250,37 @@ def test_dataobject_union_repeated():
     assert Foo.from_proto(proto=proto_repr_bar2).to_proto() == proto_repr_bar2
 
 
+def test_dataobject_complex_type_with_sequence():
+    """Ensure that dataobjects with names ending in Sequence are handled correctly"""
+
+    @dataobject
+    class FooSequence(DataObjectBase):
+        foo: List[int]
+
+    @dataobject
+    class BarSequence(DataObjectBase):
+        values: List[str]
+
+    @dataobject
+    class Baz(DataObjectBase):
+        """Union of sequences"""
+
+        series: Union[
+            Annotated[FooSequence, OneofField("ints"), FieldNumber(10)],
+            Annotated[BarSequence, OneofField("strs"), FieldNumber(20)],
+        ]
+
+    baz = Baz(series=FooSequence([1, 2, 3]))
+    assert baz.which_oneof("series") == "ints"
+    baz_foo_proto = baz.to_proto()
+    assert Baz.from_proto(baz_foo_proto).to_proto() == baz_foo_proto
+
+    baz = Baz(series=BarSequence(["a", "b", "c"]))
+    assert baz.which_oneof("series") == "strs"
+    baz_bar_proto = baz.to_proto()
+    assert Baz.from_proto(baz_bar_proto).to_proto() == baz_bar_proto
+
+
 def test_dataobject_function_inheritance():
     """Make sure inheritance works to override functionality without changing
     the schema of the parent