faster-eth-abi 5.2.10__tar.gz → 5.2.11__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.

Potentially problematic release.


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

Files changed (78) hide show
  1. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/PKG-INFO +1 -1
  2. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/_codec.py +0 -4
  3. faster_eth_abi-5.2.10/faster_eth_abi/grammar.py → faster_eth_abi-5.2.11/faster_eth_abi/_grammar.py +45 -164
  4. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/codec.py +8 -9
  5. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/from_type_str.py +3 -1
  6. faster_eth_abi-5.2.11/faster_eth_abi/grammar.py +170 -0
  7. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/registry.py +37 -24
  8. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/tools/_strategies.py +9 -5
  9. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi.egg-info/PKG-INFO +1 -1
  10. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi.egg-info/SOURCES.txt +1 -0
  11. faster_eth_abi-5.2.11/faster_eth_abi.egg-info/top_level.txt +2 -0
  12. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/pyproject.toml +2 -2
  13. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/setup.py +2 -1
  14. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/abi_tests/test_decode.py +11 -4
  15. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/abi_tests/test_encode.py +14 -3
  16. faster_eth_abi-5.2.10/faster_eth_abi.egg-info/top_level.txt +0 -2
  17. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/LICENSE +0 -0
  18. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/MANIFEST.in +0 -0
  19. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/README.md +0 -0
  20. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/__init__.py +0 -0
  21. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/_decoding.py +0 -0
  22. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/_encoding.py +0 -0
  23. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/abi.py +0 -0
  24. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/base.py +0 -0
  25. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/constants.py +0 -0
  26. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/decoding.py +0 -0
  27. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/encoding.py +0 -0
  28. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/exceptions.py +0 -0
  29. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/io.py +0 -0
  30. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/packed.py +0 -0
  31. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/py.typed +0 -0
  32. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/tools/__init__.py +0 -0
  33. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/utils/__init__.py +0 -0
  34. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/utils/numeric.py +0 -0
  35. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/utils/padding.py +0 -0
  36. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/utils/string.py +0 -0
  37. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi/utils/validation.py +0 -0
  38. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi.egg-info/dependency_links.txt +0 -0
  39. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi.egg-info/not-zip-safe +0 -0
  40. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/faster_eth_abi.egg-info/requires.txt +0 -0
  41. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/scripts/benchmark/compare_benchmark_results.py +0 -0
  42. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/scripts/benchmark/generate_benchmark_markdown.py +0 -0
  43. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/scripts/benchmark/parse_benchmark_output.py +0 -0
  44. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/scripts/release/test_package.py +0 -0
  45. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/setup.cfg +0 -0
  46. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/__init__.py +0 -0
  47. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/abi_tests/__init__.py +0 -0
  48. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/abi_tests/test_is_encodable.py +0 -0
  49. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/abi_tests/test_is_encodable_type.py +0 -0
  50. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/abi_tests/test_reversibility_properties.py +0 -0
  51. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/abi_tests/test_uint_properties.py +0 -0
  52. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/base.py +0 -0
  53. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/common/__init__.py +0 -0
  54. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/common/strategies.py +0 -0
  55. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/common/unit.py +0 -0
  56. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/decoding/__init__.py +0 -0
  57. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/decoding/test_context_frames_bytes_io.py +0 -0
  58. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/decoding/test_decoder_properties.py +0 -0
  59. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/encoding/__init__.py +0 -0
  60. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/encoding/test_encoder_properties.py +0 -0
  61. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/end_to_end/__init__.py +0 -0
  62. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/end_to_end/test_custom_registrations.py +0 -0
  63. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/grammar.py +0 -0
  64. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/packed/__init__.py +0 -0
  65. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/packed/test_encode_packed.py +0 -0
  66. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/packed/test_is_encodable_packed.py +0 -0
  67. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/registry/__init__.py +0 -0
  68. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/registry/test_abi_registry.py +0 -0
  69. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/registry/test_predicate_mapping.py +0 -0
  70. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/registry/test_predicates.py +0 -0
  71. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/test_import_and_version.py +0 -0
  72. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/test_tools.py +0 -0
  73. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/utils/__init__.py +0 -0
  74. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/utils/test_abbr.py +0 -0
  75. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/utils/test_ceil32.py +0 -0
  76. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/utils/test_scale_places.py +0 -0
  77. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/utils/test_validation_utils.py +0 -0
  78. {faster_eth_abi-5.2.10 → faster_eth_abi-5.2.11}/tests/core/utils/test_zpad.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: faster_eth_abi
3
- Version: 5.2.10
3
+ Version: 5.2.11
4
4
  Summary: A faster fork of eth_abi: Python utilities for working with Ethereum ABI definitions, especially encoding and decoding. Implemented in C.
5
5
  Home-page: https://github.com/BobTheBuidler/faster-eth-abi
6
6
  Author: The Ethereum Foundation
@@ -39,8 +39,6 @@ def encode_c(
39
39
  :returns: The head-tail encoded binary representation of the python
40
40
  values in ``args`` as values of the ABI types in ``types``.
41
41
  """
42
- # validate encode types and args
43
- validate_list_like_param(types, "types")
44
42
  validate_list_like_param(args, "args")
45
43
 
46
44
  encoder = self._registry.get_tuple_encoder(*types)
@@ -71,8 +69,6 @@ def decode_c(
71
69
  :returns: A tuple of equivalent python values for the ABI values
72
70
  represented in ``data``.
73
71
  """
74
- # validate decode types and data
75
- validate_list_like_param(types, "types")
76
72
  validate_bytes_param(data, "data")
77
73
 
78
74
  decoder = self._registry.get_tuple_decoder(*types, strict=strict)
@@ -1,22 +1,22 @@
1
- import functools
2
1
  import re
3
2
  from typing import (
4
3
  Any,
5
4
  Final,
5
+ NewType,
6
6
  NoReturn,
7
7
  Optional,
8
8
  Sequence,
9
9
  Tuple,
10
10
  TypeVar,
11
+ Union,
11
12
  final,
12
13
  )
13
14
 
14
- from eth_typing import (
15
+ from eth_typing.abi import (
15
16
  TypeStr,
16
17
  )
17
- import parsimonious
18
- from parsimonious import (
19
- expressions,
18
+ from mypy_extensions import (
19
+ mypyc_attr,
20
20
  )
21
21
  from parsimonious.nodes import (
22
22
  Node,
@@ -27,136 +27,29 @@ from typing_extensions import (
27
27
 
28
28
  from faster_eth_abi.exceptions import (
29
29
  ABITypeError,
30
- ParseError,
31
30
  )
32
31
 
33
- grammar: Final = parsimonious.Grammar(
34
- r"""
35
- type = tuple_type / basic_type
36
32
 
37
- tuple_type = components arrlist?
38
- components = non_zero_tuple
39
-
40
- non_zero_tuple = "(" type next_type* ")"
41
- next_type = "," type
42
-
43
- basic_type = base sub? arrlist?
44
-
45
- base = alphas
46
-
47
- sub = two_size / digits
48
- two_size = (digits "x" digits)
49
-
50
- arrlist = (const_arr / dynam_arr)+
51
- const_arr = "[" digits "]"
52
- dynam_arr = "[]"
33
+ TYPE_ALIASES: Final = {
34
+ "int": "int256",
35
+ "uint": "uint256",
36
+ "fixed": "fixed128x18",
37
+ "ufixed": "ufixed128x18",
38
+ "function": "bytes24",
39
+ "byte": "bytes1",
40
+ }
53
41
 
54
- alphas = ~"[A-Za-z]+"
55
- digits = ~"[1-9][0-9]*"
56
- """
42
+ TYPE_ALIAS_RE: Final = re.compile(
43
+ rf"\b({'|'.join(map(re.escape, TYPE_ALIASES.keys()))})\b"
57
44
  )
58
45
 
59
46
 
60
- @final
61
- class NodeVisitor(parsimonious.NodeVisitor): # type: ignore [misc]
62
- """
63
- Parsimonious node visitor which performs both parsing of type strings and
64
- post-processing of parse trees. Parsing operations are cached.
65
- """
66
-
67
- def __init__(self):
68
- self.parse = functools.lru_cache(maxsize=None)(self._parse_uncached)
69
-
70
- grammar = grammar
71
-
72
- def visit_non_zero_tuple(self, node, visited_children):
73
- # Ignore left and right parens
74
- _, first, rest, _ = visited_children
75
-
76
- return (first,) + rest
77
-
78
- def visit_tuple_type(self, node, visited_children):
79
- components, arrlist = visited_children
80
-
81
- return TupleType(components, arrlist, node=node)
82
-
83
- def visit_next_type(self, node, visited_children):
84
- # Ignore comma
85
- _, abi_type = visited_children
86
-
87
- return abi_type
88
-
89
- def visit_basic_type(self, node, visited_children):
90
- base, sub, arrlist = visited_children
91
-
92
- return BasicType(base, sub, arrlist, node=node)
93
-
94
- def visit_two_size(self, node, visited_children):
95
- # Ignore "x"
96
- first, _, second = visited_children
97
-
98
- return first, second
99
-
100
- def visit_const_arr(self, node, visited_children):
101
- # Ignore left and right brackets
102
- _, int_value, _ = visited_children
103
-
104
- return (int_value,)
105
-
106
- def visit_dynam_arr(self, node, visited_children):
107
- return ()
108
-
109
- def visit_alphas(self, node, visited_children):
110
- return node.text
111
-
112
- def visit_digits(self, node, visited_children):
113
- return int(node.text)
114
-
115
- def generic_visit(self, node, visited_children):
116
- expr = node.expr
117
- if isinstance(expr, expressions.OneOf):
118
- # Unwrap value chosen from alternatives
119
- return visited_children[0]
120
-
121
- if isinstance(expr, expressions.Quantifier) and expr.min == 0 and expr.max == 1:
122
- # Unwrap optional value or return `None`
123
- if len(visited_children) != 0:
124
- return visited_children[0]
125
-
126
- return None
127
-
128
- return tuple(visited_children)
129
-
130
- def _parse_uncached(self, type_str, **kwargs):
131
- """
132
- Parses a type string into an appropriate instance of
133
- :class:`~faster_eth_abi.grammar.ABIType`. If a type string cannot be parsed,
134
- throws :class:`~faster_eth_abi.exceptions.ParseError`.
135
-
136
- :param type_str: The type string to be parsed.
137
- :returns: An instance of :class:`~faster_eth_abi.grammar.ABIType` containing
138
- information about the parsed type string.
139
- """
140
- if not isinstance(type_str, str):
141
- raise TypeError(f"Can only parse string values: got {type(type_str)}")
142
-
143
- try:
144
- return super().parse(type_str, **kwargs)
145
- except parsimonious.ParseError as e:
146
- # This is a good place to add some better messaging around the type string.
147
- # If this logic grows any bigger, we should abstract it to its own function.
148
- if "()" in type_str:
149
- # validate against zero-sized tuple types
150
- raise ValueError(
151
- 'Zero-sized tuple types "()" are not supported.'
152
- ) from None
153
-
154
- raise ParseError(e.text, e.pos, e.expr) from e
155
-
156
-
157
- visitor: Final = NodeVisitor()
47
+ IntSubtype = NewType("IntSubtype", int)
48
+ FixedSubtype = NewType("FixedSubtype", Tuple[int, int])
49
+ Subtype = Union[IntSubtype, FixedSubtype]
158
50
 
159
51
 
52
+ @mypyc_attr(allow_interpreted_subclasses=True)
160
53
  class ABIType:
161
54
  """
162
55
  Base class for results of type string parsing operations.
@@ -165,7 +58,7 @@ class ABIType:
165
58
  __slots__ = ("arrlist", "node")
166
59
 
167
60
  def __init__(
168
- self, arrlist: Optional[Sequence] = None, node: Optional[Node] = None
61
+ self, arrlist: Optional[Sequence[str]] = None, node: Optional[Node] = None
169
62
  ) -> None:
170
63
  self.arrlist: Final = arrlist
171
64
  """
@@ -211,6 +104,7 @@ class ABIType:
211
104
  """
212
105
  raise NotImplementedError("Must implement `validate`")
213
106
 
107
+ @final
214
108
  def invalidate(self, error_msg: str) -> NoReturn:
215
109
  # Invalidates an ABI type with the given error message. Expects that a
216
110
  # parsimonious node was provided from the original parsing operation
@@ -222,6 +116,7 @@ class ABIType:
222
116
  f"in '{node.full_text}': {error_msg}"
223
117
  )
224
118
 
119
+ @final
225
120
  @property
226
121
  def is_array(self) -> bool:
227
122
  """
@@ -238,6 +133,7 @@ class ABIType:
238
133
  """
239
134
  raise NotImplementedError("Must implement `is_dynamic`")
240
135
 
136
+ @final
241
137
  @property
242
138
  def _has_dynamic_arrlist(self) -> bool:
243
139
  return self.is_array and any(len(dim) == 0 for dim in self.arrlist)
@@ -246,7 +142,7 @@ class ABIType:
246
142
  TComp = TypeVar("TComp", bound=ABIType)
247
143
 
248
144
 
249
- @final
145
+ @mypyc_attr(allow_interpreted_subclasses=True)
250
146
  class TupleType(ABIType):
251
147
  """
252
148
  Represents the result of parsing a tuple type string e.g. "(int,bool)".
@@ -257,7 +153,7 @@ class TupleType(ABIType):
257
153
  def __init__(
258
154
  self,
259
155
  components: Tuple[TComp, ...],
260
- arrlist: Optional[Sequence] = None,
156
+ arrlist: Optional[Sequence[str]] = None,
261
157
  *,
262
158
  node: Optional[Node] = None,
263
159
  ) -> None:
@@ -273,7 +169,7 @@ class TupleType(ABIType):
273
169
  arrlist = self.arrlist
274
170
 
275
171
  if isinstance(arrlist, tuple):
276
- arrlist = "".join(repr(list(a)) for a in arrlist)
172
+ arrlist = "".join(map(repr, map(list, arrlist)))
277
173
  else:
278
174
  arrlist = ""
279
175
 
@@ -286,11 +182,12 @@ class TupleType(ABIType):
286
182
  f"Cannot determine item type for non-array type '{self.to_type_str()}'"
287
183
  )
288
184
 
289
- return type(self)(
290
- self.components,
291
- self.arrlist[:-1] or None, # type: ignore [index]
292
- node=self.node,
293
- )
185
+ arrlist = self.arrlist[:-1] or None # type: ignore [index]
186
+ cls = type(self)
187
+ if cls is TupleType:
188
+ return TupleType(self.components, arrlist, node=self.node) # type: ignore [return-value]
189
+ else:
190
+ return cls(self.components, arrlist, node=self.node)
294
191
 
295
192
  def validate(self) -> None:
296
193
  for c in self.components:
@@ -304,7 +201,7 @@ class TupleType(ABIType):
304
201
  return any(c.is_dynamic for c in self.components)
305
202
 
306
203
 
307
- @final
204
+ @mypyc_attr(allow_interpreted_subclasses=True)
308
205
  class BasicType(ABIType):
309
206
  """
310
207
  Represents the result of parsing a basic type string e.g. "uint", "address",
@@ -316,7 +213,7 @@ class BasicType(ABIType):
316
213
  def __init__(
317
214
  self,
318
215
  base: str,
319
- sub: Any = None,
216
+ sub: Optional[Subtype] = None,
320
217
  arrlist: Optional[Sequence] = None,
321
218
  *,
322
219
  node: Optional[Node] = None,
@@ -339,12 +236,12 @@ class BasicType(ABIType):
339
236
  if isinstance(sub, int):
340
237
  substr = str(sub)
341
238
  elif isinstance(sub, tuple):
342
- substr = "x".join(str(s) for s in sub)
239
+ substr = "x".join(map(str, sub))
343
240
  else:
344
241
  substr = ""
345
242
 
346
243
  if isinstance(arrlist, tuple):
347
- return self.base + substr + "".join(repr(list(a)) for a in arrlist)
244
+ return self.base + substr + "".join(map(repr, map(list, arrlist)))
348
245
  else:
349
246
  return self.base + substr
350
247
 
@@ -355,22 +252,23 @@ class BasicType(ABIType):
355
252
  f"Cannot determine item type for non-array type '{self.to_type_str()}'"
356
253
  )
357
254
 
358
- return type(self)(
359
- self.base,
360
- self.sub,
361
- self.arrlist[:-1] or None, # type: ignore [index]
362
- node=self.node,
363
- )
255
+ cls = type(self)
256
+ arrlist = self.arrlist[:-1] or None # type: ignore [index]
257
+ if cls is BasicType:
258
+ return BasicType(self.base, self.sub, arrlist, node=self.node) # type: ignore [return-value]
259
+ else:
260
+ return cls(self.base, self.sub, arrlist, node=self.node)
364
261
 
365
262
  @property
366
263
  def is_dynamic(self) -> bool:
367
264
  if self._has_dynamic_arrlist:
368
265
  return True
369
266
 
370
- if self.base == "string":
267
+ base = self.base
268
+ if base == "string":
371
269
  return True
372
270
 
373
- if self.base == "bytes" and self.sub is None:
271
+ if base == "bytes" and self.sub is None:
374
272
  return True
375
273
 
376
274
  return False
@@ -436,20 +334,6 @@ class BasicType(ABIType):
436
334
  self.invalidate("address cannot have suffix")
437
335
 
438
336
 
439
- TYPE_ALIASES: Final = {
440
- "int": "int256",
441
- "uint": "uint256",
442
- "fixed": "fixed128x18",
443
- "ufixed": "ufixed128x18",
444
- "function": "bytes24",
445
- "byte": "bytes1",
446
- }
447
-
448
- TYPE_ALIAS_RE: Final = re.compile(
449
- rf"\b({'|'.join(re.escape(a) for a in TYPE_ALIASES.keys())})\b"
450
- )
451
-
452
-
453
337
  def normalize(type_str: TypeStr) -> TypeStr:
454
338
  """
455
339
  Normalizes a type string into its canonical version e.g. the type string
@@ -463,6 +347,3 @@ def normalize(type_str: TypeStr) -> TypeStr:
463
347
 
464
348
  def __normalize(match: "re.Match[str]") -> str:
465
349
  return TYPE_ALIASES[match.group(0)]
466
-
467
-
468
- parse: Final = visitor.parse
@@ -18,6 +18,7 @@ from faster_eth_abi.decoding import (
18
18
  )
19
19
  from faster_eth_abi.exceptions import (
20
20
  EncodingError,
21
+ MultipleEntriesFound,
21
22
  )
22
23
  from faster_eth_abi.registry import (
23
24
  ABIRegistry,
@@ -74,20 +75,18 @@ class ABIEncoder(BaseABICoder):
74
75
  :returns: ``True`` if ``arg`` is encodable as a value of the ABI type
75
76
  ``typ``. Otherwise, ``False``.
76
77
  """
77
- if not self.is_encodable_type(typ):
78
+ try:
79
+ encoder = self._registry.get_encoder(typ)
80
+ except MultipleEntriesFound:
81
+ raise
82
+ except:
78
83
  return False
79
84
 
80
- encoder = self._registry.get_encoder(typ)
81
-
85
+ validate = getattr(encoder, "validate_value", encoder)
82
86
  try:
83
- encoder.validate_value(arg)
87
+ validate(arg)
84
88
  except EncodingError:
85
89
  return False
86
- except AttributeError:
87
- try:
88
- encoder(arg)
89
- except EncodingError:
90
- return False
91
90
 
92
91
  return True
93
92
 
@@ -12,11 +12,13 @@ from eth_typing import (
12
12
  TypeStr,
13
13
  )
14
14
 
15
- from .grammar import (
15
+ from ._grammar import (
16
16
  ABIType,
17
17
  BasicType,
18
18
  TupleType,
19
19
  normalize,
20
+ )
21
+ from .grammar import (
20
22
  parse,
21
23
  )
22
24
 
@@ -0,0 +1,170 @@
1
+ import functools
2
+ import re
3
+ from typing import (
4
+ Any,
5
+ Final,
6
+ NoReturn,
7
+ Optional,
8
+ Sequence,
9
+ Tuple,
10
+ TypeVar,
11
+ final,
12
+ )
13
+
14
+ from eth_typing import (
15
+ TypeStr,
16
+ )
17
+ import parsimonious
18
+ from parsimonious import (
19
+ expressions,
20
+ )
21
+ from parsimonious.nodes import (
22
+ Node,
23
+ )
24
+ from typing_extensions import (
25
+ Self,
26
+ )
27
+
28
+ from faster_eth_abi._grammar import (
29
+ TYPE_ALIASES,
30
+ TYPE_ALIAS_RE,
31
+ ABIType,
32
+ BasicType,
33
+ TupleType,
34
+ normalize,
35
+ )
36
+ from faster_eth_abi.exceptions import (
37
+ ABITypeError,
38
+ ParseError,
39
+ )
40
+
41
+ grammar: Final = parsimonious.Grammar(
42
+ r"""
43
+ type = tuple_type / basic_type
44
+
45
+ tuple_type = components arrlist?
46
+ components = non_zero_tuple
47
+
48
+ non_zero_tuple = "(" type next_type* ")"
49
+ next_type = "," type
50
+
51
+ basic_type = base sub? arrlist?
52
+
53
+ base = alphas
54
+
55
+ sub = two_size / digits
56
+ two_size = (digits "x" digits)
57
+
58
+ arrlist = (const_arr / dynam_arr)+
59
+ const_arr = "[" digits "]"
60
+ dynam_arr = "[]"
61
+
62
+ alphas = ~"[A-Za-z]+"
63
+ digits = ~"[1-9][0-9]*"
64
+ """
65
+ )
66
+
67
+
68
+ @final
69
+ class NodeVisitor(parsimonious.NodeVisitor): # type: ignore [misc]
70
+ """
71
+ Parsimonious node visitor which performs both parsing of type strings and
72
+ post-processing of parse trees. Parsing operations are cached.
73
+ """
74
+
75
+ def __init__(self):
76
+ self.parse = functools.lru_cache(maxsize=None)(self._parse_uncached)
77
+
78
+ grammar = grammar
79
+
80
+ def visit_non_zero_tuple(self, node, visited_children):
81
+ # Ignore left and right parens
82
+ _, first, rest, _ = visited_children
83
+
84
+ return (first,) + rest
85
+
86
+ def visit_tuple_type(self, node, visited_children):
87
+ components, arrlist = visited_children
88
+
89
+ return TupleType(components, arrlist, node=node)
90
+
91
+ def visit_next_type(self, node, visited_children):
92
+ # Ignore comma
93
+ _, abi_type = visited_children
94
+
95
+ return abi_type
96
+
97
+ def visit_basic_type(self, node, visited_children):
98
+ base, sub, arrlist = visited_children
99
+
100
+ return BasicType(base, sub, arrlist, node=node)
101
+
102
+ def visit_two_size(self, node, visited_children):
103
+ # Ignore "x"
104
+ first, _, second = visited_children
105
+
106
+ return first, second
107
+
108
+ def visit_const_arr(self, node, visited_children):
109
+ # Ignore left and right brackets
110
+ _, int_value, _ = visited_children
111
+
112
+ return (int_value,)
113
+
114
+ def visit_dynam_arr(self, node, visited_children):
115
+ return ()
116
+
117
+ def visit_alphas(self, node, visited_children):
118
+ return node.text
119
+
120
+ def visit_digits(self, node, visited_children):
121
+ return int(node.text)
122
+
123
+ def generic_visit(self, node, visited_children):
124
+ expr = node.expr
125
+ if isinstance(expr, expressions.OneOf):
126
+ # Unwrap value chosen from alternatives
127
+ return visited_children[0]
128
+
129
+ if isinstance(expr, expressions.Quantifier) and expr.min == 0 and expr.max == 1:
130
+ # Unwrap optional value or return `None`
131
+ if len(visited_children) != 0:
132
+ return visited_children[0]
133
+
134
+ return None
135
+
136
+ return tuple(visited_children)
137
+
138
+ def _parse_uncached(self, type_str, **kwargs):
139
+ """
140
+ Parses a type string into an appropriate instance of
141
+ :class:`~faster_eth_abi.grammar.ABIType`. If a type string cannot be parsed,
142
+ throws :class:`~faster_eth_abi.exceptions.ParseError`.
143
+
144
+ :param type_str: The type string to be parsed.
145
+ :returns: An instance of :class:`~faster_eth_abi.grammar.ABIType` containing
146
+ information about the parsed type string.
147
+ """
148
+ if not isinstance(type_str, str):
149
+ raise TypeError(f"Can only parse string values: got {type(type_str)}")
150
+
151
+ try:
152
+ return super().parse(type_str, **kwargs)
153
+ except parsimonious.ParseError as e:
154
+ # This is a good place to add some better messaging around the type string.
155
+ # If this logic grows any bigger, we should abstract it to its own function.
156
+ if "()" in type_str:
157
+ # validate against zero-sized tuple types
158
+ raise ValueError(
159
+ 'Zero-sized tuple types "()" are not supported.'
160
+ ) from None
161
+
162
+ raise ParseError(e.text, e.pos, e.expr) from e
163
+
164
+
165
+ visitor: Final = NodeVisitor()
166
+
167
+ parse: Final = visitor.parse
168
+
169
+
170
+ __all__ = ["NodeVisitor", "ABIType", "TupleType", "BasicType", "grammar", "parse", "normalize", "visitor", "TYPE_ALIASES", "TYPE_ALIAS_RE"]
@@ -1,17 +1,24 @@
1
1
  import abc
2
- import copy
3
2
  import functools
3
+ from copy import copy
4
4
  from typing import (
5
5
  Any,
6
6
  Callable,
7
+ Dict,
8
+ Final,
9
+ Generic,
7
10
  Optional,
8
11
  Type,
12
+ TypeVar,
9
13
  Union,
10
14
  )
11
15
 
12
16
  from eth_typing import (
13
17
  TypeStr,
14
18
  )
19
+ from typing_extensions import (
20
+ Self,
21
+ )
15
22
 
16
23
  from . import (
17
24
  decoding,
@@ -30,6 +37,8 @@ from .io import (
30
37
  ContextFramesBytesIO,
31
38
  )
32
39
 
40
+ T = TypeVar("T")
41
+
33
42
  Lookup = Union[TypeStr, Callable[[TypeStr], bool]]
34
43
 
35
44
  EncoderCallable = Callable[[Any], bytes]
@@ -51,34 +60,35 @@ class Copyable(abc.ABC):
51
60
  return self.copy()
52
61
 
53
62
 
54
- class PredicateMapping(Copyable):
63
+ class PredicateMapping(Copyable, Generic[T]):
55
64
  """
56
65
  Acts as a mapping from predicate functions to values. Values are retrieved
57
66
  when their corresponding predicate matches a given input. Predicates can
58
67
  also be labeled to facilitate removal from the mapping.
59
68
  """
60
69
 
61
- def __init__(self, name):
62
- self._name = name
63
- self._values = {}
64
- self._labeled_predicates = {}
70
+ def __init__(self, name: str):
71
+ self._name: Final = name
72
+ self._values: Dict[Lookup, T] = {}
73
+ self._labeled_predicates: Dict[str, Lookup] = {}
65
74
 
66
- def add(self, predicate, value, label=None):
75
+ def add(self, predicate: Lookup, value: T, label: Optional[str] = None) -> None:
67
76
  if predicate in self._values:
68
77
  raise ValueError(f"Matcher {predicate!r} already exists in {self._name}")
69
78
 
70
79
  if label is not None:
71
- if label in self._labeled_predicates:
80
+ labeled_predicates = self._labeled_predicates
81
+ if label in labeled_predicates:
72
82
  raise ValueError(
73
83
  f"Matcher {predicate!r} with label '{label}' "
74
84
  f"already exists in {self._name}"
75
85
  )
76
86
 
77
- self._labeled_predicates[label] = predicate
87
+ labeled_predicates[label] = predicate
78
88
 
79
89
  self._values[predicate] = value
80
90
 
81
- def find(self, type_str):
91
+ def find(self, type_str: TypeStr) -> T:
82
92
  results = tuple(
83
93
  (predicate, value)
84
94
  for predicate, value in self._values.items()
@@ -103,9 +113,9 @@ class PredicateMapping(Copyable):
103
113
  "documentation for more information."
104
114
  )
105
115
 
106
- return values[0]
116
+ return values[0] # type: ignore [no-any-return]
107
117
 
108
- def remove_by_equality(self, predicate):
118
+ def remove_by_equality(self, predicate: Lookup) -> None:
109
119
  # Delete the predicate mapping to the previously stored value
110
120
  try:
111
121
  del self._values[predicate]
@@ -120,7 +130,7 @@ class PredicateMapping(Copyable):
120
130
  else:
121
131
  del self._labeled_predicates[label]
122
132
 
123
- def _label_for_predicate(self, predicate):
133
+ def _label_for_predicate(self, predicate: Lookup) -> str:
124
134
  # Both keys and values in `_labeled_predicates` are unique since the
125
135
  # `add` method enforces this
126
136
  for key, value in self._labeled_predicates.items():
@@ -131,16 +141,17 @@ class PredicateMapping(Copyable):
131
141
  f"Matcher {predicate!r} not referred to by any label in {self._name}"
132
142
  )
133
143
 
134
- def remove_by_label(self, label):
144
+ def remove_by_label(self, label: str) -> None:
145
+ labeled_predicates = self._labeled_predicates
135
146
  try:
136
- predicate = self._labeled_predicates[label]
147
+ predicate = labeled_predicates[label]
137
148
  except KeyError:
138
149
  raise KeyError(f"Label '{label}' not found in {self._name}")
139
150
 
140
- del self._labeled_predicates[label]
151
+ del labeled_predicates[label]
141
152
  del self._values[predicate]
142
153
 
143
- def remove(self, predicate_or_label):
154
+ def remove(self, predicate_or_label: Union[Lookup, str]) -> None:
144
155
  if callable(predicate_or_label):
145
156
  self.remove_by_equality(predicate_or_label)
146
157
  elif isinstance(predicate_or_label, str):
@@ -151,11 +162,11 @@ class PredicateMapping(Copyable):
151
162
  f"{type(predicate_or_label)}"
152
163
  )
153
164
 
154
- def copy(self):
165
+ def copy(self) -> Self:
155
166
  cpy = type(self)(self._name)
156
167
 
157
- cpy._values = copy.copy(self._values)
158
- cpy._labeled_predicates = copy.copy(self._labeled_predicates)
168
+ cpy._values = copy(self._values)
169
+ cpy._labeled_predicates = copy(self._labeled_predicates)
159
170
 
160
171
  return cpy
161
172
 
@@ -282,6 +293,7 @@ def _clear_encoder_cache(old_method: Callable[..., None]) -> Callable[..., None]
282
293
  @functools.wraps(old_method)
283
294
  def new_method(self: "ABIRegistry", *args: Any, **kwargs: Any) -> None:
284
295
  self.get_encoder.cache_clear()
296
+ self.get_tuple_encoder.cache_clear()
285
297
  return old_method(self, *args, **kwargs)
286
298
 
287
299
  return new_method
@@ -291,6 +303,7 @@ def _clear_decoder_cache(old_method: Callable[..., None]) -> Callable[..., None]
291
303
  @functools.wraps(old_method)
292
304
  def new_method(self: "ABIRegistry", *args: Any, **kwargs: Any) -> None:
293
305
  self.get_decoder.cache_clear()
306
+ self.get_tuple_decoder.cache_clear()
294
307
  return old_method(self, *args, **kwargs)
295
308
 
296
309
  return new_method
@@ -343,8 +356,8 @@ class BaseRegistry:
343
356
 
344
357
  class ABIRegistry(Copyable, BaseRegistry):
345
358
  def __init__(self):
346
- self._encoders = PredicateMapping("encoder registry")
347
- self._decoders = PredicateMapping("decoder registry")
359
+ self._encoders: PredicateMapping[Encoder] = PredicateMapping("encoder registry")
360
+ self._decoders: PredicateMapping[Decoder] = PredicateMapping("decoder registry")
348
361
  self.get_encoder = functools.lru_cache(maxsize=None)(self._get_encoder_uncached)
349
362
  self.get_decoder = functools.lru_cache(maxsize=None)(self._get_decoder_uncached)
350
363
  self.get_tuple_encoder = functools.lru_cache(maxsize=None)(
@@ -518,8 +531,8 @@ class ABIRegistry(Copyable, BaseRegistry):
518
531
  """
519
532
  cpy = type(self)()
520
533
 
521
- cpy._encoders = copy.copy(self._encoders)
522
- cpy._decoders = copy.copy(self._decoders)
534
+ cpy._encoders = copy(self._encoders)
535
+ cpy._decoders = copy(self._decoders)
523
536
 
524
537
  return cpy
525
538
 
@@ -2,7 +2,9 @@ from typing import (
2
2
  Callable,
3
3
  Final,
4
4
  Optional,
5
+ Tuple,
5
6
  Union,
7
+ cast,
6
8
  )
7
9
 
8
10
  from cchecksum import (
@@ -15,11 +17,13 @@ from hypothesis import (
15
17
  strategies as st,
16
18
  )
17
19
 
18
- from faster_eth_abi.grammar import (
20
+ from faster_eth_abi._grammar import (
19
21
  ABIType,
20
22
  BasicType,
21
23
  TupleType,
22
24
  normalize,
25
+ )
26
+ from faster_eth_abi.grammar import (
23
27
  parse,
24
28
  )
25
29
  from faster_eth_abi.registry import (
@@ -81,7 +85,7 @@ class StrategyRegistry(BaseRegistry):
81
85
  def get_uint_strategy(
82
86
  abi_type: BasicType, registry: StrategyRegistry
83
87
  ) -> st.SearchStrategy:
84
- bits = abi_type.sub
88
+ bits = cast(int, abi_type.sub)
85
89
 
86
90
  return st.integers(
87
91
  min_value=0,
@@ -92,7 +96,7 @@ def get_uint_strategy(
92
96
  def get_int_strategy(
93
97
  abi_type: BasicType, registry: StrategyRegistry
94
98
  ) -> st.SearchStrategy:
95
- bits = abi_type.sub
99
+ bits = cast(int, abi_type.sub)
96
100
 
97
101
  return st.integers(
98
102
  min_value=-(2 ** (bits - 1)),
@@ -107,7 +111,7 @@ bool_strategy: Final = st.booleans()
107
111
  def get_ufixed_strategy(
108
112
  abi_type: BasicType, registry: StrategyRegistry
109
113
  ) -> st.SearchStrategy:
110
- bits, places = abi_type.sub
114
+ bits, places = cast(Tuple[int, int], abi_type.sub)
111
115
 
112
116
  return st.decimals(
113
117
  min_value=0,
@@ -119,7 +123,7 @@ def get_ufixed_strategy(
119
123
  def get_fixed_strategy(
120
124
  abi_type: BasicType, registry: StrategyRegistry
121
125
  ) -> st.SearchStrategy:
122
- bits, places = abi_type.sub
126
+ bits, places = cast(Tuple[int, int], abi_type.sub)
123
127
 
124
128
  return st.decimals(
125
129
  min_value=-(2 ** (bits - 1)),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: faster-eth-abi
3
- Version: 5.2.10
3
+ Version: 5.2.11
4
4
  Summary: A faster fork of eth_abi: Python utilities for working with Ethereum ABI definitions, especially encoding and decoding. Implemented in C.
5
5
  Home-page: https://github.com/BobTheBuidler/faster-eth-abi
6
6
  Author: The Ethereum Foundation
@@ -7,6 +7,7 @@ faster_eth_abi/__init__.py
7
7
  faster_eth_abi/_codec.py
8
8
  faster_eth_abi/_decoding.py
9
9
  faster_eth_abi/_encoding.py
10
+ faster_eth_abi/_grammar.py
10
11
  faster_eth_abi/abi.py
11
12
  faster_eth_abi/base.py
12
13
  faster_eth_abi/codec.py
@@ -0,0 +1,2 @@
1
+ 29859a9e7da9d19bb98c__mypyc
2
+ faster_eth_abi
@@ -5,8 +5,8 @@ requires = [
5
5
  "mypy[mypyc]>=1.14.1,<1.18.3",
6
6
  "mypy_extensions",
7
7
  "cchecksum>=0.2.6,<0.4",
8
- # pydantic-core (installed with faster-eth-utils) is unable to build on Python3.14
9
- "faster-eth-utils>=2.0.0; python_version<'3.14'",
8
+ # pydantic-core (installed with faster-eth-utils) is unable to build on Python3.14 on 32-bit Linux distros
9
+ "faster-eth-utils>=2.0.0; platform_system != 'Linux' or python_version != '3.14' or platform_machine != 'i686'",
10
10
  "eth-typing>=3.0.0",
11
11
  "parsimonious>=0.10.0,<0.11.0",
12
12
  ]
@@ -55,6 +55,7 @@ ext_modules = mypycify(
55
55
  "faster_eth_abi/_codec.py",
56
56
  "faster_eth_abi/_decoding.py",
57
57
  "faster_eth_abi/_encoding.py",
58
+ "faster_eth_abi/_grammar.py",
58
59
  "faster_eth_abi/abi.py",
59
60
  "faster_eth_abi/constants.py",
60
61
  "faster_eth_abi/from_type_str.py",
@@ -73,7 +74,7 @@ ext_modules = mypycify(
73
74
  setup(
74
75
  name="faster_eth_abi",
75
76
  # *IMPORTANT*: Don't manually change the version here. See Contributing docs for the release process.
76
- version="5.2.10",
77
+ version="5.2.11",
77
78
  description="""A faster fork of eth_abi: Python utilities for working with Ethereum ABI definitions, especially encoding and decoding. Implemented in C.""",
78
79
  long_description=long_description,
79
80
  long_description_content_type="text/markdown",
@@ -7,6 +7,7 @@ from faster_eth_abi.abi import (
7
7
  from faster_eth_abi.exceptions import (
8
8
  InsufficientDataBytes,
9
9
  InvalidPointer,
10
+ NoEntriesFound,
10
11
  )
11
12
  from faster_eth_abi.grammar import (
12
13
  parse,
@@ -90,9 +91,9 @@ def test_abi_decode_wrong_data_param_type_raises(data, strict):
90
91
  @pytest.mark.parametrize(
91
92
  "types",
92
93
  (
93
- "",
94
+ # "", This parametrization is disabled because we don't type check `types` and when iterated upon, `""` yields no items and ends iteration.
94
95
  123,
95
- b"",
96
+ # b"", This parametrization is disabled because we don't type check `types` and when iterated upon, `""` yields no items and ends iteration.
96
97
  b"\xff",
97
98
  b"david attenborough",
98
99
  bytearray(b"\x01\xff"),
@@ -101,9 +102,15 @@ def test_abi_decode_wrong_data_param_type_raises(data, strict):
101
102
  ),
102
103
  )
103
104
  def test_abi_decode_wrong_types_param_type_raises(types, strict):
105
+ if types == {"key": "val"}:
106
+ expected_exc = NoEntriesFound
107
+ else:
108
+ expected_exc = TypeError
104
109
  with pytest.raises(
105
- TypeError,
106
- match=f"The `types` value type must be one of list or tuple. Got {type(types)}",
110
+ expected_exc,
111
+ # NOTE The `match` arg was commented out because faster-eth-abi might not raise
112
+ # the same exception, but we should still test that it raises an Exception like eth-abi
113
+ # match=f"The `types` value type must be one of list or tuple. Got {type(types)}",
107
114
  ):
108
115
  decode(types, b"\x00" * 32, strict=strict)
109
116
 
@@ -4,6 +4,10 @@ import re
4
4
  from faster_eth_abi import (
5
5
  encode,
6
6
  )
7
+ from faster_eth_abi.exceptions import (
8
+ NoEntriesFound,
9
+ ValueOutOfBounds,
10
+ )
7
11
  from faster_eth_abi.grammar import (
8
12
  parse,
9
13
  )
@@ -101,10 +105,17 @@ def test_abi_encode_for_single_dynamic_types(
101
105
  )
102
106
  def test_abi_encode_raises_for_non_list_like_params(non_list_like_value):
103
107
  # test raises when `types` param is not list-like
108
+ if non_list_like_value in ("", b""):
109
+ expected_exc = ValueOutOfBounds
110
+ elif non_list_like_value == {"key": "val"}:
111
+ expected_exc = NoEntriesFound
112
+ else:
113
+ expected_exc = TypeError
104
114
  with pytest.raises(
105
- TypeError,
106
- match=f"The `types` value type must be one of list or tuple. "
107
- f"Got {type(non_list_like_value)}",
115
+ expected_exc,
116
+ # NOTE The `match` arg was commented out because faster-eth-abi might not raise
117
+ # the same exception, but we should still test that it raises an Exception like eth-abi
118
+ # match=f"The `types` value type must be one of list or tuple. Got {type(non_list_like_value)}",
108
119
  ):
109
120
  encode(non_list_like_value, ["bytes"])
110
121
 
@@ -1,2 +0,0 @@
1
- 76f9a3652d4d2667c55c__mypyc
2
- faster_eth_abi
File without changes