Skip to content

Commit 8d643c8

Browse files
authored
GG-31694: Fix Null reading and writing (#12)
1 parent 4290f23 commit 8d643c8

6 files changed

Lines changed: 232 additions & 28 deletions

File tree

pygridgain/datatypes/complex.py

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@
2020

2121
from pygridgain.constants import *
2222
from pygridgain.exceptions import ParseError
23+
2324
from .base import GridGainDataType
2425
from .internal import AnyDataObject, infer_from_python
2526
from .type_codes import *
2627
from .type_ids import *
2728
from .type_names import *
29+
from .null_object import Null
2830

2931

3032
__all__ = [
@@ -68,8 +70,13 @@ def build_header(cls):
6870

6971
@classmethod
7072
def parse(cls, client: 'Client'):
73+
tc_type = client.recv(ctypes.sizeof(ctypes.c_byte))
74+
75+
if tc_type == TC_NULL:
76+
return Null.build_c_type(), tc_type
77+
7178
header_class = cls.build_header()
72-
buffer = client.recv(ctypes.sizeof(header_class))
79+
buffer = tc_type + client.recv(ctypes.sizeof(header_class) - len(tc_type))
7380
header = header_class.from_buffer_copy(buffer)
7481
fields = []
7582

@@ -91,7 +98,10 @@ def parse(cls, client: 'Client'):
9198
@classmethod
9299
def to_python(cls, ctype_object, *args, **kwargs):
93100
result = []
94-
for i in range(ctype_object.length):
101+
length = getattr(ctype_object, "length", None)
102+
if length is None:
103+
return None
104+
for i in range(length):
95105
result.append(
96106
AnyDataObject.to_python(
97107
getattr(ctype_object, 'element_{}'.format(i)),
@@ -102,6 +112,9 @@ def to_python(cls, ctype_object, *args, **kwargs):
102112

103113
@classmethod
104114
def from_python(cls, value):
115+
if value is None:
116+
return Null.from_python()
117+
105118
type_or_id, value = value
106119
header_class = cls.build_header()
107120
header = header_class()
@@ -150,8 +163,13 @@ def build_header(cls):
150163

151164
@classmethod
152165
def parse(cls, client: 'Client'):
166+
tc_type = client.recv(ctypes.sizeof(ctypes.c_byte))
167+
168+
if tc_type == TC_NULL:
169+
return Null.build_c_type(), tc_type
170+
153171
header_class = cls.build_header()
154-
buffer = client.recv(ctypes.sizeof(header_class))
172+
buffer = tc_type + client.recv(ctypes.sizeof(header_class) - len(tc_type))
155173
header = header_class.from_buffer_copy(buffer)
156174

157175
final_class = type(
@@ -243,8 +261,13 @@ def build_header(cls):
243261

244262
@classmethod
245263
def parse(cls, client: 'Client'):
264+
tc_type = client.recv(ctypes.sizeof(ctypes.c_byte))
265+
266+
if tc_type == TC_NULL:
267+
return Null.build_c_type(), tc_type
268+
246269
header_class = cls.build_header()
247-
buffer = client.recv(ctypes.sizeof(header_class))
270+
buffer = tc_type + client.recv(ctypes.sizeof(header_class) - len(tc_type))
248271
header = header_class.from_buffer_copy(buffer)
249272
fields = []
250273

@@ -266,7 +289,10 @@ def parse(cls, client: 'Client'):
266289
@classmethod
267290
def to_python(cls, ctype_object, *args, **kwargs):
268291
result = []
269-
for i in range(ctype_object.length):
292+
length = getattr(ctype_object, "length", None)
293+
if length is None:
294+
return None
295+
for i in range(length):
270296
result.append(
271297
AnyDataObject.to_python(
272298
getattr(ctype_object, 'element_{}'.format(i)),
@@ -277,6 +303,9 @@ def to_python(cls, ctype_object, *args, **kwargs):
277303

278304
@classmethod
279305
def from_python(cls, value):
306+
if value is None:
307+
return Null.from_python()
308+
280309
type_or_id, value = value
281310
header_class = cls.build_header()
282311
header = header_class()
@@ -330,8 +359,13 @@ def build_header(cls):
330359

331360
@classmethod
332361
def parse(cls, client: 'Client'):
362+
tc_type = client.recv(ctypes.sizeof(ctypes.c_byte))
363+
364+
if tc_type == TC_NULL:
365+
return Null.build_c_type(), tc_type
366+
333367
header_class = cls.build_header()
334-
buffer = client.recv(ctypes.sizeof(header_class))
368+
buffer = tc_type + client.recv(ctypes.sizeof(header_class) - len(tc_type))
335369
header = header_class.from_buffer_copy(buffer)
336370
fields = []
337371

@@ -420,12 +454,18 @@ def build_header(cls):
420454

421455
@classmethod
422456
def to_python(cls, ctype_object, *args, **kwargs):
457+
type = getattr(ctype_object, "type", None)
458+
if type is None:
459+
return None
423460
return ctype_object.type, super().to_python(
424461
ctype_object, *args, **kwargs
425462
)
426463

427464
@classmethod
428465
def from_python(cls, value):
466+
if value is None:
467+
return Null.from_python()
468+
429469
type_id, value = value
430470
return super().from_python(value, type_id)
431471

@@ -539,9 +579,13 @@ def get_dataclass(conn: 'Connection', header) -> OrderedDict:
539579
@classmethod
540580
def parse(cls, client: 'Client'):
541581
from pygridgain.datatypes import Struct
582+
tc_type = client.recv(ctypes.sizeof(ctypes.c_byte))
583+
584+
if tc_type == TC_NULL:
585+
return Null.build_c_type(), tc_type
542586

543587
header_class = cls.build_header()
544-
buffer = client.recv(ctypes.sizeof(header_class))
588+
buffer = tc_type + client.recv(ctypes.sizeof(header_class) - len(tc_type))
545589
header = header_class.from_buffer_copy(buffer)
546590

547591
# ignore full schema, always retrieve fields' types and order
@@ -572,14 +616,17 @@ def parse(cls, client: 'Client'):
572616

573617
@classmethod
574618
def to_python(cls, ctype_object, client: 'Client' = None, *args, **kwargs):
619+
type_id = getattr(ctype_object, "type_id", None)
620+
if type_id is None:
621+
return None
575622

576623
if not client:
577624
raise ParseError(
578-
'Can not query binary type {}'.format(ctype_object.type_id)
625+
'Can not query binary type {}'.format(type_id)
579626
)
580627

581628
data_class = client.query_binary_type(
582-
ctype_object.type_id,
629+
type_id,
583630
ctype_object.schema_id
584631
)
585632
result = data_class()
@@ -596,6 +643,8 @@ def to_python(cls, ctype_object, client: 'Client' = None, *args, **kwargs):
596643

597644
@classmethod
598645
def from_python(cls, value: object):
646+
if value is None:
647+
return Null.from_python()
599648

600649
if getattr(value, '_buffer', None) is None:
601650
client = cls.find_client()

pygridgain/datatypes/internal.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,10 @@ def parse(self, client: 'Client'):
479479
@classmethod
480480
def to_python(cls, ctype_object, *args, **kwargs):
481481
result = []
482-
for i in range(ctype_object.length):
482+
length = getattr(ctype_object, "length", None)
483+
if length is None:
484+
return None
485+
for i in range(length):
483486
result.append(
484487
super().to_python(
485488
getattr(ctype_object, 'element_{}'.format(i)),

pygridgain/datatypes/primitive_arrays.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from typing import Any
1818

1919
from pygridgain.constants import *
20+
from . import Null
2021
from .base import GridGainDataType
2122
from .primitive import *
2223
from .type_codes import *
@@ -61,8 +62,13 @@ def build_header_class(cls):
6162

6263
@classmethod
6364
def parse(cls, client: 'Client'):
65+
tc_type = client.recv(ctypes.sizeof(ctypes.c_byte))
66+
67+
if tc_type == TC_NULL:
68+
return Null.build_c_type(), tc_type
69+
6470
header_class = cls.build_header_class()
65-
buffer = client.recv(ctypes.sizeof(header_class))
71+
buffer = tc_type + client.recv(ctypes.sizeof(header_class) - len(tc_type))
6672
header = header_class.from_buffer_copy(buffer)
6773
final_class = type(
6874
cls.__name__,
@@ -82,12 +88,18 @@ def parse(cls, client: 'Client'):
8288
@classmethod
8389
def to_python(cls, ctype_object, *args, **kwargs):
8490
result = []
85-
for i in range(ctype_object.length):
91+
length = getattr(ctype_object, "length", None)
92+
if length is None:
93+
return None
94+
for i in range(length):
8695
result.append(ctype_object.data[i])
8796
return result
8897

8998
@classmethod
9099
def from_python(cls, value):
100+
if value is None:
101+
return Null.from_python()
102+
91103
header_class = cls.build_header_class()
92104
header = header_class()
93105
if hasattr(header, 'type_code'):
@@ -112,7 +124,10 @@ class ByteArray(PrimitiveArray):
112124

113125
@classmethod
114126
def to_python(cls, ctype_object, *args, **kwargs):
115-
return bytearray(ctype_object.data)
127+
data = getattr(ctype_object, "data", None)
128+
if data is None:
129+
return None
130+
return bytearray(data)
116131

117132
@classmethod
118133
def from_python(cls, value):
@@ -210,6 +225,9 @@ def to_python(cls, ctype_object, *args, **kwargs):
210225

211226
@classmethod
212227
def from_python(cls, value):
228+
if value is None:
229+
return Null.from_python()
230+
213231
header_class = cls.build_header_class()
214232
header = header_class()
215233
header.type_code = int.from_bytes(
@@ -282,6 +300,8 @@ class CharArrayObject(PrimitiveArrayObject):
282300
@classmethod
283301
def to_python(cls, ctype_object, *args, **kwargs):
284302
values = super().to_python(ctype_object, *args, **kwargs)
303+
if values is None:
304+
return None
285305
return [
286306
v.to_bytes(
287307
ctypes.sizeof(cls.primitive_type.c_type),
@@ -301,6 +321,9 @@ class BoolArrayObject(PrimitiveArrayObject):
301321
@classmethod
302322
def to_python(cls, ctype_object, *args, **kwargs):
303323
result = []
304-
for i in range(ctype_object.length):
324+
length = getattr(ctype_object, "length", None)
325+
if length is None:
326+
return None
327+
for i in range(length):
305328
result.append(ctype_object.data[i] != 0)
306329
return result

pygridgain/datatypes/primitive_objects.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717

1818
from pygridgain.constants import *
1919
from pygridgain.utils import unsigned
20+
2021
from .base import GridGainDataType
2122
from .type_codes import *
2223
from .type_ids import *
2324
from .type_names import *
25+
from .null_object import Null
2426

2527

2628
__all__ = [
@@ -60,16 +62,21 @@ def build_c_type(cls):
6062

6163
@classmethod
6264
def parse(cls, client: 'Client'):
65+
tc_type = client.recv(ctypes.sizeof(ctypes.c_byte))
66+
if tc_type == TC_NULL:
67+
return Null.build_c_type(), tc_type
6368
data_type = cls.build_c_type()
64-
buffer = client.recv(ctypes.sizeof(data_type))
69+
buffer = tc_type + client.recv(ctypes.sizeof(data_type) - len(tc_type))
6570
return data_type, buffer
6671

6772
@staticmethod
6873
def to_python(ctype_object, *args, **kwargs):
69-
return ctype_object.value
74+
return getattr(ctype_object, "value", None)
7075

7176
@classmethod
7277
def from_python(cls, value):
78+
if value is None:
79+
return Null.from_python()
7380
data_type = cls.build_c_type()
7481
data_object = data_type()
7582
data_object.type_code = int.from_bytes(
@@ -185,13 +192,18 @@ def hashcode(value: str, *args, **kwargs) -> int:
185192

186193
@classmethod
187194
def to_python(cls, ctype_object, *args, **kwargs):
188-
return ctype_object.value.to_bytes(
195+
value = getattr(ctype_object, "value", None)
196+
if value is None:
197+
return None
198+
return value.to_bytes(
189199
ctypes.sizeof(cls.c_type),
190200
byteorder=PROTOCOL_BYTE_ORDER
191201
).decode(PROTOCOL_CHAR_ENCODING)
192202

193203
@classmethod
194204
def from_python(cls, value):
205+
if value is None:
206+
return Null.from_python()
195207
if type(value) is str:
196208
value = value.encode(PROTOCOL_CHAR_ENCODING)
197209
# assuming either a bytes or an integer
@@ -218,5 +230,8 @@ def hashcode(value: bool, *args, **kwargs) -> int:
218230

219231
@classmethod
220232
def to_python(cls, ctype_object, *args, **kwargs):
221-
return ctype_object.value != 0
233+
value = getattr(ctype_object, "value", None)
234+
if value is None:
235+
return None
236+
return value != 0
222237

0 commit comments

Comments
 (0)