faster-eth-abi 5.2.12__cp314-cp314-macosx_11_0_arm64.whl → 5.2.14__cp314-cp314-macosx_11_0_arm64.whl

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.

Potentially problematic release.


This version of faster-eth-abi might be problematic. Click here for more details.

Files changed (39) hide show
  1. benchmarks/__init__.py +1 -0
  2. benchmarks/batch.py +9 -0
  3. benchmarks/data.py +313 -0
  4. benchmarks/test_abi_benchmarks.py +82 -0
  5. benchmarks/test_decoding_benchmarks.py +109 -0
  6. benchmarks/test_encoding_benchmarks.py +99 -0
  7. benchmarks/test_grammar_benchmarks.py +38 -0
  8. benchmarks/test_io_benchmarks.py +99 -0
  9. benchmarks/test_packed_benchmarks.py +41 -0
  10. benchmarks/test_registry_benchmarks.py +45 -0
  11. benchmarks/type_strings.py +26 -0
  12. faster_eth_abi/_codec.cpython-314-darwin.so +0 -0
  13. faster_eth_abi/_codec.py +1 -1
  14. faster_eth_abi/_decoding.cpython-314-darwin.so +0 -0
  15. faster_eth_abi/_decoding.py +136 -5
  16. faster_eth_abi/_encoding.cpython-314-darwin.so +0 -0
  17. faster_eth_abi/_encoding.py +141 -6
  18. faster_eth_abi/_grammar.cpython-314-darwin.so +0 -0
  19. faster_eth_abi/abi.cpython-314-darwin.so +0 -0
  20. faster_eth_abi/constants.cpython-314-darwin.so +0 -0
  21. faster_eth_abi/decoding.py +107 -96
  22. faster_eth_abi/encoding.py +55 -27
  23. faster_eth_abi/from_type_str.cpython-314-darwin.so +0 -0
  24. faster_eth_abi/packed.cpython-314-darwin.so +0 -0
  25. faster_eth_abi/registry.py +47 -31
  26. faster_eth_abi/tools/__init__.cpython-314-darwin.so +0 -0
  27. faster_eth_abi/tools/_strategies.cpython-314-darwin.so +0 -0
  28. faster_eth_abi/utils/__init__.cpython-314-darwin.so +0 -0
  29. faster_eth_abi/utils/numeric.cpython-314-darwin.so +0 -0
  30. faster_eth_abi/utils/padding.cpython-314-darwin.so +0 -0
  31. faster_eth_abi/utils/string.cpython-314-darwin.so +0 -0
  32. faster_eth_abi/utils/validation.cpython-314-darwin.so +0 -0
  33. {faster_eth_abi-5.2.12.dist-info → faster_eth_abi-5.2.14.dist-info}/METADATA +14 -2
  34. faster_eth_abi-5.2.14.dist-info/RECORD +57 -0
  35. {faster_eth_abi-5.2.12.dist-info → faster_eth_abi-5.2.14.dist-info}/top_level.txt +1 -0
  36. faster_eth_abi__mypyc.cpython-314-darwin.so +0 -0
  37. faster_eth_abi-5.2.12.dist-info/RECORD +0 -46
  38. {faster_eth_abi-5.2.12.dist-info → faster_eth_abi-5.2.14.dist-info}/WHEEL +0 -0
  39. {faster_eth_abi-5.2.12.dist-info → faster_eth_abi-5.2.14.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,11 @@
1
1
  import abc
2
2
  import decimal
3
+ from functools import (
4
+ cached_property,
5
+ )
6
+ from types import (
7
+ MethodType,
8
+ )
3
9
  from typing import (
4
10
  Any,
5
11
  Callable,
@@ -7,6 +13,7 @@ from typing import (
7
13
  Optional,
8
14
  Tuple,
9
15
  Union,
16
+ final,
10
17
  )
11
18
 
12
19
  from faster_eth_utils import (
@@ -24,13 +31,14 @@ from faster_eth_abi._decoding import (
24
31
  read_fixed_byte_size_data_from_stream,
25
32
  split_data_and_padding_fixed_byte_size,
26
33
  validate_padding_bytes_fixed_byte_size,
34
+ validate_padding_bytes_signed_integer,
35
+ validate_pointers_array,
27
36
  )
28
37
  from faster_eth_abi.base import (
29
38
  BaseCoder,
30
39
  )
31
40
  from faster_eth_abi.exceptions import (
32
41
  InsufficientDataBytes,
33
- InvalidPointer,
34
42
  NonEmptyPaddingBytes,
35
43
  )
36
44
  from faster_eth_abi.from_type_str import (
@@ -111,6 +119,10 @@ class TupleDecoder(BaseDecoder):
111
119
  self.len_of_head = sum(
112
120
  getattr(decoder, "array_size", 1) for decoder in decoders
113
121
  )
122
+ self._is_head_tail = tuple(
123
+ isinstance(decoder, HeadTailDecoder) for decoder in decoders
124
+ )
125
+ self._no_head_tail = not any(self._is_head_tail)
114
126
 
115
127
  def validate(self) -> None:
116
128
  super().validate()
@@ -118,35 +130,9 @@ class TupleDecoder(BaseDecoder):
118
130
  if self.decoders is None:
119
131
  raise ValueError("No `decoders` set")
120
132
 
133
+ @final
121
134
  def validate_pointers(self, stream: ContextFramesBytesIO) -> None:
122
- """
123
- Verify that all pointers point to a valid location in the stream.
124
- """
125
- current_location = stream.tell()
126
- end_of_offsets = current_location + 32 * self.len_of_head
127
- total_stream_length = len(stream.getbuffer())
128
- for decoder in self.decoders:
129
- if isinstance(decoder, HeadTailDecoder):
130
- # the next 32 bytes are a pointer
131
- offset = decode_uint_256(stream)
132
- indicated_idx = current_location + offset
133
- if (
134
- indicated_idx < end_of_offsets
135
- or indicated_idx >= total_stream_length
136
- ):
137
- # the pointer is indicating its data is located either within the
138
- # offsets section of the stream or beyond the end of the stream,
139
- # both of which are invalid
140
- raise InvalidPointer(
141
- "Invalid pointer in tuple at location "
142
- f"{stream.tell() - 32} in payload"
143
- )
144
- else:
145
- # the next 32 bytes are not a pointer, so progress the stream per
146
- # the decoder
147
- decoder(stream)
148
- # return the stream to its original location for actual decoding
149
- stream.seek(current_location)
135
+ raise NotImplementedError("didnt call __init__")
150
136
 
151
137
  def decode(self, stream: ContextFramesBytesIO) -> Tuple[Any, ...]:
152
138
  return decode_tuple(self, stream)
@@ -171,16 +157,13 @@ class SingleDecoder(BaseDecoder):
171
157
  if self.decoder_fn is None:
172
158
  raise ValueError("No `decoder_fn` set")
173
159
 
174
- def validate_padding_bytes(self, value, padding_bytes):
160
+ def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
175
161
  raise NotImplementedError("Must be implemented by subclasses")
176
162
 
177
- def decode(self, stream):
163
+ def decode(self, stream: ContextFramesBytesIO) -> Any:
178
164
  raw_data = self.read_data_from_stream(stream)
179
165
  data, padding_bytes = self.split_data_and_padding(raw_data)
180
- decoder_fn = self.decoder_fn
181
- if decoder_fn is None:
182
- raise AssertionError("`decoder_fn` is None")
183
- value = decoder_fn(data)
166
+ value = self.decoder_fn(data) # type: ignore [misc]
184
167
  self.validate_padding_bytes(value, padding_bytes)
185
168
 
186
169
  return value
@@ -204,6 +187,16 @@ class BaseArrayDecoder(BaseDecoder):
204
187
  item_decoder = self.item_decoder
205
188
  if item_decoder.is_dynamic:
206
189
  self.item_decoder = HeadTailDecoder(tail_decoder=item_decoder)
190
+ self.validate_pointers = MethodType(validate_pointers_array, self)
191
+ else:
192
+
193
+ def noop(stream: ContextFramesBytesIO, array_size: int) -> None:
194
+ ...
195
+
196
+ self.validate_pointers = noop
197
+
198
+ def decode(self, stream: ContextFramesBytesIO) -> Tuple[Any, ...]:
199
+ raise NotImplementedError # this is a type stub
207
200
 
208
201
  def validate(self) -> None:
209
202
  super().validate()
@@ -230,25 +223,7 @@ class BaseArrayDecoder(BaseDecoder):
230
223
  """
231
224
  Verify that all pointers point to a valid location in the stream.
232
225
  """
233
- if isinstance(self.item_decoder, HeadTailDecoder):
234
- current_location = stream.tell()
235
- end_of_offsets = current_location + 32 * array_size
236
- total_stream_length = len(stream.getbuffer())
237
- for _ in range(array_size):
238
- offset = decode_uint_256(stream)
239
- indicated_idx = current_location + offset
240
- if (
241
- indicated_idx < end_of_offsets
242
- or indicated_idx >= total_stream_length
243
- ):
244
- # the pointer is indicating its data is located either within the
245
- # offsets section of the stream or beyond the end of the stream,
246
- # both of which are invalid
247
- raise InvalidPointer(
248
- "Invalid pointer in array at location "
249
- f"{stream.tell() - 32} in payload"
250
- )
251
- stream.seek(current_location)
226
+ validate_pointers_array(self, stream, array_size)
252
227
 
253
228
 
254
229
  class SizedArrayDecoder(BaseArrayDecoder):
@@ -281,6 +256,23 @@ class FixedByteSizeDecoder(SingleDecoder):
281
256
  data_byte_size: int = None
282
257
  is_big_endian: bool = None
283
258
 
259
+ def __init__(self, *args, **kwargs):
260
+ super().__init__(*args, **kwargs)
261
+
262
+ self.read_data_from_stream = MethodType(
263
+ read_fixed_byte_size_data_from_stream, self
264
+ )
265
+ self.split_data_and_padding = MethodType(
266
+ split_data_and_padding_fixed_byte_size, self
267
+ )
268
+ self._get_value_byte_size = MethodType(get_value_byte_size, self)
269
+
270
+ # Only assign validate_padding_bytes if not overridden in subclass
271
+ if type(self).validate_padding_bytes is SingleDecoder.validate_padding_bytes:
272
+ self.validate_padding_bytes = MethodType(
273
+ validate_padding_bytes_fixed_byte_size, self
274
+ )
275
+
284
276
  def validate(self) -> None:
285
277
  super().validate()
286
278
 
@@ -304,17 +296,14 @@ class FixedByteSizeDecoder(SingleDecoder):
304
296
  raise ValueError("Value byte size exceeds data size")
305
297
 
306
298
  def read_data_from_stream(self, stream: ContextFramesBytesIO) -> bytes:
307
- return read_fixed_byte_size_data_from_stream(self, stream)
299
+ raise NotImplementedError("didnt call __init__")
308
300
 
309
301
  def split_data_and_padding(self, raw_data: bytes) -> Tuple[bytes, bytes]:
310
- return split_data_and_padding_fixed_byte_size(self, raw_data)
311
-
312
- def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
313
- validate_padding_bytes_fixed_byte_size(self, value, padding_bytes)
302
+ raise NotImplementedError("didnt call __init__")
314
303
 
304
+ # This is unused, but it is kept in to preserve the eth-abi api
315
305
  def _get_value_byte_size(self) -> int:
316
- # This is unused, but it is kept in to preserve the eth-abi api
317
- return get_value_byte_size(self)
306
+ raise NotImplementedError("didnt call __init__")
318
307
 
319
308
 
320
309
  class Fixed32ByteSizeDecoder(FixedByteSizeDecoder):
@@ -363,27 +352,29 @@ decode_uint_256 = UnsignedIntegerDecoder(value_bit_size=256)
363
352
  class SignedIntegerDecoder(Fixed32ByteSizeDecoder):
364
353
  is_big_endian = True
365
354
 
366
- def decoder_fn(self, data):
367
- value = big_endian_to_int(data)
368
- value_bit_size = self.value_bit_size
369
- if value >= 2 ** (value_bit_size - 1):
370
- return value - 2**value_bit_size
371
- else:
372
- return value
355
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
356
+ super().__init__(*args, **kwargs)
357
+
358
+ # Only assign validate_padding_bytes if not overridden in subclass
359
+ if type(self).validate_padding_bytes is SignedIntegerDecoder.validate_padding_bytes:
360
+ self.validate_padding_bytes = MethodType(validate_padding_bytes_signed_integer, self)
373
361
 
374
- def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
375
- value_byte_size = get_value_byte_size(self)
376
- padding_size = self.data_byte_size - value_byte_size
362
+ @cached_property
363
+ def neg_threshold(self) -> int:
364
+ return int(2 ** (self.value_bit_size - 1))
377
365
 
378
- if value >= 0:
379
- expected_padding_bytes = b"\x00" * padding_size
380
- else:
381
- expected_padding_bytes = b"\xff" * padding_size
366
+ @cached_property
367
+ def neg_offset(self) -> int:
368
+ return int(2**self.value_bit_size)
382
369
 
383
- if padding_bytes != expected_padding_bytes:
384
- raise NonEmptyPaddingBytes(
385
- f"Padding bytes were not empty: {padding_bytes!r}"
386
- )
370
+ def decoder_fn(self, data: bytes) -> int:
371
+ value = big_endian_to_int(data)
372
+ if value >= self.neg_threshold:
373
+ value -= self.neg_offset
374
+ return value
375
+
376
+ def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
377
+ return validate_padding_bytes_signed_integer(self, value, padding_bytes)
387
378
 
388
379
  @parse_type_str("int")
389
380
  def from_type_str(cls, abi_type, registry):
@@ -397,7 +388,7 @@ class BytesDecoder(Fixed32ByteSizeDecoder):
397
388
  is_big_endian = False
398
389
 
399
390
  @staticmethod
400
- def decoder_fn(data):
391
+ def decoder_fn(data: bytes) -> bytes:
401
392
  return data
402
393
 
403
394
  @parse_type_str("bytes")
@@ -408,6 +399,10 @@ class BytesDecoder(Fixed32ByteSizeDecoder):
408
399
  class BaseFixedDecoder(Fixed32ByteSizeDecoder):
409
400
  frac_places: int = None
410
401
  is_big_endian = True
402
+
403
+ @cached_property
404
+ def denominator(self) -> decimal.Decimal:
405
+ return TEN**self.frac_places
411
406
 
412
407
  def validate(self) -> None:
413
408
  super().validate()
@@ -417,15 +412,16 @@ class BaseFixedDecoder(Fixed32ByteSizeDecoder):
417
412
  raise ValueError("must specify `frac_places`")
418
413
 
419
414
  if frac_places <= 0 or frac_places > 80:
420
- raise ValueError("`frac_places` must be in range (0, 80]")
415
+ raise ValueError("`frac_places` must be in range (0, 80)")
421
416
 
422
417
 
423
418
  class UnsignedFixedDecoder(BaseFixedDecoder):
424
- def decoder_fn(self, data):
419
+
420
+ def decoder_fn(self, data: bytes) -> decimal.Decimal:
425
421
  value = big_endian_to_int(data)
426
422
 
427
423
  with decimal.localcontext(abi_decimal_context):
428
- decimal_value = decimal.Decimal(value) / TEN**self.frac_places
424
+ decimal_value = decimal.Decimal(value) / self.denominator
429
425
 
430
426
  return decimal_value
431
427
 
@@ -437,27 +433,42 @@ class UnsignedFixedDecoder(BaseFixedDecoder):
437
433
 
438
434
 
439
435
  class SignedFixedDecoder(BaseFixedDecoder):
440
- def decoder_fn(self, data):
436
+
437
+ @cached_property
438
+ def neg_threshold(self) -> int:
439
+ return int(2 ** (self.value_bit_size - 1))
440
+
441
+ @cached_property
442
+ def neg_offset(self) -> int:
443
+ return int(2**self.value_bit_size)
444
+
445
+ @cached_property
446
+ def expected_padding_pos(self) -> bytes:
447
+ value_byte_size = get_value_byte_size(self)
448
+ padding_size = self.data_byte_size - value_byte_size
449
+ return b"\x00" * padding_size
450
+
451
+ @cached_property
452
+ def expected_padding_neg(self) -> bytes:
453
+ value_byte_size = get_value_byte_size(self)
454
+ padding_size = self.data_byte_size - value_byte_size
455
+ return b"\xff" * padding_size
456
+
457
+ def decoder_fn(self, data: bytes) -> decimal.Decimal:
441
458
  value = big_endian_to_int(data)
442
- value_bit_size = self.value_bit_size
443
- if value >= 2 ** (value_bit_size - 1):
444
- signed_value = value - 2**value_bit_size
445
- else:
446
- signed_value = value
459
+ if value >= self.neg_threshold:
460
+ value -= self.neg_offset
447
461
 
448
462
  with decimal.localcontext(abi_decimal_context):
449
- decimal_value = decimal.Decimal(signed_value) / TEN**self.frac_places
463
+ decimal_value = decimal.Decimal(value) / self.denominator
450
464
 
451
465
  return decimal_value
452
466
 
453
467
  def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
454
- value_byte_size = get_value_byte_size(self)
455
- padding_size = self.data_byte_size - value_byte_size
456
-
457
468
  if value >= 0:
458
- expected_padding_bytes = b"\x00" * padding_size
469
+ expected_padding_bytes = self.expected_padding_pos
459
470
  else:
460
- expected_padding_bytes = b"\xff" * padding_size
471
+ expected_padding_bytes = self.expected_padding_neg
461
472
 
462
473
  if padding_bytes != expected_padding_bytes:
463
474
  raise NonEmptyPaddingBytes(
@@ -478,7 +489,7 @@ class ByteStringDecoder(SingleDecoder):
478
489
  is_dynamic = True
479
490
 
480
491
  @staticmethod
481
- def decoder_fn(data):
492
+ def decoder_fn(data: bytes) -> bytes:
482
493
  return data
483
494
 
484
495
  def read_data_from_stream(self, stream: ContextFramesBytesIO) -> bytes:
@@ -1,15 +1,23 @@
1
1
  import abc
2
2
  import codecs
3
3
  import decimal
4
+ from functools import (
5
+ cached_property,
6
+ )
7
+ from types import (
8
+ MethodType,
9
+ )
4
10
  from typing import (
5
11
  Any,
6
12
  Callable,
7
13
  ClassVar,
14
+ Final,
8
15
  NoReturn,
9
16
  Optional,
10
17
  Sequence,
11
18
  Tuple,
12
19
  Type,
20
+ final,
13
21
  )
14
22
 
15
23
  from faster_eth_utils import (
@@ -32,7 +40,11 @@ from faster_eth_abi._encoding import (
32
40
  encode_fixed,
33
41
  encode_signed,
34
42
  encode_tuple,
43
+ encode_tuple_all_dynamic,
44
+ encode_tuple_no_dynamic,
45
+ encode_tuple_no_dynamic_funcs,
35
46
  int_to_big_endian,
47
+ validate_tuple,
36
48
  )
37
49
  from faster_eth_abi.base import (
38
50
  BaseCoder,
@@ -111,7 +123,33 @@ class TupleEncoder(BaseEncoder):
111
123
  def __init__(self, encoders: Tuple[BaseEncoder, ...], **kwargs: Any) -> None:
112
124
  super().__init__(encoders=encoders, **kwargs)
113
125
 
114
- self.is_dynamic = any(getattr(e, "is_dynamic", False) for e in self.encoders)
126
+ self._is_dynamic: Final = tuple(getattr(e, "is_dynamic", False) for e in self.encoders)
127
+ self.is_dynamic = any(self._is_dynamic)
128
+
129
+ validators = []
130
+ for encoder in self.encoders:
131
+ try:
132
+ validator = encoder.validate_value
133
+ except AttributeError:
134
+ validators.append(encoder)
135
+ else:
136
+ validators.append(validator)
137
+
138
+ self.validators: Final[Callable[[Any], None]] = tuple(validators)
139
+
140
+ if type(self).encode is TupleEncoder.encode:
141
+ encode_func = (
142
+ encode_tuple_all_dynamic
143
+ if all(self._is_dynamic)
144
+ else encode_tuple_no_dynamic_funcs.get(
145
+ len(self.encoders),
146
+ encode_tuple_no_dynamic,
147
+ )
148
+ if not self.is_dynamic
149
+ else encode_tuple
150
+ )
151
+
152
+ self.encode = MethodType(encode_func, self)
115
153
 
116
154
  def validate(self) -> None:
117
155
  super().validate()
@@ -119,33 +157,15 @@ class TupleEncoder(BaseEncoder):
119
157
  if self.encoders is None:
120
158
  raise ValueError("`encoders` may not be none")
121
159
 
160
+ @final
122
161
  def validate_value(self, value: Sequence[Any]) -> None:
123
- if not is_list_like(value):
124
- self.invalidate_value(
125
- value,
126
- msg="must be list-like object such as array or tuple",
127
- )
128
-
129
- encoders = self.encoders
130
- if len(value) != len(encoders):
131
- self.invalidate_value(
132
- value,
133
- exc=ValueOutOfBounds,
134
- msg=f"value has {len(value)} items when {len(encoders)} "
135
- "were expected",
136
- )
137
-
138
- for item, encoder in zip(value, encoders):
139
- try:
140
- encoder.validate_value(item)
141
- except AttributeError:
142
- encoder(item)
162
+ validate_tuple(self, value)
143
163
 
144
164
  def encode(self, values: Sequence[Any]) -> bytes:
145
- self.validate_value(values)
146
- return encode_tuple(values, self.encoders)
165
+ return encode_tuple(self, values)
147
166
 
148
- __call__: Callable[[Self, Sequence[Any]], bytes] = encode
167
+ def __call__(self, values: Sequence[Any]) -> bytes:
168
+ return self.encode(values)
149
169
 
150
170
  @parse_tuple_type_str
151
171
  def from_type_str(cls, abi_type, registry):
@@ -328,11 +348,19 @@ class BaseFixedEncoder(NumberEncoder):
328
348
 
329
349
  return False
330
350
 
351
+ @cached_property
352
+ def denominator(self) -> decimal.Decimal:
353
+ return TEN**self.frac_places
354
+
355
+ @cached_property
356
+ def precision(self) -> int:
357
+ return TEN**-self.frac_places
358
+
331
359
  def validate_value(self, value):
332
360
  super().validate_value(value)
333
361
 
334
362
  with decimal.localcontext(abi_decimal_context):
335
- residue = value % (TEN**-self.frac_places)
363
+ residue = value % self.precision
336
364
 
337
365
  if residue > 0:
338
366
  self.invalidate_value(
@@ -359,7 +387,7 @@ class UnsignedFixedEncoder(BaseFixedEncoder):
359
387
 
360
388
  def encode_fn(self, value):
361
389
  with decimal.localcontext(abi_decimal_context):
362
- scaled_value = value * TEN**self.frac_places
390
+ scaled_value = value * self.denominator
363
391
  integer_value = int(scaled_value)
364
392
 
365
393
  return int_to_big_endian(integer_value)
@@ -392,7 +420,7 @@ class SignedFixedEncoder(BaseFixedEncoder):
392
420
 
393
421
  def encode_fn(self, value):
394
422
  with decimal.localcontext(abi_decimal_context):
395
- scaled_value = value * TEN**self.frac_places
423
+ scaled_value = value * self.denominator
396
424
  integer_value = int(scaled_value)
397
425
 
398
426
  unsigned_integer_value = integer_value % (2**self.value_bit_size)
Binary file