jamcodec 0.1.5__tar.gz → 0.1.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {jamcodec-0.1.5 → jamcodec-0.1.7}/PKG-INFO +1 -1
- {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec/mixins.py +5 -1
- {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec/types.py +17 -17
- {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec.egg-info/PKG-INFO +1 -1
- {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec.egg-info/SOURCES.txt +1 -0
- jamcodec-0.1.7/test/test_collection_buffers.py +35 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_mixins.py +87 -1
- {jamcodec-0.1.5 → jamcodec-0.1.7}/LICENSE +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/README.md +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec/__init__.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec/base.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec/constants.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec/exceptions.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec.egg-info/dependency_links.txt +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec.egg-info/requires.txt +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec.egg-info/top_level.txt +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/pyproject.toml +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/requirements.txt +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/setup.cfg +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_array.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_bitarray.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_bitvec.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_boolean.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_bounded_vec.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_bytes.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_compact.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_enum.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_float.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_hashmap.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_integer.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_map.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_option.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_scale_types.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_scalebytes.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_string.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_struct.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_tuple.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_varint.py +0 -0
- {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_vec.py +0 -0
|
@@ -6,7 +6,7 @@ import typing
|
|
|
6
6
|
import json
|
|
7
7
|
|
|
8
8
|
from jamcodec.base import JamCodecTypeDef, JamCodecType, JamBytes
|
|
9
|
-
from jamcodec.types import Struct, Option, Vec, Enum
|
|
9
|
+
from jamcodec.types import Struct, StructObject, Option, Vec, Enum
|
|
10
10
|
|
|
11
11
|
T = TypeVar('T')
|
|
12
12
|
|
|
@@ -69,6 +69,10 @@ class Serializable:
|
|
|
69
69
|
|
|
70
70
|
def to_jam_bytes(self) -> JamBytes:
|
|
71
71
|
scale_obj = self.to_codec_type()
|
|
72
|
+
if type(scale_obj) in (JamCodecType, StructObject):
|
|
73
|
+
# The value is already normalized; only the encoded bytes are needed.
|
|
74
|
+
return scale_obj.type_def.encode(scale_obj.value_serialized)
|
|
75
|
+
# Preserve encoding and validation hooks on custom codec wrappers.
|
|
72
76
|
return scale_obj.encode()
|
|
73
77
|
|
|
74
78
|
@classmethod
|
|
@@ -197,18 +197,18 @@ class Struct(JamCodecTypeDef):
|
|
|
197
197
|
|
|
198
198
|
def encode(self, value: dict) -> JamBytes:
|
|
199
199
|
|
|
200
|
-
data =
|
|
200
|
+
data = bytearray()
|
|
201
201
|
for name, scale_obj in self.arguments.items():
|
|
202
202
|
|
|
203
203
|
if name not in value:
|
|
204
204
|
raise ScaleEncodeException(f'Argument "{name}" of Struct is missing in given value')
|
|
205
205
|
|
|
206
|
-
data
|
|
206
|
+
data.extend(scale_obj.encode(value[name]).data)
|
|
207
207
|
|
|
208
208
|
if value[name] and issubclass(value[name].__class__, JamCodecType):
|
|
209
209
|
value[name] = value[name].serialize()
|
|
210
210
|
|
|
211
|
-
return data
|
|
211
|
+
return JamBytes(data)
|
|
212
212
|
|
|
213
213
|
def decode(self, data) -> dict:
|
|
214
214
|
value = {}
|
|
@@ -277,11 +277,11 @@ class Tuple(JamCodecTypeDef):
|
|
|
277
277
|
if type(value) is not tuple:
|
|
278
278
|
value = (value,)
|
|
279
279
|
|
|
280
|
-
data =
|
|
280
|
+
data = bytearray()
|
|
281
281
|
for idx, scale_obj in enumerate(self.values):
|
|
282
282
|
|
|
283
|
-
data
|
|
284
|
-
return data
|
|
283
|
+
data.extend(scale_obj.encode(value[idx]).data)
|
|
284
|
+
return JamBytes(data)
|
|
285
285
|
|
|
286
286
|
def decode(self, data: JamBytes) -> tuple:
|
|
287
287
|
value = ()
|
|
@@ -615,17 +615,17 @@ class Vec(JamCodecTypeDef):
|
|
|
615
615
|
return Bytes.encode(value)
|
|
616
616
|
|
|
617
617
|
# Encode length of Vec
|
|
618
|
-
data = VarInt64.encode(len(value))
|
|
618
|
+
data = VarInt64.encode(len(value)).data
|
|
619
619
|
|
|
620
620
|
for idx, item in enumerate(value):
|
|
621
621
|
if type(item) is JamBytes:
|
|
622
|
-
data
|
|
622
|
+
data.extend(item.data)
|
|
623
623
|
else:
|
|
624
|
-
data
|
|
624
|
+
data.extend(self.type_def.encode(item).data)
|
|
625
625
|
if item and issubclass(item.__class__, JamCodecType):
|
|
626
626
|
value[idx] = item.serialize()
|
|
627
627
|
|
|
628
|
-
return data
|
|
628
|
+
return JamBytes(data)
|
|
629
629
|
|
|
630
630
|
def decode(self, data: JamBytes) -> Union[list, bytes]:
|
|
631
631
|
|
|
@@ -823,7 +823,7 @@ class Array(JamCodecTypeDef):
|
|
|
823
823
|
|
|
824
824
|
return JamBytes(value)
|
|
825
825
|
else:
|
|
826
|
-
data =
|
|
826
|
+
data = bytearray()
|
|
827
827
|
|
|
828
828
|
if type(value) is not list:
|
|
829
829
|
raise ScaleEncodeException("Value must be of type list")
|
|
@@ -832,9 +832,9 @@ class Array(JamCodecTypeDef):
|
|
|
832
832
|
raise ScaleEncodeException("Length of list does not match size of array")
|
|
833
833
|
|
|
834
834
|
for item in value:
|
|
835
|
-
data
|
|
835
|
+
data.extend(self.type_def.encode(item).data)
|
|
836
836
|
|
|
837
|
-
return data
|
|
837
|
+
return JamBytes(data)
|
|
838
838
|
|
|
839
839
|
def decode(self, data: JamBytes) -> Union[list, bytes]:
|
|
840
840
|
if self.type_def is U8:
|
|
@@ -1042,13 +1042,13 @@ class Map(JamCodecTypeDef):
|
|
|
1042
1042
|
value = sorted(value)
|
|
1043
1043
|
|
|
1044
1044
|
# Encode length of Vec
|
|
1045
|
-
data = VarInt64.encode(len(value))
|
|
1045
|
+
data = VarInt64.encode(len(value)).data
|
|
1046
1046
|
|
|
1047
1047
|
for item_key, item_value in value:
|
|
1048
|
-
data
|
|
1049
|
-
data
|
|
1048
|
+
data.extend(self.key_def.encode(item_key).data)
|
|
1049
|
+
data.extend(self.value_def.encode(item_value).data)
|
|
1050
1050
|
|
|
1051
|
-
return data
|
|
1051
|
+
return JamBytes(data)
|
|
1052
1052
|
|
|
1053
1053
|
def decode(self, data: JamBytes) -> list:
|
|
1054
1054
|
# Decode length of Map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from jamcodec.base import JamBytes
|
|
4
|
+
from jamcodec.types import Array, Map, Struct, Tuple, U16, Vec
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.parametrize("codec,value,expected", [
|
|
8
|
+
(Struct(first=U16, second=U16), {"first": 1, "second": 2}, b"\x01\x00\x02\x00"),
|
|
9
|
+
(Tuple(U16, U16), (1, 2), b"\x01\x00\x02\x00"),
|
|
10
|
+
(Array(U16, 2), [1, 2], b"\x01\x00\x02\x00"),
|
|
11
|
+
(Vec(U16), [1, 2], b"\x02\x01\x00\x02\x00"),
|
|
12
|
+
(Map(U16, U16), {2: 1, 1: 2}, b"\x02\x01\x00\x02\x00\x02\x00\x01\x00"),
|
|
13
|
+
])
|
|
14
|
+
def test_collection_encoding_has_complete_readable_buffer(codec, value, expected):
|
|
15
|
+
encoded = codec.encode(value)
|
|
16
|
+
|
|
17
|
+
assert encoded.to_bytes() == expected
|
|
18
|
+
assert encoded.offset == 0
|
|
19
|
+
assert encoded.length == len(expected)
|
|
20
|
+
codec.new().decode(encoded, check_remaining=True)
|
|
21
|
+
assert encoded.get_remaining_length() == 0
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_vec_does_not_mutate_or_share_preencoded_item_buffer():
|
|
25
|
+
item = JamBytes(b"\x01\x00")
|
|
26
|
+
item.get_next_bytes(1)
|
|
27
|
+
encoded = Vec(U16).encode([item, item])
|
|
28
|
+
|
|
29
|
+
assert item.to_bytes() == b"\x01\x00"
|
|
30
|
+
assert item.offset == 1
|
|
31
|
+
assert encoded.to_bytes() == b"\x02\x01\x00\x01\x00"
|
|
32
|
+
item.data[0] = 9
|
|
33
|
+
assert encoded.to_bytes() == b"\x02\x01\x00\x01\x00"
|
|
34
|
+
encoded.data[1] = 7
|
|
35
|
+
assert item.to_bytes() == b"\x09\x00"
|
|
@@ -7,8 +7,9 @@ from os import path
|
|
|
7
7
|
from typing import Optional, Type, Union, List
|
|
8
8
|
|
|
9
9
|
from jamcodec.base import JamBytes, JamCodecType
|
|
10
|
+
from jamcodec.exceptions import ScaleDeserializeException, ScaleEncodeException
|
|
10
11
|
from jamcodec.mixins import Serializable, T
|
|
11
|
-
from jamcodec.types import H256, U8, Array, Enum, Option, Null, Bytes
|
|
12
|
+
from jamcodec.types import H256, U8, U16, Array, Enum, Option, Null, Bytes, Map, Struct, StructObject, Vec
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
# Test definitions
|
|
@@ -151,6 +152,91 @@ class TestSerializableMixin(unittest.TestCase):
|
|
|
151
152
|
|
|
152
153
|
self.assertEqual(self.test_obj, validator_obj)
|
|
153
154
|
|
|
155
|
+
def test_binary_enum_encodings(self):
|
|
156
|
+
cases = (
|
|
157
|
+
(CustomErrorCode.bad_slot, b'\x00'),
|
|
158
|
+
(CustomErrorCode.duplicate_ticket, b'\x06'),
|
|
159
|
+
(Output(ok=OutputMarks(epoch_mark=None)), b'\x00\x00'),
|
|
160
|
+
(Output(err=CustomErrorCode.duplicate_ticket), b'\x01\x06'),
|
|
161
|
+
(WorkExecResult(ok=b''), b'\x00\x00'),
|
|
162
|
+
(WorkExecResult(ok=b'abc'), b'\x00\x03abc'),
|
|
163
|
+
(WorkExecResult(out_of_gas=Null), b'\x01'),
|
|
164
|
+
)
|
|
165
|
+
for value, expected in cases:
|
|
166
|
+
with self.subTest(value=value):
|
|
167
|
+
serialized = value.serialize()
|
|
168
|
+
encoded = value.to_jam_bytes()
|
|
169
|
+
self.assertEqual(expected, encoded.to_bytes())
|
|
170
|
+
self.assertEqual(0, encoded.offset)
|
|
171
|
+
self.assertEqual(serialized, value.serialize())
|
|
172
|
+
|
|
173
|
+
def test_binary_nested_collections_roundtrip(self):
|
|
174
|
+
@dataclass
|
|
175
|
+
class Packet(Serializable):
|
|
176
|
+
payload: bytes = field(metadata={'codec': Bytes})
|
|
177
|
+
numbers: list = field(metadata={'codec': Vec(Option(U16))})
|
|
178
|
+
labels: dict = field(metadata={'codec': Map(U8, Bytes)})
|
|
179
|
+
|
|
180
|
+
packet = Packet(b'abc', [None, 258], {2: b'b', 1: b'a'})
|
|
181
|
+
encoded = packet.to_jam_bytes()
|
|
182
|
+
self.assertEqual(b'\x03abc\x02\x00\x01\x02\x01\x02\x01\x01a\x02\x01b', encoded.to_bytes())
|
|
183
|
+
self.assertEqual(packet, Packet.from_jam_bytes(encoded))
|
|
184
|
+
self.assertEqual(encoded.length, encoded.offset)
|
|
185
|
+
|
|
186
|
+
def test_binary_encoding_returns_independent_buffers(self):
|
|
187
|
+
value = Output(ok=OutputMarks(epoch_mark=None))
|
|
188
|
+
first = value.to_jam_bytes()
|
|
189
|
+
first.get_next_bytes(1)
|
|
190
|
+
second = value.to_jam_bytes()
|
|
191
|
+
|
|
192
|
+
self.assertEqual(1, first.offset)
|
|
193
|
+
self.assertEqual(0, second.offset)
|
|
194
|
+
second.data[0] = 99
|
|
195
|
+
self.assertEqual(b'\x00\x00', first.to_bytes())
|
|
196
|
+
self.assertEqual(b'\x00\x00', value.to_jam_bytes().to_bytes())
|
|
197
|
+
|
|
198
|
+
value.ok.epoch_mark = EpochMark(bytes(32), [bytes(32)] * 6)
|
|
199
|
+
self.assertEqual(b'\x00\x01' + bytes(32 * 7), value.to_jam_bytes().to_bytes())
|
|
200
|
+
self.assertEqual(b'\x00\x00', first.to_bytes())
|
|
201
|
+
|
|
202
|
+
def test_binary_encoding_preserves_validation(self):
|
|
203
|
+
@dataclass
|
|
204
|
+
class Packet(Serializable):
|
|
205
|
+
number: int = field(metadata={'codec': U8})
|
|
206
|
+
digest: bytes = field(metadata={'codec': H256})
|
|
207
|
+
|
|
208
|
+
for value in (Packet(256, bytes(32)), Packet(1, b'short')):
|
|
209
|
+
with self.subTest(value=value):
|
|
210
|
+
with self.assertRaises(ScaleEncodeException):
|
|
211
|
+
value.to_jam_bytes()
|
|
212
|
+
|
|
213
|
+
def test_binary_encoding_preserves_custom_encoder(self):
|
|
214
|
+
class PrefixedCodec(StructObject):
|
|
215
|
+
def encode(self, value=None):
|
|
216
|
+
return JamBytes(b'\xaa' + super().encode(value).to_bytes())
|
|
217
|
+
|
|
218
|
+
@dataclass
|
|
219
|
+
class Packet(Serializable):
|
|
220
|
+
number: int
|
|
221
|
+
_codec_type_def = Struct(number=U8).impl(PrefixedCodec)
|
|
222
|
+
|
|
223
|
+
self.assertEqual(b'\xaa\x07', Packet(7).to_jam_bytes().to_bytes())
|
|
224
|
+
|
|
225
|
+
def test_binary_encoding_preserves_custom_validation_hook(self):
|
|
226
|
+
class ValidatedCodec(StructObject):
|
|
227
|
+
def deserialize(self, value):
|
|
228
|
+
if self._data is not None:
|
|
229
|
+
raise ScaleDeserializeException('custom post-encode validation')
|
|
230
|
+
return super().deserialize(value)
|
|
231
|
+
|
|
232
|
+
@dataclass
|
|
233
|
+
class Packet(Serializable):
|
|
234
|
+
number: int
|
|
235
|
+
_codec_type_def = Struct(number=U8).impl(ValidatedCodec)
|
|
236
|
+
|
|
237
|
+
with self.assertRaisesRegex(ScaleDeserializeException, 'custom post-encode validation'):
|
|
238
|
+
Packet(7).to_jam_bytes()
|
|
239
|
+
|
|
154
240
|
|
|
155
241
|
if __name__ == '__main__':
|
|
156
242
|
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|