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.
Files changed (39) hide show
  1. {jamcodec-0.1.5 → jamcodec-0.1.7}/PKG-INFO +1 -1
  2. {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec/mixins.py +5 -1
  3. {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec/types.py +17 -17
  4. {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec.egg-info/PKG-INFO +1 -1
  5. {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec.egg-info/SOURCES.txt +1 -0
  6. jamcodec-0.1.7/test/test_collection_buffers.py +35 -0
  7. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_mixins.py +87 -1
  8. {jamcodec-0.1.5 → jamcodec-0.1.7}/LICENSE +0 -0
  9. {jamcodec-0.1.5 → jamcodec-0.1.7}/README.md +0 -0
  10. {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec/__init__.py +0 -0
  11. {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec/base.py +0 -0
  12. {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec/constants.py +0 -0
  13. {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec/exceptions.py +0 -0
  14. {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec.egg-info/dependency_links.txt +0 -0
  15. {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec.egg-info/requires.txt +0 -0
  16. {jamcodec-0.1.5 → jamcodec-0.1.7}/jamcodec.egg-info/top_level.txt +0 -0
  17. {jamcodec-0.1.5 → jamcodec-0.1.7}/pyproject.toml +0 -0
  18. {jamcodec-0.1.5 → jamcodec-0.1.7}/requirements.txt +0 -0
  19. {jamcodec-0.1.5 → jamcodec-0.1.7}/setup.cfg +0 -0
  20. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_array.py +0 -0
  21. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_bitarray.py +0 -0
  22. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_bitvec.py +0 -0
  23. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_boolean.py +0 -0
  24. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_bounded_vec.py +0 -0
  25. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_bytes.py +0 -0
  26. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_compact.py +0 -0
  27. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_enum.py +0 -0
  28. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_float.py +0 -0
  29. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_hashmap.py +0 -0
  30. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_integer.py +0 -0
  31. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_map.py +0 -0
  32. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_option.py +0 -0
  33. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_scale_types.py +0 -0
  34. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_scalebytes.py +0 -0
  35. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_string.py +0 -0
  36. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_struct.py +0 -0
  37. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_tuple.py +0 -0
  38. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_varint.py +0 -0
  39. {jamcodec-0.1.5 → jamcodec-0.1.7}/test/test_vec.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jamcodec
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: Python JAM Codec Library
5
5
  Author-email: JAMdot Technologies <dev@jamdot.tech>
6
6
  Project-URL: Homepage, https://github.com/JAMdotTech/py-jam-codec
@@ -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 = JamBytes(bytearray())
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 += scale_obj.encode(value[name])
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 = JamBytes(bytearray())
280
+ data = bytearray()
281
281
  for idx, scale_obj in enumerate(self.values):
282
282
 
283
- data += scale_obj.encode(value[idx])
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 += item
622
+ data.extend(item.data)
623
623
  else:
624
- data += self.type_def.encode(item)
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 = JamBytes(bytearray())
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 += self.type_def.encode(item)
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 += self.key_def.encode(item_key)
1049
- data += self.value_def.encode(item_value)
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jamcodec
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: Python JAM Codec Library
5
5
  Author-email: JAMdot Technologies <dev@jamdot.tech>
6
6
  Project-URL: Homepage, https://github.com/JAMdotTech/py-jam-codec
@@ -19,6 +19,7 @@ test/test_bitvec.py
19
19
  test/test_boolean.py
20
20
  test/test_bounded_vec.py
21
21
  test/test_bytes.py
22
+ test/test_collection_buffers.py
22
23
  test/test_compact.py
23
24
  test/test_enum.py
24
25
  test/test_float.py
@@ -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