diff --git a/pyignite/aio_client.py b/pyignite/aio_client.py index b0498f7..8c2ca56 100644 --- a/pyignite/aio_client.py +++ b/pyignite/aio_client.py @@ -31,7 +31,7 @@ from .exceptions import BinaryTypeError, CacheError, ReconnectError, connection_errors from .queries.query import CacheInfo from .stream import AioBinaryStream, READ_BACKWARD -from .utils import cache_id, entity_id, status_to_exception, is_wrapped +from .utils import cache_id, entity_id, status_to_exception __all__ = ['AioClient'] @@ -269,11 +269,24 @@ async def unwrap_binary(self, value: Any) -> Any: :return: the result of the Binary Object unwrapping with all other data left intact. """ - if is_wrapped(value): - blob, offset = value - with AioBinaryStream(self, blob) as stream: - data_class = await BinaryObject.parse_async(stream) - return await BinaryObject.to_python_async(stream.read_ctype(data_class, direction=READ_BACKWARD), self) + if isinstance(value, tuple) and len(value) == 2: + if type(value[0]) is bytes and type(value[1]) is int: + blob, offset = value + with AioBinaryStream(self, blob) as stream: + data_class = await BinaryObject.parse_async(stream) + return await BinaryObject.to_python_async(stream.read_ctype(data_class, direction=READ_BACKWARD), + client=self) + + if isinstance(value[0], int): + col_type, collection = value + if isinstance(collection, list): + coros = [self.unwrap_binary(v) for v in collection] + return col_type, await asyncio.gather(*coros) + + if isinstance(collection, dict): + coros = [asyncio.gather(self.unwrap_binary(k), self.unwrap_binary(v)) + for k, v in collection.items()] + return col_type, dict(await asyncio.gather(*coros)) return value @status_to_exception(CacheError) @@ -351,7 +364,7 @@ async def get_best_node( key, key_hint = self._get_affinity_key(c_id, key, key_hint) - hashcode = await key_hint.hashcode_async(key, self) + hashcode = await key_hint.hashcode_async(key, client=self) best_node = self._get_node_by_hashcode(c_id, hashcode, parts) if best_node: diff --git a/pyignite/client.py b/pyignite/client.py index 099b44d..01ee373 100644 --- a/pyignite/client.py +++ b/pyignite/client.py @@ -61,7 +61,7 @@ from .queries.query import CacheInfo from .stream import BinaryStream, READ_BACKWARD from .utils import ( - cache_id, capitalize, entity_id, schema_id, process_delimiter, status_to_exception, is_iterable, is_wrapped, + cache_id, capitalize, entity_id, schema_id, process_delimiter, status_to_exception, is_iterable, get_field_by_id, unsigned ) from .binary import GenericObjectMeta @@ -539,17 +539,26 @@ def query_binary_type(self, binary_type: Union[int, str], schema: Union[int, dic def unwrap_binary(self, value: Any) -> Any: """ - Detects and recursively unwraps Binary Object. + Detects and recursively unwraps Binary Object or collections of BinaryObject. - :param value: anything that could be a Binary Object, + :param value: anything that could be a Binary Object or collection of BinaryObject, :return: the result of the Binary Object unwrapping with all other data left intact. """ - if is_wrapped(value): - blob, offset = value - with BinaryStream(self, blob) as stream: - data_class = BinaryObject.parse(stream) - return BinaryObject.to_python(stream.read_ctype(data_class, direction=READ_BACKWARD), self) + if isinstance(value, tuple) and len(value) == 2: + if type(value[0]) is bytes and type(value[1]) is int: + blob, offset = value + with BinaryStream(self, blob) as stream: + data_class = BinaryObject.parse(stream) + return BinaryObject.to_python(stream.read_ctype(data_class, direction=READ_BACKWARD), client=self) + + if isinstance(value[0], int): + col_type, collection = value + if isinstance(collection, list): + return col_type, [self.unwrap_binary(v) for v in collection] + + if isinstance(collection, dict): + return col_type, {self.unwrap_binary(k): self.unwrap_binary(v) for k, v in collection.items()} return value @status_to_exception(CacheError) @@ -619,7 +628,7 @@ def get_best_node( return conn key, key_hint = self._get_affinity_key(c_id, key, key_hint) - hashcode = key_hint.hashcode(key, self) + hashcode = key_hint.hashcode(key, client=self) best_node = self._get_node_by_hashcode(c_id, hashcode, parts) if best_node: diff --git a/pyignite/datatypes/base.py b/pyignite/datatypes/base.py index 87b251c..5a4c780 100644 --- a/pyignite/datatypes/base.py +++ b/pyignite/datatypes/base.py @@ -48,11 +48,11 @@ class IgniteDataType(metaclass=IgniteDataTypeMeta): classes, both object and payload varieties. """ @classmethod - async def hashcode_async(cls, value, *args, **kwargs): - return cls.hashcode(value, *args, **kwargs) + async def hashcode_async(cls, value, **kwargs): + return cls.hashcode(value, **kwargs) @classmethod - def hashcode(cls, value, *args, **kwargs): + def hashcode(cls, value, **kwargs): return 0 @classmethod @@ -72,9 +72,9 @@ async def from_python_async(cls, stream, value, **kwargs): cls.from_python(stream, value, **kwargs) @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): + def to_python(cls, ctypes_object, **kwargs): raise NotImplementedError @classmethod - async def to_python_async(cls, ctypes_object, *args, **kwargs): - return cls.to_python(ctypes_object, *args, **kwargs) + async def to_python_async(cls, ctypes_object, **kwargs): + return cls.to_python(ctypes_object, **kwargs) diff --git a/pyignite/datatypes/cache_properties.py b/pyignite/datatypes/cache_properties.py index a1766f3..49327a3 100644 --- a/pyignite/datatypes/cache_properties.py +++ b/pyignite/datatypes/cache_properties.py @@ -117,12 +117,12 @@ async def parse_async(cls, stream): return cls.parse(stream) @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): - return cls.prop_data_class.to_python(ctypes_object.data, *args, **kwargs) + def to_python(cls, ctypes_object, **kwargs): + return cls.prop_data_class.to_python(ctypes_object.data, **kwargs) @classmethod - async def to_python_async(cls, ctypes_object, *args, **kwargs): - return cls.to_python(ctypes_object, *args, **kwargs) + async def to_python_async(cls, ctypes_object, **kwargs): + return cls.to_python(ctypes_object, **kwargs) @classmethod def from_python(cls, stream, value): @@ -302,6 +302,6 @@ def from_python(cls, stream, value): ) @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): + def to_python(cls, ctypes_object, **kwargs): prop_data_class = prop_map(ctypes_object.prop_code) - return prop_data_class.to_python(ctypes_object.data, *args, **kwargs) + return prop_data_class.to_python(ctypes_object.data, **kwargs) diff --git a/pyignite/datatypes/complex.py b/pyignite/datatypes/complex.py index 119c552..cddf743 100644 --- a/pyignite/datatypes/complex.py +++ b/pyignite/datatypes/complex.py @@ -90,22 +90,21 @@ def __build_final_class(cls, fields): ) @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): result = [] for i in range(ctypes_object.length): result.append( AnyDataObject.to_python( - getattr(ctypes_object, f'element_{i}'), - *args, **kwargs + getattr(ctypes_object, f'element_{i}'), **kwargs ) ) return ctypes_object.type_id, result @classmethod - async def to_python_not_null_async(cls, ctypes_object, *args, **kwargs): + async def to_python_not_null_async(cls, ctypes_object, **kwargs): result = [ await AnyDataObject.to_python_async( - getattr(ctypes_object, f'element_{i}'), *args, **kwargs + getattr(ctypes_object, f'element_{i}'), **kwargs ) for i in range(ctypes_object.length)] return ctypes_object.type_id, result @@ -223,8 +222,6 @@ class CollectionObject(Nullable): _type_id = TYPE_COL _header_class = None type_code = TC_COLLECTION - pythonic = list - default = [] @classmethod def parse_not_null(cls, stream): @@ -271,7 +268,7 @@ def __build_final_class(cls, fields): @classmethod def to_python_not_null(cls, ctypes_object, *args, **kwargs): result = [ - AnyDataObject.to_python(getattr(ctypes_object, f'element_{i}'), *args, **kwargs) + AnyDataObject.to_python(getattr(ctypes_object, f'element_{i}'), **kwargs) for i in range(ctypes_object.length) ] return ctypes_object.type, result @@ -279,7 +276,7 @@ def to_python_not_null(cls, ctypes_object, *args, **kwargs): @classmethod async def to_python_not_null_async(cls, ctypes_object, *args, **kwargs): result_coro = [ - AnyDataObject.to_python_async(getattr(ctypes_object, f'element_{i}'), *args, **kwargs) + AnyDataObject.to_python_async(getattr(ctypes_object, f'element_{i}'), **kwargs) for i in range(ctypes_object.length) ] @@ -361,35 +358,27 @@ def __build_final_class(cls, fields): ) @classmethod - def _to_python(cls, ctypes_object, *args, **kwargs): + def _to_python(cls, ctypes_object, **kwargs): map_cls = cls.__get_map_class(ctypes_object) result = map_cls() for i in range(0, ctypes_object.length << 1, 2): - k = AnyDataObject.to_python( - getattr(ctypes_object, f'element_{i}'), - *args, **kwargs - ) - v = AnyDataObject.to_python( - getattr(ctypes_object, f'element_{i + 1}'), - *args, **kwargs - ) + k = AnyDataObject.to_python(getattr(ctypes_object, f'element_{i}'), **kwargs) + v = AnyDataObject.to_python(getattr(ctypes_object, f'element_{i + 1}'), **kwargs) result[k] = v return result @classmethod - async def _to_python_async(cls, ctypes_object, *args, **kwargs): + async def _to_python_async(cls, ctypes_object, **kwargs): map_cls = cls.__get_map_class(ctypes_object) kv_pairs_coro = [ asyncio.gather( AnyDataObject.to_python_async( - getattr(ctypes_object, f'element_{i}'), - *args, **kwargs + getattr(ctypes_object, f'element_{i}'), **kwargs ), AnyDataObject.to_python_async( - getattr(ctypes_object, f'element_{i + 1}'), - *args, **kwargs + getattr(ctypes_object, f'element_{i + 1}'), **kwargs ) ) for i in range(0, ctypes_object.length << 1, 2) ] @@ -449,12 +438,12 @@ def _parse_header(cls, stream): return [('length', ctypes.c_int)], length @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): - return cls._to_python(ctypes_object, *args, **kwargs) + def to_python(cls, ctypes_object, **kwargs): + return cls._to_python(ctypes_object, **kwargs) @classmethod - async def to_python_async(cls, ctypes_object, *args, **kwargs): - return await cls._to_python_async(ctypes_object, *args, **kwargs) + async def to_python_async(cls, ctypes_object, **kwargs): + return await cls._to_python_async(ctypes_object, **kwargs) @classmethod def from_python(cls, stream, value, type_id=None): @@ -484,8 +473,6 @@ class MapObject(Nullable, _MapBase): _type_name = NAME_MAP _type_id = TYPE_MAP type_code = TC_MAP - pythonic = dict - default = {} @classmethod def parse_not_null(cls, stream): @@ -507,12 +494,12 @@ def _parse_header(cls, stream): return fields, length @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): - return ctypes_object.type, cls._to_python(ctypes_object, *args, **kwargs) + def to_python_not_null(cls, ctypes_object, **kwargs): + return ctypes_object.type, cls._to_python(ctypes_object, **kwargs) @classmethod - async def to_python_not_null_async(cls, ctypes_object, *args, **kwargs): - return ctypes_object.type, await cls._to_python_async(ctypes_object, *args, **kwargs) + async def to_python_not_null_async(cls, ctypes_object, **kwargs): + return ctypes_object.type, await cls._to_python_async(ctypes_object, **kwargs) @classmethod def from_python_not_null(cls, stream, value, **kwargs): @@ -557,7 +544,7 @@ class BinaryObject(Nullable): COMPACT_FOOTER = 0x0020 @classmethod - def hashcode(cls, value: object, client: Optional['Client']) -> int: + def hashcode(cls, value: object, client: Optional['Client'] = None) -> int: # binary objects's hashcode implementation is special in the sense # that you need to fully serialize the object to calculate # its hashcode @@ -568,7 +555,7 @@ def hashcode(cls, value: object, client: Optional['Client']) -> int: return value._hashcode @classmethod - async def hashcode_async(cls, value: object, client: Optional['AioClient']) -> int: + async def hashcode_async(cls, value: object, client: Optional['AioClient'] = None) -> int: if not value._hashcode and client: with AioBinaryStream(client) as stream: await value._from_python_async(stream, save_to_buf=True) @@ -680,7 +667,7 @@ def __build_final_class(cls, stream, header, header_class, object_fields, fields return final_class @classmethod - def to_python_not_null(cls, ctypes_object, client: 'Client' = None, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, client: 'Client' = None, **kwargs): type_id = ctypes_object.type_id if not client: raise ParseError(f'Can not query binary type {type_id}') @@ -692,14 +679,13 @@ def to_python_not_null(cls, ctypes_object, client: 'Client' = None, *args, **kwa for field_name, field_type in data_class.schema.items(): setattr( result, field_name, field_type.to_python( - getattr(ctypes_object.object_fields, field_name), - client, *args, **kwargs + getattr(ctypes_object.object_fields, field_name), client=client, **kwargs ) ) return result @classmethod - async def to_python_not_null_async(cls, ctypes_object, client: 'AioClient' = None, *args, **kwargs): + async def to_python_not_null_async(cls, ctypes_object, client: 'AioClient' = None, **kwargs): type_id = ctypes_object.type_id if not client: raise ParseError(f'Can not query binary type {type_id}') @@ -711,7 +697,7 @@ async def to_python_not_null_async(cls, ctypes_object, client: 'AioClient' = Non field_values = await asyncio.gather( *[ field_type.to_python_async( - getattr(ctypes_object.object_fields, field_name), client, *args, **kwargs + getattr(ctypes_object.object_fields, field_name), client=client, **kwargs ) for field_name, field_type in data_class.schema.items() ] diff --git a/pyignite/datatypes/expiry_policy.py b/pyignite/datatypes/expiry_policy.py index 3572754..d729da5 100644 --- a/pyignite/datatypes/expiry_policy.py +++ b/pyignite/datatypes/expiry_policy.py @@ -80,14 +80,14 @@ async def parse_async(cls, stream): return cls.parse(stream) @classmethod - def to_python(cls, ctypes_object): + def to_python(cls, ctypes_object, **kwargs): if ctypes_object == 0: return None return ExpiryPolicy(create=ctypes_object.create, update=ctypes_object.update, access=ctypes_object.access) @classmethod - async def to_python_async(cls, ctypes_object): + async def to_python_async(cls, ctypes_object, **kwargs): return cls.to_python(ctypes_object) @classmethod diff --git a/pyignite/datatypes/internal.py b/pyignite/datatypes/internal.py index 9bd1b76..54d72bf 100644 --- a/pyignite/datatypes/internal.py +++ b/pyignite/datatypes/internal.py @@ -136,15 +136,15 @@ async def parse_async(self, stream, context): return await self.var1.parse_async(stream) return await self.var2.parse_async(stream) - def to_python(self, ctypes_object, context, *args, **kwargs): + def to_python(self, ctypes_object, context, **kwargs): if self.predicate2(context): - return self.var1.to_python(ctypes_object, *args, **kwargs) - return self.var2.to_python(ctypes_object, *args, **kwargs) + return self.var1.to_python(ctypes_object, **kwargs) + return self.var2.to_python(ctypes_object, **kwargs) - async def to_python_async(self, ctypes_object, context, *args, **kwargs): + async def to_python_async(self, ctypes_object, context, **kwargs): if self.predicate2(context): - return await self.var1.to_python_async(ctypes_object, *args, **kwargs) - return await self.var2.to_python_async(ctypes_object, *args, **kwargs) + return await self.var1.to_python_async(ctypes_object, **kwargs) + return await self.var2.to_python_async(ctypes_object, **kwargs) @attr.s @@ -192,19 +192,17 @@ def build_c_type(fields): }, ) - def to_python(self, ctypes_object, *args, **kwargs): + def to_python(self, ctypes_object, **kwargs): length = getattr(ctypes_object, 'length', 0) return [ - Struct(self.following, dict_type=dict).to_python(getattr(ctypes_object, f'element_{i}'), - *args, **kwargs) + Struct(self.following, dict_type=dict).to_python(getattr(ctypes_object, f'element_{i}'), **kwargs) for i in range(length) ] - async def to_python_async(self, ctypes_object, *args, **kwargs): + async def to_python_async(self, ctypes_object, **kwargs): length = getattr(ctypes_object, 'length', 0) result_coro = [ - Struct(self.following, dict_type=dict).to_python_async(getattr(ctypes_object, f'element_{i}'), - *args, **kwargs) + Struct(self.following, dict_type=dict).to_python_async(getattr(ctypes_object, f'element_{i}'), **kwargs) for i in range(length) ] return await asyncio.gather(*result_coro) @@ -284,21 +282,21 @@ def build_c_type(fields): }, ) - def to_python(self, ctypes_object, *args, **kwargs) -> Union[dict, OrderedDict]: + def to_python(self, ctypes_object, **kwargs) -> Union[dict, OrderedDict]: result = self.dict_type() for name, c_type in self.fields: is_cond = isinstance(c_type, Conditional) result[name] = c_type.to_python( getattr(ctypes_object, name), result, - *args, **kwargs + **kwargs ) if is_cond else c_type.to_python( getattr(ctypes_object, name), - *args, **kwargs + **kwargs ) return result - async def to_python_async(self, ctypes_object, *args, **kwargs) -> Union[dict, OrderedDict]: + async def to_python_async(self, ctypes_object, **kwargs) -> Union[dict, OrderedDict]: result = self.dict_type() for name, c_type in self.fields: is_cond = isinstance(c_type, Conditional) @@ -307,12 +305,12 @@ async def to_python_async(self, ctypes_object, *args, **kwargs) -> Union[dict, O value = await c_type.to_python_async( getattr(ctypes_object, name), result, - *args, **kwargs + **kwargs ) else: value = await c_type.to_python_async( getattr(ctypes_object, name), - *args, **kwargs + **kwargs ) result[name] = value return result @@ -394,14 +392,14 @@ def __data_class_parse(cls, stream): raise ParseError('Unknown type code: `{}`'.format(type_code)) @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): + def to_python(cls, ctypes_object, **kwargs): data_class = cls.__data_class_from_ctype(ctypes_object) - return data_class.to_python(ctypes_object) + return data_class.to_python(ctypes_object, **kwargs) @classmethod - async def to_python_async(cls, ctypes_object, *args, **kwargs): + async def to_python_async(cls, ctypes_object, **kwargs): data_class = cls.__data_class_from_ctype(ctypes_object) - return await data_class.to_python_async(ctypes_object) + return await data_class.to_python_async(ctypes_object, **kwargs) @classmethod def __data_class_from_ctype(cls, ctypes_object): @@ -580,16 +578,16 @@ def build_c_type(self, fields): ) @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): + def to_python(cls, ctypes_object, **kwargs): length = getattr(ctypes_object, "length", 0) return [ - super().to_python(getattr(ctypes_object, f'element_{i}'), *args, **kwargs) + super().to_python(getattr(ctypes_object, f'element_{i}'), **kwargs) for i in range(length) ] @classmethod - async def to_python_async(cls, ctypes_object, *args, **kwargs): + async def to_python_async(cls, ctypes_object, **kwargs): length = getattr(ctypes_object, "length", 0) values = asyncio.gather( diff --git a/pyignite/datatypes/null_object.py b/pyignite/datatypes/null_object.py index 8ac47b2..d51e5fb 100644 --- a/pyignite/datatypes/null_object.py +++ b/pyignite/datatypes/null_object.py @@ -57,7 +57,7 @@ def parse(cls, stream): return cls.build_c_type() @classmethod - def to_python(cls, *args, **kwargs): + def to_python(cls, ctypes_object, **kwargs): return None @classmethod @@ -105,7 +105,7 @@ def from_python(cls, stream, value, **kwargs): if value is None: Null.from_python(stream) else: - cls.from_python_not_null(stream, value) + cls.from_python_not_null(stream, value, **kwargs) @classmethod async def from_python_async(cls, stream, value, **kwargs): @@ -115,26 +115,26 @@ async def from_python_async(cls, stream, value, **kwargs): await cls.from_python_not_null_async(stream, value, **kwargs) @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): raise NotImplementedError @classmethod - async def to_python_not_null_async(cls, ctypes_object, *args, **kwargs): - return cls.to_python_not_null(ctypes_object, *args, **kwargs) + async def to_python_not_null_async(cls, ctypes_object, **kwargs): + return cls.to_python_not_null(ctypes_object, **kwargs) @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): + def to_python(cls, ctypes_object, **kwargs): if cls.__is_null(ctypes_object): return None - return cls.to_python_not_null(ctypes_object, *args, **kwargs) + return cls.to_python_not_null(ctypes_object, **kwargs) @classmethod - async def to_python_async(cls, ctypes_object, *args, **kwargs): + async def to_python_async(cls, ctypes_object, **kwargs): if cls.__is_null(ctypes_object): return None - return await cls.to_python_not_null_async(ctypes_object, *args, **kwargs) + return await cls.to_python_not_null_async(ctypes_object, **kwargs) @classmethod def __check_null_input(cls, stream): diff --git a/pyignite/datatypes/primitive.py b/pyignite/datatypes/primitive.py index 037f680..2213f3d 100644 --- a/pyignite/datatypes/primitive.py +++ b/pyignite/datatypes/primitive.py @@ -52,7 +52,7 @@ def parse(cls, stream): return cls.c_type @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): + def to_python(cls, ctypes_object, **kwargs): return ctypes_object @@ -122,7 +122,7 @@ class Char(Primitive): c_type = ctypes.c_short @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): + def to_python(cls, ctypes_object, **kwargs): return ctypes_object.value.to_bytes( ctypes.sizeof(cls.c_type), byteorder=PROTOCOL_BYTE_ORDER @@ -147,7 +147,7 @@ class Bool(Primitive): c_type = ctypes.c_byte # Use c_byte because c_bool throws endianness conversion error on BE systems. @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): + def to_python(cls, ctypes_object, **kwargs): return ctypes_object != 0 @classmethod diff --git a/pyignite/datatypes/primitive_arrays.py b/pyignite/datatypes/primitive_arrays.py index e1d4289..fcf877c 100644 --- a/pyignite/datatypes/primitive_arrays.py +++ b/pyignite/datatypes/primitive_arrays.py @@ -67,7 +67,7 @@ def parse(cls, stream): return c_type @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): + def to_python(cls, ctypes_object, **kwargs): return [ctypes_object.data[i] for i in range(ctypes_object.length)] @classmethod @@ -88,7 +88,7 @@ class ByteArray(PrimitiveArray): type_code = TC_BYTE_ARRAY @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): + def to_python(cls, ctypes_object, **kwargs): return bytes(ctypes_object.data) @classmethod @@ -184,7 +184,7 @@ def parse_not_null(cls, stream): return c_type @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): return [ctypes_object.data[i] for i in range(ctypes_object.length)] @classmethod @@ -206,7 +206,7 @@ class ByteArrayObject(PrimitiveArrayObject): type_code = TC_BYTE_ARRAY @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): return bytes(ctypes_object.data) @classmethod @@ -277,8 +277,8 @@ class CharArrayObject(PrimitiveArrayObject): type_code = TC_CHAR_ARRAY @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): - values = super().to_python_not_null(ctypes_object, *args, **kwargs) + def to_python_not_null(cls, ctypes_object, **kwargs): + values = super().to_python_not_null(ctypes_object, **kwargs) return [ v.to_bytes( ctypes.sizeof(cls.primitive_type.c_type), @@ -296,5 +296,5 @@ class BoolArrayObject(PrimitiveArrayObject): type_code = TC_BOOL_ARRAY @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): return [ctypes_object.data[i] != 0 for i in range(ctypes_object.length)] diff --git a/pyignite/datatypes/primitive_objects.py b/pyignite/datatypes/primitive_objects.py index 9b23ec9..4e66334 100644 --- a/pyignite/datatypes/primitive_objects.py +++ b/pyignite/datatypes/primitive_objects.py @@ -65,7 +65,7 @@ def parse_not_null(cls, stream): return data_type @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): return ctypes_object.value @classmethod @@ -89,7 +89,7 @@ class ByteObject(DataObject): default = 0 @classmethod - def hashcode(cls, value: int, *args, **kwargs) -> int: + def hashcode(cls, value: int, **kwargs) -> int: return value @@ -102,7 +102,7 @@ class ShortObject(DataObject): default = 0 @classmethod - def hashcode(cls, value: int, *args, **kwargs) -> int: + def hashcode(cls, value: int, **kwargs) -> int: return value @@ -115,7 +115,7 @@ class IntObject(DataObject): default = 0 @classmethod - def hashcode(cls, value: int, *args, **kwargs) -> int: + def hashcode(cls, value: int, **kwargs) -> int: return value @@ -128,7 +128,7 @@ class LongObject(DataObject): default = 0 @classmethod - def hashcode(cls, value: int, *args, **kwargs) -> int: + def hashcode(cls, value: int, **kwargs) -> int: return value ^ (unsigned(value, ctypes.c_ulonglong) >> 32) @@ -141,7 +141,7 @@ class FloatObject(DataObject): default = 0.0 @classmethod - def hashcode(cls, value: float, *args, **kwargs) -> int: + def hashcode(cls, value: float, **kwargs) -> int: return ctypes.cast( ctypes.pointer(ctypes.c_float(value)), ctypes.POINTER(ctypes.c_int) @@ -157,7 +157,7 @@ class DoubleObject(DataObject): default = 0.0 @classmethod - def hashcode(cls, value: float, *args, **kwargs) -> int: + def hashcode(cls, value: float, **kwargs) -> int: bits = ctypes.cast( ctypes.pointer(ctypes.c_double(value)), ctypes.POINTER(ctypes.c_longlong) @@ -180,11 +180,11 @@ class CharObject(DataObject): default = ' ' @classmethod - def hashcode(cls, value: str, *args, **kwargs) -> int: + def hashcode(cls, value: str, **kwargs) -> int: return ord(value) @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): value = ctypes_object.value return value.to_bytes( ctypes.sizeof(cls.c_type), @@ -214,9 +214,9 @@ class BoolObject(DataObject): default = False @classmethod - def hashcode(cls, value: bool, *args, **kwargs) -> int: + def hashcode(cls, value: bool, **kwargs) -> int: return 1231 if value else 1237 @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): return ctypes_object.value != 0 diff --git a/pyignite/datatypes/standard.py b/pyignite/datatypes/standard.py index 5657afb..9173daa 100644 --- a/pyignite/datatypes/standard.py +++ b/pyignite/datatypes/standard.py @@ -71,7 +71,7 @@ class String(Nullable): pythonic = str @classmethod - def hashcode(cls, value: str, *args, **kwargs) -> int: + def hashcode(cls, value: str, **kwargs) -> int: return hashcode(value) @classmethod @@ -101,7 +101,7 @@ def parse_not_null(cls, stream): return data_type @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): if ctypes_object.length > 0: return ctypes_object.data.decode(PROTOCOL_STRING_ENCODING) @@ -132,7 +132,7 @@ class DecimalObject(Nullable): default = decimal.Decimal('0.00') @classmethod - def hashcode(cls, value: decimal.Decimal, *args, **kwargs) -> int: + def hashcode(cls, value: decimal.Decimal, **kwargs) -> int: return decimal_hashcode(value) @classmethod @@ -163,7 +163,7 @@ def parse_not_null(cls, stream): return data_type @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): sign = 1 if ctypes_object.data[0] & 0x80 else 0 data = ctypes_object.data[1:] data.insert(0, ctypes_object.data[0] & 0x7f) @@ -227,7 +227,7 @@ class UUIDObject(StandardObject): UUID_BYTE_ORDER = (7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8) @classmethod - def hashcode(cls, value: 'UUID', *args, **kwargs) -> int: + def hashcode(cls, value: 'UUID', **kwargs) -> int: msb = value.int >> 64 lsb = value.int & 0xffffffffffffffff hilo = msb ^ lsb @@ -263,7 +263,7 @@ def from_python_not_null(cls, stream, value: uuid.UUID, **kwargs): stream.write(data_object) @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): uuid_array = bytearray(ctypes_object.value) return uuid.UUID( bytes=bytes([uuid_array[i] for i in cls.UUID_BYTE_ORDER]) @@ -289,7 +289,7 @@ class TimestampObject(StandardObject): default = (datetime(1970, 1, 1), 0) @classmethod - def hashcode(cls, value: Tuple[datetime, int], *args, **kwargs) -> int: + def hashcode(cls, value: Tuple[datetime, int], **kwargs) -> int: return datetime_hashcode(int(value[0].timestamp() * 1000)) @classmethod @@ -323,7 +323,7 @@ def from_python_not_null(cls, stream, value: tuple, **kwargs): stream.write(data_object) @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): return ( datetime.fromtimestamp(ctypes_object.epoch / 1000), ctypes_object.fraction @@ -345,7 +345,7 @@ class DateObject(StandardObject): default = datetime(1970, 1, 1) @classmethod - def hashcode(cls, value: datetime, *args, **kwargs) -> int: + def hashcode(cls, value: datetime, **kwargs) -> int: return datetime_hashcode(int(value.timestamp() * 1000)) @classmethod @@ -379,7 +379,7 @@ def from_python_not_null(cls, stream, value: [date, datetime], **kwargs): stream.write(data_object) @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): return datetime.fromtimestamp(ctypes_object.epoch / 1000) @@ -397,7 +397,7 @@ class TimeObject(StandardObject): default = timedelta() @classmethod - def hashcode(cls, value: timedelta, *args, **kwargs) -> int: + def hashcode(cls, value: timedelta, **kwargs) -> int: return datetime_hashcode(int(value.total_seconds() * 1000)) @classmethod @@ -429,7 +429,7 @@ def from_python_not_null(cls, stream, value: timedelta, **kwargs): stream.write(data_object) @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): return timedelta(milliseconds=ctypes_object.value) @@ -476,7 +476,7 @@ def from_python_not_null(cls, stream, value: tuple, **kwargs): stream.write(data_object) @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): + def to_python_not_null(cls, ctypes_object, **kwargs): return ctypes_object.type_id, ctypes_object.ordinal @@ -570,8 +570,8 @@ def from_python(cls, stream, value, **kwargs): cls._from_python(stream, value, **kwargs) @classmethod - def to_python(cls, ctypes_object, *args, **kwargs): - return cls._to_python(ctypes_object, *args, **kwargs) + def to_python(cls, ctypes_object, **kwargs): + return cls._to_python(ctypes_object, **kwargs) class StringArray(StandardArray): @@ -660,8 +660,8 @@ def from_python_not_null(cls, stream, value, **kwargs): cls._from_python(stream, value, **kwargs) @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): - return cls._to_python(ctypes_object, *args, **kwargs) + def to_python_not_null(cls, ctypes_object, **kwargs): + return cls._to_python(ctypes_object, **kwargs) class StringArrayObject(StandardArrayObject): @@ -759,8 +759,8 @@ def from_python_not_null(cls, stream, value, **kwargs): super().from_python_not_null(stream, value, type_id=type_id) @classmethod - def to_python_not_null(cls, ctypes_object, *args, **kwargs): - return ctypes_object.type_id, cls._to_python(ctypes_object, *args, **kwargs) + def to_python_not_null(cls, ctypes_object, **kwargs): + return ctypes_object.type_id, cls._to_python(ctypes_object, **kwargs) class BinaryEnumArrayObject(EnumArrayObject): diff --git a/pyignite/queries/response.py b/pyignite/queries/response.py index c0311ec..11e71a7 100644 --- a/pyignite/queries/response.py +++ b/pyignite/queries/response.py @@ -128,25 +128,22 @@ async def _parse_success_async(self, stream, fields: list): c_type = await ignite_type.parse_async(stream) fields.append((name, c_type)) - def to_python(self, ctypes_object, *args, **kwargs): + def to_python(self, ctypes_object, **kwargs): if not self.following: return None result = OrderedDict() for name, c_type in self.following: - result[name] = c_type.to_python( - getattr(ctypes_object, name), - *args, **kwargs - ) + result[name] = c_type.to_python(getattr(ctypes_object, name), **kwargs) return result - async def to_python_async(self, ctypes_object, *args, **kwargs): + async def to_python_async(self, ctypes_object, **kwargs): if not self.following: return None values = await asyncio.gather( - *[c_type.to_python_async(getattr(ctypes_object, name), *args, **kwargs) for name, c_type in self.following] + *[c_type.to_python_async(getattr(ctypes_object, name), **kwargs) for name, c_type in self.following] ) return OrderedDict([(name, values[i]) for i, (name, _) in enumerate(self.following)]) @@ -239,9 +236,9 @@ def __body_class_post_process(body_class, fields, data_fields): ('more', ctypes.c_byte), ] - def to_python(self, ctypes_object, *args, **kwargs): + def to_python(self, ctypes_object, **kwargs): if getattr(ctypes_object, 'status_code', 0) == 0: - result = self.__to_python_result_header(ctypes_object, *args, **kwargs) + result = self.__to_python_result_header(ctypes_object, **kwargs) for row_item in ctypes_object.data._fields_: row_name = row_item[0] @@ -250,13 +247,13 @@ def to_python(self, ctypes_object, *args, **kwargs): for col_item in row_object._fields_: col_name = col_item[0] col_object = getattr(row_object, col_name) - row.append(AnyDataObject.to_python(col_object, *args, **kwargs)) + row.append(AnyDataObject.to_python(col_object, **kwargs)) result['data'].append(row) return result - async def to_python_async(self, ctypes_object, *args, **kwargs): + async def to_python_async(self, ctypes_object, **kwargs): if getattr(ctypes_object, 'status_code', 0) == 0: - result = self.__to_python_result_header(ctypes_object, *args, **kwargs) + result = self.__to_python_result_header(ctypes_object, **kwargs) data_coro = [] for row_item in ctypes_object.data._fields_: @@ -266,7 +263,7 @@ async def to_python_async(self, ctypes_object, *args, **kwargs): for col_item in row_object._fields_: col_name = col_item[0] col_object = getattr(row_object, col_name) - row_coro.append(AnyDataObject.to_python_async(col_object, *args, **kwargs)) + row_coro.append(AnyDataObject.to_python_async(col_object, **kwargs)) data_coro.append(asyncio.gather(*row_coro)) @@ -328,7 +325,7 @@ def __process_type_exists(stream, fields): return type_exists - def to_python(self, ctypes_object, *args, **kwargs): + def to_python(self, ctypes_object, **kwargs): if getattr(ctypes_object, 'status_code', 0) == 0: result = { 'type_exists': Bool.to_python(ctypes_object.type_exists) @@ -349,5 +346,5 @@ def to_python(self, ctypes_object, *args, **kwargs): } return result - async def to_python_async(self, ctypes_object, *args, **kwargs): - return self.to_python(ctypes_object, *args, **kwargs) + async def to_python_async(self, ctypes_object, **kwargs): + return self.to_python(ctypes_object, **kwargs) diff --git a/pyignite/utils.py b/pyignite/utils.py index 975f414..427cceb 100644 --- a/pyignite/utils.py +++ b/pyignite/utils.py @@ -69,13 +69,6 @@ def is_hinted(value): return isinstance(value, tuple) and len(value) == 2 and issubclass(value[1], IgniteDataType) -def is_wrapped(value: Any) -> bool: - """ - Check if a value is of WrappedDataObject type. - """ - return type(value) is tuple and len(value) == 2 and type(value[0]) is bytes and type(value[1]) is int - - def int_overflow(value: int) -> int: """ Simulates 32bit integer overflow. diff --git a/tests/common/test_binary.py b/tests/common/test_binary.py index c94c4d5..449709e 100644 --- a/tests/common/test_binary.py +++ b/tests/common/test_binary.py @@ -451,13 +451,13 @@ def complex_objects(): def test_complex_object_hash(client, complex_objects): for obj, hash in complex_objects: - assert hash == BinaryObject.hashcode(obj, client) + assert hash == BinaryObject.hashcode(obj, client=client) @pytest.mark.asyncio async def test_complex_object_hash_async(async_client, complex_objects): for obj, hash in complex_objects: - assert hash == await BinaryObject.hashcode_async(obj, async_client) + assert hash == await BinaryObject.hashcode_async(obj, client=async_client) def camel_to_snake(name): @@ -504,3 +504,55 @@ async def test_complex_object_null_fields_async(async_cache, null_fields_object) """ await async_cache.put(1, null_fields_object) assert await async_cache.get(1) == null_fields_object, 'Objects mismatch' + + +def test_object_with_collections_of_binary_objects(cache): + __check_object_with_collections_of_binary_objects(cache) + + +@pytest.mark.asyncio +async def test_object_with_collections_of_binary_objects_async(async_cache): + await __check_object_with_collections_of_binary_objects(async_cache) + + +def __check_object_with_collections_of_binary_objects(cache): + class Container( + metaclass=GenericObjectMeta, + schema={ + 'id': IntObject, + 'collection': CollectionObject, + 'array': ObjectArrayObject, + 'map': MapObject + } + ): + pass + + class Value( + metaclass=GenericObjectMeta, + schema={ + 'id': IntObject, + 'name': String + } + ): + pass + + def fixtures(): + map_obj = (MapObject.HASH_MAP, {i: Value(i, f'val_{i}') for i in range(10)}) + col_obj = (CollectionObject.ARR_LIST, [Value(i, f'val_{i}') for i in range(10)]) + arr_obj = (ObjectArrayObject.OBJECT, [Value(i, f'val_{i}') for i in range(10)]) + return [ + Container(1, map=map_obj, collection=col_obj, array=arr_obj), + Container(2), # Check if collections are not set + ] + + async def inner_async(): + for i, val in enumerate(fixtures()): + await cache.put(i, val) + assert await cache.get(i) == val + + def inner(): + for i, val in enumerate(fixtures()): + cache.put(i, val) + assert cache.get(i) == val + + return inner_async() if isinstance(cache, AioCache) else inner() diff --git a/tests/common/test_datatypes.py b/tests/common/test_datatypes.py index 6771f94..ebbafb6 100644 --- a/tests/common/test_datatypes.py +++ b/tests/common/test_datatypes.py @@ -20,6 +20,7 @@ import pytest import uuid +from pyignite import GenericObjectMeta from pyignite.datatypes import ( ByteObject, IntObject, FloatObject, CharObject, ShortObject, BoolObject, ByteArrayObject, IntArrayObject, ShortArrayObject, FloatArrayObject, BoolArrayObject, CharArrayObject, TimestampObject, String, BinaryEnumObject, @@ -27,6 +28,17 @@ ) from pyignite.utils import unsigned + +class Value( + metaclass=GenericObjectMeta, + schema={ + 'id': IntObject, + 'name': String, + } +): + pass + + put_get_data_params = [ # integers (42, None), @@ -124,6 +136,7 @@ # object array ((ObjectArrayObject.OBJECT, [1, 2, decimal.Decimal('3'), bytearray(b'\x10\x20')]), ObjectArrayObject), + ((ObjectArrayObject.OBJECT, [Value(id=i, name=f'val_{i}') for i in range(10)]), ObjectArrayObject), # collection ((CollectionObject.LINKED_LIST, [1, 2, 3]), None), diff --git a/tests/common/test_key_value.py b/tests/common/test_key_value.py index b03bec2..e26d373 100644 --- a/tests/common/test_key_value.py +++ b/tests/common/test_key_value.py @@ -17,7 +17,8 @@ import pytest -from pyignite.datatypes import CollectionObject, IntObject, MapObject, TimestampObject +from pyignite import GenericObjectMeta +from pyignite.datatypes import CollectionObject, IntObject, MapObject, TimestampObject, String def test_put_get(cache): @@ -352,16 +353,35 @@ async def test_cache_get_size_async(async_cache): assert await async_cache.get_size() == 1 +class Value( + metaclass=GenericObjectMeta, + schema={ + 'id': IntObject, + 'name': String, + } +): + pass + + collection_params = [ [ 'simple', - (1, [(123, IntObject), 678, None, 55.2, ((datetime(year=1996, month=3, day=1), 0), TimestampObject)]), - (1, [123, 678, None, 55.2, (datetime(year=1996, month=3, day=1), 0)]) + (CollectionObject.ARR_LIST, [ + (123, IntObject), 678, None, 55.2, ((datetime(year=1996, month=3, day=1), 0), TimestampObject) + ]), + (CollectionObject.ARR_LIST, [123, 678, None, 55.2, (datetime(year=1996, month=3, day=1), 0)]) ], [ 'nested', - (1, [123, ((1, [456, 'inner_test_string', 789]), CollectionObject), 'outer_test_string']), - (1, [123, (1, [456, 'inner_test_string', 789]), 'outer_test_string']) + (CollectionObject.ARR_LIST, [ + 123, ((1, [456, 'inner_test_string', 789]), CollectionObject), 'outer_test_string' + ]), + (CollectionObject.ARR_LIST, [123, (1, [456, 'inner_test_string', 789]), 'outer_test_string']) + ], + [ + 'binary', + (CollectionObject.ARR_LIST, [Value(id=i, name=f'val_{i}') for i in range(0, 10)]), + (CollectionObject.ARR_LIST, [Value(id=i, name=f'val_{i}') for i in range(0, 10)]), ], [ 'hash_map', @@ -403,6 +423,11 @@ async def test_cache_get_size_async(async_cache): } ) ], + [ + 'binary_map', + (MapObject.HASH_MAP, {i: Value(id=i, name=f"val_{i}") for i in range(10)}), + (MapObject.HASH_MAP, {i: Value(id=i, name=f"val_{i}") for i in range(10)}) + ] ]