You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fory.apache.org by ch...@apache.org on 2025/07/10 04:05:09 UTC
(fory) branch main updated: feat(python): Add StatefulSerializer using __getstate__, __setstate__ (#2400)
This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new 44ca29c70 feat(python): Add StatefulSerializer using __getstate__, __setstate__ (#2400)
44ca29c70 is described below
commit 44ca29c70cba3a80730dc89e9bbadd2696c2768f
Author: Emre Şafak <39...@users.noreply.github.com>
AuthorDate: Thu Jul 10 00:05:04 2025 -0400
feat(python): Add StatefulSerializer using __getstate__, __setstate__ (#2400)
## What does this PR do?
* add support for objects with `__getstate__` and `__setstate__` in the
registry
* implement StatefulSerializer for cross-language, cross-version
* use StatefulSerializer for objects with `__getstate__`/`__setstate__`
(excluding pandas)
* update tests to include various stateful and argument-based objects
* test serialization/deserialization and serializer selection
correctness
* ensure nested and reference tracking scenarios are covered
* validate that objects with `__getnewargs_ex__` and `__getnewargs__`
work properly
* verify that the new serializer is used and state is restored
accurately
## Related issues
Closes #2293
## Does this PR introduce any user-facing change?
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
Not done
---------
Co-authored-by: Emre Şafak <es...@users.noreply.github.com>
---
python/pyfory/_registry.py | 12 +
python/pyfory/serializer.py | 46 ++++
python/pyfory/tests/test_stateful_reproduction.py | 152 +++++++++++
python/pyfory/tests/test_stateful_serializer.py | 307 ++++++++++++++++++++++
python/pyfory/tests/test_struct.py | 30 +--
5 files changed, 528 insertions(+), 19 deletions(-)
diff --git a/python/pyfory/_registry.py b/python/pyfory/_registry.py
index dd999ce2c..3a4cd131b 100644
--- a/python/pyfory/_registry.py
+++ b/python/pyfory/_registry.py
@@ -59,6 +59,7 @@ from pyfory.serializer import (
PickleStrongCacheSerializer,
PickleSerializer,
DataClassSerializer,
+ StatefulSerializer,
)
from pyfory.meta.metastring import MetaStringEncoder, MetaStringDecoder
from pyfory.type import (
@@ -459,6 +460,8 @@ class TypeResolver:
type_id = TypeId.NAMED_ENUM
elif type(serializer) is PickleSerializer:
type_id = PickleSerializer.PICKLE_TYPE_ID
+ elif isinstance(serializer, StatefulSerializer):
+ type_id = TypeId.NAMED_EXT
if not self.require_registration:
if isinstance(serializer, DataClassSerializer):
type_id = TypeId.NAMED_STRUCT
@@ -485,6 +488,15 @@ class TypeResolver:
serializer = DataClassSerializer(self.fory, cls)
elif issubclass(cls, enum.Enum):
serializer = EnumSerializer(self.fory, cls)
+ elif hasattr(cls, "__getstate__") and hasattr(cls, "__setstate__"):
+ # Use StatefulSerializer for objects that support __getstate__ and __setstate__
+ # But exclude certain types that have incompatible state methods
+ module_name = getattr(cls, "__module__", "")
+ if module_name.startswith("pandas."):
+ # Pandas objects have __getstate__/__setstate__ but use incompatible pickle formats
+ serializer = PickleSerializer(self.fory, cls)
+ else:
+ serializer = StatefulSerializer(self.fory, cls)
else:
serializer = PickleSerializer(self.fory, cls)
return serializer
diff --git a/python/pyfory/serializer.py b/python/pyfory/serializer.py
index 4a24829b8..a0e8ebaca 100644
--- a/python/pyfory/serializer.py
+++ b/python/pyfory/serializer.py
@@ -703,6 +703,52 @@ class BytesBufferObject(BufferObject):
return Buffer(self.binary)
+class StatefulSerializer(CrossLanguageCompatibleSerializer):
+ """
+ Serializer for objects that support __getstate__ and __setstate__.
+ Uses Fory's native serialization for better cross-language support.
+ """
+
+ def __init__(self, fory, cls):
+ super().__init__(fory, cls)
+ self.cls = cls
+ # Cache the method references as fields in the serializer.
+ self._getnewargs_ex = getattr(cls, "__getnewargs_ex__", None)
+ self._getnewargs = getattr(cls, "__getnewargs__", None)
+
+ def write(self, buffer, value):
+ state = value.__getstate__()
+ args = ()
+ kwargs = {}
+ if self._getnewargs_ex is not None:
+ args, kwargs = self._getnewargs_ex(value)
+ elif self._getnewargs is not None:
+ args = self._getnewargs(value)
+
+ # Serialize constructor arguments first
+ self.fory.serialize_ref(buffer, args)
+ self.fory.serialize_ref(buffer, kwargs)
+
+ # Then serialize the state
+ self.fory.serialize_ref(buffer, state)
+
+ def read(self, buffer):
+ args = self.fory.deserialize_ref(buffer)
+ kwargs = self.fory.deserialize_ref(buffer)
+ state = self.fory.deserialize_ref(buffer)
+
+ if args or kwargs:
+ # Case 1: __getnewargs__ was used. Re-create by calling __init__.
+ obj = self.cls(*args, **kwargs)
+ else:
+ # Case 2: Only __getstate__ was used. Create without calling __init__.
+ obj = self.cls.__new__(self.cls)
+
+ if state:
+ obj.__setstate__(state)
+ return obj
+
+
class PickleSerializer(Serializer):
PICKLE_TYPE_ID = 96
diff --git a/python/pyfory/tests/test_stateful_reproduction.py b/python/pyfory/tests/test_stateful_reproduction.py
new file mode 100644
index 000000000..55628e5ca
--- /dev/null
+++ b/python/pyfory/tests/test_stateful_reproduction.py
@@ -0,0 +1,152 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from pyfory import Fory, Language
+
+
+# Test class with __getstate__ and __setstate__
+class StatefulObject:
+ def __init__(self, value, secret=None):
+ self.value = value
+ self.secret = secret or "default_secret"
+ self.computed = self.value * 2
+
+ def __getstate__(self):
+ # Only serialize value, not secret or computed
+ return {"value": self.value}
+
+ def __setstate__(self, state):
+ self.value = state["value"]
+ self.secret = "restored_secret"
+ self.computed = self.value * 2
+
+ def __eq__(self, other):
+ return isinstance(other, StatefulObject) and self.value == other.value and self.computed == other.computed
+ # Note: secret is expected to be different after deserialization
+
+ def __repr__(self):
+ return f"StatefulObject(value={self.value}, secret={self.secret}, computed={self.computed})"
+
+
+# Test class with getnewargs_ex
+class ImmutableWithArgs:
+ def __init__(self, x, y, name="default"):
+ self._x = x
+ self._y = y
+ self._name = name
+
+ def __getnewargs_ex__(self):
+ return (self._x, self._y), {"name": self._name}
+
+ def __getstate__(self):
+ return {"extra_data": "some_extra"}
+
+ def __setstate__(self, state):
+ self._extra = state.get("extra_data", "none")
+
+ def __eq__(self, other):
+ return (
+ isinstance(other, ImmutableWithArgs)
+ and self._x == other._x
+ and self._y == other._y
+ and self._name == other._name
+ and getattr(self, "_extra", None) == getattr(other, "_extra", None)
+ )
+
+ def __repr__(self):
+ return f"ImmutableWithArgs(x={self._x}, y={self._y}, name={self._name}, extra={getattr(self, '_extra', None)})"
+
+
+# Test class with getnewargs (older style)
+class ImmutableOldStyle:
+ def __init__(self, a, b):
+ self._a = a
+ self._b = b
+
+ def __getnewargs__(self):
+ return self._a, self._b
+
+ def __getstate__(self):
+ return {"metadata": "old_style"}
+
+ def __setstate__(self, state):
+ self._metadata = state.get("metadata", "none")
+
+ def __eq__(self, other):
+ return (
+ isinstance(other, ImmutableOldStyle)
+ and self._a == other._a
+ and self._b == other._b
+ and getattr(self, "_metadata", None) == getattr(other, "_metadata", None)
+ )
+
+ def __repr__(self):
+ return f"ImmutableOldStyle(a={self._a}, b={self._b}, metadata={getattr(self, '_metadata', None)})"
+
+
+def test_current_behavior():
+ print("Testing current behavior with stateful objects...")
+
+ fory = Fory(language=Language.PYTHON, ref_tracking=True, require_type_registration=False)
+
+ # Test basic stateful object
+ obj1 = StatefulObject(42, "original_secret")
+ print(f"Original: {obj1}")
+
+ serialized = fory.serialize(obj1)
+ deserialized = fory.deserialize(serialized)
+ print(f"Deserialized: {deserialized}")
+ print(f"Equal: {obj1 == deserialized}")
+ print()
+
+ # Test with getnewargs_ex
+ obj2 = ImmutableWithArgs(10, 20, "test")
+ print(f"Original: {obj2}")
+
+ serialized2 = fory.serialize(obj2)
+ deserialized2 = fory.deserialize(serialized2)
+ print(f"Deserialized attributes: {dir(deserialized2)}")
+ print(f"Deserialized vars: {vars(deserialized2)}")
+ try:
+ print(f"Deserialized: {deserialized2}")
+ print(f"Equal: {obj2 == deserialized2}")
+ except Exception as e:
+ print(f"Error in repr/comparison: {e}")
+ print()
+
+ # Test with getnewargs (old style)
+ obj3 = ImmutableOldStyle(100, 200)
+ print(f"Original: {obj3}")
+
+ serialized3 = fory.serialize(obj3)
+ deserialized3 = fory.deserialize(serialized3)
+ print(f"Deserialized: {deserialized3}")
+ print(f"Equal: {obj3 == deserialized3}")
+ print()
+
+ # Check what serializer is being used
+ serializer1 = fory.type_resolver.get_serializer(StatefulObject)
+ serializer2 = fory.type_resolver.get_serializer(ImmutableWithArgs)
+ serializer3 = fory.type_resolver.get_serializer(ImmutableOldStyle)
+
+ print(f"StatefulObject serializer: {type(serializer1)}")
+ print(f"ImmutableWithArgs serializer: {type(serializer2)}")
+ print(f"ImmutableOldStyle serializer: {type(serializer3)}")
+
+
+if __name__ == "__main__":
+ test_current_behavior()
diff --git a/python/pyfory/tests/test_stateful_serializer.py b/python/pyfory/tests/test_stateful_serializer.py
new file mode 100644
index 000000000..a91c49e38
--- /dev/null
+++ b/python/pyfory/tests/test_stateful_serializer.py
@@ -0,0 +1,307 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import pytest
+from pyfory import Fory, Language
+from pyfory.serializer import StatefulSerializer
+
+
+class BasicStatefulObject:
+ """Basic object with __getstate__ and __setstate__"""
+
+ def __init__(self, value, secret=None):
+ self.value = value
+ self.secret = secret or "default_secret"
+ self.computed = self.value * 2
+
+ def __getstate__(self):
+ # Only serialize value, not secret or computed
+ return {"value": self.value}
+
+ def __setstate__(self, state):
+ self.value = state["value"]
+ self.secret = "restored_secret"
+ self.computed = self.value * 2
+
+ def __eq__(self, other):
+ return isinstance(other, BasicStatefulObject) and self.value == other.value and self.computed == other.computed
+ # Note: secret is expected to be different after deserialization
+
+
+class ImmutableWithArgsEx:
+ """Object with __getnewargs_ex__ and state methods"""
+
+ def __init__(self, x, y, name="default"):
+ self._x = x
+ self._y = y
+ self._name = name
+
+ def __getnewargs_ex__(self):
+ return (self._x, self._y), {"name": self._name}
+
+ def __getstate__(self):
+ return {"extra_data": "some_extra"}
+
+ def __setstate__(self, state):
+ self._extra = state.get("extra_data", "none")
+
+ def __eq__(self, other):
+ if not isinstance(other, ImmutableWithArgsEx):
+ return False
+ return (
+ self._x == other._x
+ and self._y == other._y
+ and self._name == other._name
+ and getattr(self, "_extra", None) == getattr(other, "_extra", None)
+ )
+
+
+class ImmutableWithArgs:
+ """Object with __getnewargs__ (old style) and state methods"""
+
+ def __init__(self, a, b):
+ self._a = a
+ self._b = b
+
+ def __getnewargs__(self):
+ return self._a, self._b
+
+ def __getstate__(self):
+ return {"metadata": "old_style"}
+
+ def __setstate__(self, state):
+ self._metadata = state.get("metadata", "none")
+
+ def __eq__(self, other):
+ if not isinstance(other, ImmutableWithArgs):
+ return False
+ return self._a == other._a and self._b == other._b and getattr(self, "_metadata", None) == getattr(other, "_metadata", None)
+
+
+class StatefulOnlyObject:
+ """Object with only __getstate__ and __setstate__, no constructor args"""
+
+ def __init__(self, data):
+ self.data = data
+ self.processed = f"processed_{data}"
+
+ def __getstate__(self):
+ return {"data": self.data}
+
+ def __setstate__(self, state):
+ self.data = state["data"]
+ self.processed = f"restored_{self.data}"
+
+ def __eq__(self, other):
+ if not isinstance(other, StatefulOnlyObject):
+ return False
+ return self.data == other.data and self.processed == other.processed
+
+
+class ComplexStateObject:
+ """Object with a complex state including nested objects"""
+
+ def __init__(self, name, items=None):
+ self.name = name
+ self.items = items or []
+ self.count = len(self.items)
+
+ def __getstate__(self):
+ return {"name": self.name, "items": self.items, "extra_info": {"serialized_at": "test_time"}}
+
+ def __setstate__(self, state):
+ self.name = state["name"]
+ self.items = state["items"]
+ self.count = len(self.items)
+ self.extra_info = state.get("extra_info", {})
+
+ def __eq__(self, other):
+ if not isinstance(other, ComplexStateObject):
+ return False
+ return (
+ self.name == other.name
+ and self.items == other.items
+ and self.count == other.count
+ and getattr(self, "extra_info", {}) == getattr(other, "extra_info", {})
+ )
+
+
+def test_basic_stateful_object():
+ """Test basic object with __getstate__ and __setstate__"""
+ fory = Fory(language=Language.PYTHON, ref_tracking=True, require_type_registration=False)
+
+ obj = BasicStatefulObject(42, "original_secret")
+ serialized = fory.serialize(obj)
+ deserialized = fory.deserialize(serialized)
+
+ # Check that StatefulSerializer is being used
+ serializer = fory.type_resolver.get_serializer(BasicStatefulObject)
+ assert isinstance(serializer, StatefulSerializer)
+
+ # Check deserialization correctness
+ assert deserialized.value == 42
+ assert deserialized.secret == "restored_secret" # Changed by __setstate__
+ assert deserialized.computed == 84
+ assert obj == deserialized
+
+
+def test_immutable_with_getnewargs_ex():
+ """Test object with __getnewargs_ex__"""
+ fory = Fory(language=Language.PYTHON, ref_tracking=True, require_type_registration=False)
+
+ obj = ImmutableWithArgsEx(10, 20, "test")
+ # Simulate the state that would be set by __setstate__ for comparison
+ obj._extra = "some_extra"
+
+ serialized = fory.serialize(obj)
+ deserialized = fory.deserialize(serialized)
+
+ # Check that StatefulSerializer is being used
+ serializer = fory.type_resolver.get_serializer(ImmutableWithArgsEx)
+ assert isinstance(serializer, StatefulSerializer)
+
+ # Check that constructor arguments were used correctly
+ assert deserialized._x == 10
+ assert deserialized._y == 20
+ assert deserialized._name == "test"
+ assert deserialized._extra == "some_extra" # Set by __setstate__
+ assert obj == deserialized
+
+
+def test_immutable_with_getnewargs():
+ """Test object with __getnewargs__ (old style)"""
+ fory = Fory(language=Language.PYTHON, ref_tracking=True, require_type_registration=False)
+
+ obj = ImmutableWithArgs(100, 200)
+ # Simulate the state that would be set by __setstate__ for comparison
+ obj._metadata = "old_style"
+
+ serialized = fory.serialize(obj)
+ deserialized = fory.deserialize(serialized)
+
+ # Check that StatefulSerializer is being used
+ serializer = fory.type_resolver.get_serializer(ImmutableWithArgs)
+ assert isinstance(serializer, StatefulSerializer)
+
+ # Check that constructor arguments were used correctly
+ assert deserialized._a == 100
+ assert deserialized._b == 200
+ assert deserialized._metadata == "old_style" # Set by __setstate__
+ assert obj == deserialized
+
+
+def test_stateful_only_object():
+ """Test object with only state methods, no constructor args"""
+ fory = Fory(language=Language.PYTHON, ref_tracking=True, require_type_registration=False)
+
+ obj = StatefulOnlyObject("test_data")
+ # Simulate the state that would be set by __setstate__ for comparison
+ obj.processed = "restored_test_data"
+
+ serialized = fory.serialize(obj)
+ deserialized = fory.deserialize(serialized)
+
+ # Check that StatefulSerializer is being used
+ serializer = fory.type_resolver.get_serializer(StatefulOnlyObject)
+ assert isinstance(serializer, StatefulSerializer)
+
+ # Check deserialization correctness
+ assert deserialized.data == "test_data"
+ assert deserialized.processed == "restored_test_data" # Changed by __setstate__
+ assert obj == deserialized
+
+
+def test_complex_state_object():
+ """Test object with a complex nested state"""
+ fory = Fory(language=Language.PYTHON, ref_tracking=True, require_type_registration=False)
+
+ obj = ComplexStateObject("test", [1, 2, 3, {"nested": "value"}])
+ # Simulate the state that would be set by __setstate__ for comparison
+ obj.extra_info = {"serialized_at": "test_time"}
+
+ serialized = fory.serialize(obj)
+ deserialized = fory.deserialize(serialized)
+
+ # Check that StatefulSerializer is being used
+ serializer = fory.type_resolver.get_serializer(ComplexStateObject)
+ assert isinstance(serializer, StatefulSerializer)
+
+ # Check deserialization correctness
+ assert deserialized.name == "test"
+ assert deserialized.items == [1, 2, 3, {"nested": "value"}]
+ assert deserialized.count == 4
+ assert deserialized.extra_info == {"serialized_at": "test_time"}
+ assert obj == deserialized
+
+
+def test_reference_tracking():
+ """Test that reference tracking works with StatefulSerializer"""
+ fory = Fory(language=Language.PYTHON, ref_tracking=True, require_type_registration=False)
+
+ obj = BasicStatefulObject(42)
+ # Create a list with the same object referenced twice
+ container = [obj, obj, {"ref": obj}]
+
+ serialized = fory.serialize(container)
+ deserialized = fory.deserialize(serialized)
+
+ # Check that references are preserved
+ assert deserialized[0] is deserialized[1]
+ assert deserialized[0] is deserialized[2]["ref"]
+ assert deserialized[0].value == 42
+
+
+def test_nested_stateful_objects():
+ """Test serialization of nested stateful objects"""
+ fory = Fory(language=Language.PYTHON, ref_tracking=True, require_type_registration=False)
+
+ inner = BasicStatefulObject(10)
+ outer = ComplexStateObject("outer", [inner, BasicStatefulObject(20)])
+
+ serialized = fory.serialize(outer)
+ deserialized = fory.deserialize(serialized)
+
+ # Check that nested objects are correctly deserialized
+ assert deserialized.name == "outer"
+ assert len(deserialized.items) == 2
+ assert isinstance(deserialized.items[0], BasicStatefulObject)
+ assert isinstance(deserialized.items[1], BasicStatefulObject)
+ assert deserialized.items[0].value == 10
+ assert deserialized.items[1].value == 20
+
+
+def test_cross_language_compatibility():
+ """Test that StatefulSerializer works with type registration"""
+ fory = Fory(language=Language.PYTHON, ref_tracking=True, require_type_registration=True)
+
+ # Register the type explicitly
+ fory.register_type(BasicStatefulObject)
+
+ obj = BasicStatefulObject(42)
+ serialized = fory.serialize(obj)
+
+ # Deserialize with a new Fory instance that also has the type registered
+ fory_new = Fory(language=Language.PYTHON, ref_tracking=True, require_type_registration=True)
+ fory_new.register_type(BasicStatefulObject)
+ deserialized = fory_new.deserialize(serialized)
+
+ assert deserialized.value == 42
+ assert obj == deserialized
+
+
+if __name__ == "__main__":
+ pytest.main([__file__, "-v"])
diff --git a/python/pyfory/tests/test_struct.py b/python/pyfory/tests/test_struct.py
index 5be25cb45..e212cec04 100644
--- a/python/pyfory/tests/test_struct.py
+++ b/python/pyfory/tests/test_struct.py
@@ -104,19 +104,14 @@ def test_inheritance():
type_hints = typing.get_type_hints(ChildClass1)
print(type_hints)
assert type_hints.keys() == {"f1", "f2", "f3"}
- fory = Fory(
- language=Language.PYTHON, ref_tracking=True, require_type_registration=False
- )
+ fory = Fory(language=Language.PYTHON, ref_tracking=True, require_type_registration=False)
obj = ChildClass1(f1="a", f2=-10, f3={"a": -10.0, "b": 1 / 3})
assert ser_de(fory, obj) == obj
- assert (
- type(fory.type_resolver.get_serializer(ChildClass1))
- == pyfory.DataClassSerializer
- )
+ assert type(fory.type_resolver.get_serializer(ChildClass1)) is pyfory.DataClassSerializer
@dataclass
-class TestDataClassObject:
+class DataClassObject:
f_int: int
f_float: float
f_str: str
@@ -130,7 +125,7 @@ class TestDataClassObject:
def test_data_class_serializer_xlang():
fory = Fory(language=Language.XLANG, ref_tracking=True)
fory.register_type(ComplexObject, typename="example.ComplexObject")
- fory.register_type(TestDataClassObject, typename="example.TestDataClassObject")
+ fory.register_type(DataClassObject, typename="example.TestDataClassObject")
complex_data = ComplexObject(
f1="nested_str",
@@ -138,7 +133,7 @@ def test_data_class_serializer_xlang():
f8=3.14,
f10={10: 1.0, 20: 2.0},
)
- obj_original = TestDataClassObject(
+ obj_original = DataClassObject(
f_int=123,
f_float=45.67,
f_str="hello xlang",
@@ -160,21 +155,18 @@ def test_data_class_serializer_xlang():
assert obj_deserialized.f_dict == obj_original.f_dict
assert obj_deserialized.f_any == obj_original.f_any
assert obj_deserialized.f_complex == obj_original.f_complex
- assert (
- type(fory.type_resolver.get_serializer(TestDataClassObject))
- == pyfory.DataClassSerializer
- )
- # Ensure it's using xlang mode (indirectly, by checking no JIT methods if possible,
+ assert type(fory.type_resolver.get_serializer(DataClassObject)) is pyfory.DataClassSerializer
+ # Ensure it's using xlang mode indirectly, by checking no JIT methods if possible,
# or by ensuring it was registered with _register_xtype which now uses DataClassSerializer(xlang=True)
# For now, the registration path check is implicit via Language.XLANG usage.
# We can also check if the hash is non-zero if it was computed,
- # or if _serializers attribute exists.
- serializer_instance = fory.type_resolver.get_serializer(TestDataClassObject)
+ # or if the _serializers attribute exists.
+ serializer_instance = fory.type_resolver.get_serializer(DataClassObject)
assert hasattr(serializer_instance, "_serializers") # xlang mode creates this
assert serializer_instance._xlang is True
- # Test with None for complex field
- obj_with_none_complex = TestDataClassObject(
+ # Test with None for a complex field
+ obj_with_none_complex = DataClassObject(
f_int=789,
f_float=12.34,
f_str="another string",
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@fory.apache.org
For additional commands, e-mail: commits-help@fory.apache.org