Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions hazelcast/serialization/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2587,6 +2587,10 @@ def write_array_of_compact(
Args:
field_name: Name of the field.
value: Value to be written.

Raises:
hazelcast.errors.HazelcastSerializationError: If the list contains
different item types.
"""


Expand Down
25 changes: 24 additions & 1 deletion hazelcast/serialization/compact.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,9 +426,32 @@ def write_array_of_compact(
self, field_name: str, value: typing.Optional[typing.List[typing.Optional[typing.Any]]]
) -> None:
self._write_array_of_var_sized_fields(
field_name, FieldKind.ARRAY_OF_COMPACT, value, self._write_compact_helper
field_name,
FieldKind.ARRAY_OF_COMPACT,
value,
self._single_type_compact_item_writer(),
)

def _single_type_compact_item_writer(self):
expected_type = None

def writer(value: typing.Any):
nonlocal expected_type

item_type = type(value)
if not expected_type:
expected_type = item_type
elif expected_type != item_type:
raise HazelcastSerializationError(
f"It is not allowed to serialize an array of Compact serializable "
f"objects containing different item types. Expected array "
f"item type: {expected_type}, current item type: {item_type}"
)

self._write_compact_helper(value)

return writer

def _write_compact_helper(self, value: typing.Any) -> None:
self._compact_serializer.write(self._out, value)

Expand Down
57 changes: 57 additions & 0 deletions tests/unit/serialization/compact_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import unittest
import uuid

from mock import MagicMock
from parameterized import parameterized

from hazelcast.config import Config
Expand Down Expand Up @@ -209,6 +210,9 @@ class Child:
def __init__(self, name: str):
self.name = name

def __eq__(self, other):
return isinstance(other, Child) and self.name == other.name


class Parent:
def __init__(self, child: Child):
Expand Down Expand Up @@ -283,6 +287,37 @@ def test_serializer_with_duplicate_field_names(self):
with self.assertRaisesRegex(HazelcastSerializationError, "already exists"):
service.to_data(Child("foo"))

def test_writing_array_of_compact_with_same_item_types(self):
service = self._get_service_with_schemas(ChildSerializer(), ChildrenSerializer())

children = Children([Child("Joe"), Child("Jane"), Child("James")])
serialized = service.to_data(children)
self.assertEqual(children, service.to_object(serialized))

def test_writing_array_of_compact_with_different_item_types(self):
service = self._get_service_with_schemas(
ChildSerializer(), ChildrenSerializer(), ParentSerializer()
)

children = Children([Child("Joe"), Child("Jane"), Parent(Child("James"))])
with self.assertRaisesRegex(HazelcastSerializationError, "different item types"):
service.to_data(children)

@staticmethod
def _get_service_with_schemas(*serializers):
config = Config()
config.compact_serializers = list(serializers)
service = SerializationServiceV1(config)

for serializer in serializers:
writer = SchemaWriter(serializer.get_type_name())
serializer.write(writer, MagicMock())
service.compact_stream_serializer.register_schema_to_type(
writer.build(), serializer.get_class()
)

return service


class StringCompactSerializer(CompactSerializer[str]):
def read(self, reader: CompactReader) -> str:
Expand Down Expand Up @@ -311,3 +346,25 @@ def get_class(self) -> typing.Type[Child]:

def get_type_name(self) -> str:
return "child"


class Children:
def __init__(self, children):
self.children = children

def __eq__(self, other):
return isinstance(other, Children) and self.children == other.children


class ChildrenSerializer(CompactSerializer[Children]):
def read(self, reader: CompactReader) -> Children:
return Children(reader.read_array_of_compact("children"))

def write(self, writer: CompactWriter, obj: Children) -> None:
writer.write_array_of_compact("children", obj.children)

def get_class(self) -> typing.Type[Children]:
return Children

def get_type_name(self) -> str:
return "Children"