Skip to content

Commit

Permalink
Do not allow different item types while writing Compact arrays [API-1…
Browse files Browse the repository at this point in the history
…564] (#612)

* Do not allow different item types while writing Compact arrays

We have decided to only support single item type for Compact arrays.

This PR adds this check, updates the documentation and adds
a test for the change.

* address review comments
  • Loading branch information
mdumandag committed Mar 23, 2023
1 parent b728a54 commit 8125a2d
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
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"

0 comments on commit 8125a2d

Please sign in to comment.