registry.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. import abc
  2. import copy
  3. import functools
  4. from typing import (
  5. Any,
  6. Callable,
  7. Optional,
  8. Type,
  9. Union,
  10. )
  11. from eth_typing import (
  12. abi,
  13. )
  14. from . import (
  15. decoding,
  16. encoding,
  17. exceptions,
  18. grammar,
  19. )
  20. from .base import (
  21. BaseCoder,
  22. )
  23. from .exceptions import (
  24. MultipleEntriesFound,
  25. NoEntriesFound,
  26. )
  27. Lookup = Union[abi.TypeStr, Callable[[abi.TypeStr], bool]]
  28. EncoderCallable = Callable[[Any], bytes]
  29. DecoderCallable = Callable[[decoding.ContextFramesBytesIO], Any]
  30. Encoder = Union[EncoderCallable, Type[encoding.BaseEncoder]]
  31. Decoder = Union[DecoderCallable, Type[decoding.BaseDecoder]]
  32. class Copyable(abc.ABC):
  33. @abc.abstractmethod
  34. def copy(self):
  35. pass
  36. def __copy__(self):
  37. return self.copy()
  38. def __deepcopy__(self, *args):
  39. return self.copy()
  40. class PredicateMapping(Copyable):
  41. """
  42. Acts as a mapping from predicate functions to values. Values are retrieved
  43. when their corresponding predicate matches a given input. Predicates can
  44. also be labeled to facilitate removal from the mapping.
  45. """
  46. def __init__(self, name):
  47. self._name = name
  48. self._values = {}
  49. self._labeled_predicates = {}
  50. def add(self, predicate, value, label=None):
  51. if predicate in self._values:
  52. raise ValueError(
  53. f"Matcher {repr(predicate)} already exists in {self._name}"
  54. )
  55. if label is not None:
  56. if label in self._labeled_predicates:
  57. raise ValueError(
  58. f"Matcher {repr(predicate)} with label '{label}' already exists "
  59. f"in {self._name}"
  60. )
  61. self._labeled_predicates[label] = predicate
  62. self._values[predicate] = value
  63. def find(self, type_str):
  64. results = tuple(
  65. (predicate, value)
  66. for predicate, value in self._values.items()
  67. if predicate(type_str)
  68. )
  69. if len(results) == 0:
  70. raise NoEntriesFound(
  71. f"No matching entries for '{type_str}' in {self._name}"
  72. )
  73. predicates, values = tuple(zip(*results))
  74. if len(results) > 1:
  75. predicate_reprs = ", ".join(map(repr, predicates))
  76. raise MultipleEntriesFound(
  77. f"Multiple matching entries for '{type_str}' in {self._name}: "
  78. f"{predicate_reprs}. This occurs when two registrations match the "
  79. "same type string. You may need to delete one of the "
  80. "registrations or modify its matching behavior to ensure it "
  81. 'doesn\'t collide with other registrations. See the "Registry" '
  82. "documentation for more information."
  83. )
  84. return values[0]
  85. def remove_by_equality(self, predicate):
  86. # Delete the predicate mapping to the previously stored value
  87. try:
  88. del self._values[predicate]
  89. except KeyError:
  90. raise KeyError(f"Matcher {repr(predicate)} not found in {self._name}")
  91. # Delete any label which refers to this predicate
  92. try:
  93. label = self._label_for_predicate(predicate)
  94. except ValueError:
  95. pass
  96. else:
  97. del self._labeled_predicates[label]
  98. def _label_for_predicate(self, predicate):
  99. # Both keys and values in `_labeled_predicates` are unique since the
  100. # `add` method enforces this
  101. for key, value in self._labeled_predicates.items():
  102. if value is predicate:
  103. return key
  104. raise ValueError(
  105. f"Matcher {repr(predicate)} not referred to by any label in {self._name}"
  106. )
  107. def remove_by_label(self, label):
  108. try:
  109. predicate = self._labeled_predicates[label]
  110. except KeyError:
  111. raise KeyError(f"Label '{label}' not found in {self._name}")
  112. del self._labeled_predicates[label]
  113. del self._values[predicate]
  114. def remove(self, predicate_or_label):
  115. if callable(predicate_or_label):
  116. self.remove_by_equality(predicate_or_label)
  117. elif isinstance(predicate_or_label, str):
  118. self.remove_by_label(predicate_or_label)
  119. else:
  120. raise TypeError(
  121. "Key to be removed must be callable or string: got "
  122. f"{type(predicate_or_label)}"
  123. )
  124. def copy(self):
  125. cpy = type(self)(self._name)
  126. cpy._values = copy.copy(self._values)
  127. cpy._labeled_predicates = copy.copy(self._labeled_predicates)
  128. return cpy
  129. class Predicate:
  130. """
  131. Represents a predicate function to be used for type matching in
  132. ``ABIRegistry``.
  133. """
  134. __slots__ = tuple()
  135. def __call__(self, *args, **kwargs): # pragma: no cover
  136. raise NotImplementedError("Must implement `__call__`")
  137. def __str__(self): # pragma: no cover
  138. raise NotImplementedError("Must implement `__str__`")
  139. def __repr__(self):
  140. return f"<{type(self).__name__} {self}>"
  141. def __iter__(self):
  142. for attr in self.__slots__:
  143. yield getattr(self, attr)
  144. def __hash__(self):
  145. return hash(tuple(self))
  146. def __eq__(self, other):
  147. return type(self) is type(other) and tuple(self) == tuple(other)
  148. class Equals(Predicate):
  149. """
  150. A predicate that matches any input equal to `value`.
  151. """
  152. __slots__ = ("value",)
  153. def __init__(self, value):
  154. self.value = value
  155. def __call__(self, other):
  156. return self.value == other
  157. def __str__(self):
  158. return f"(== {repr(self.value)})"
  159. class BaseEquals(Predicate):
  160. """
  161. A predicate that matches a basic type string with a base component equal to
  162. `value` and no array component. If `with_sub` is `True`, the type string
  163. must have a sub component to match. If `with_sub` is `False`, the type
  164. string must *not* have a sub component to match. If `with_sub` is None,
  165. the type string's sub component is ignored.
  166. """
  167. __slots__ = ("base", "with_sub")
  168. def __init__(self, base, *, with_sub=None):
  169. self.base = base
  170. self.with_sub = with_sub
  171. def __call__(self, type_str):
  172. try:
  173. abi_type = grammar.parse(type_str)
  174. except (exceptions.ParseError, ValueError):
  175. return False
  176. if isinstance(abi_type, grammar.BasicType):
  177. if abi_type.arrlist is not None:
  178. return False
  179. if self.with_sub is not None:
  180. if self.with_sub and abi_type.sub is None:
  181. return False
  182. if not self.with_sub and abi_type.sub is not None:
  183. return False
  184. return abi_type.base == self.base
  185. # We'd reach this point if `type_str` did not contain a basic type
  186. # e.g. if it contained a tuple type
  187. return False
  188. def __str__(self):
  189. return (
  190. f"(base == {repr(self.base)}"
  191. + (
  192. ""
  193. if self.with_sub is None
  194. else (" and sub is not None" if self.with_sub else " and sub is None")
  195. )
  196. + ")"
  197. )
  198. def has_arrlist(type_str):
  199. """
  200. A predicate that matches a type string with an array dimension list.
  201. """
  202. try:
  203. abi_type = grammar.parse(type_str)
  204. except (exceptions.ParseError, ValueError):
  205. return False
  206. return abi_type.arrlist is not None
  207. def is_base_tuple(type_str):
  208. """
  209. A predicate that matches a tuple type with no array dimension list.
  210. """
  211. try:
  212. abi_type = grammar.parse(type_str)
  213. except (exceptions.ParseError, ValueError):
  214. return False
  215. return isinstance(abi_type, grammar.TupleType) and abi_type.arrlist is None
  216. def _clear_encoder_cache(old_method: Callable[..., None]) -> Callable[..., None]:
  217. @functools.wraps(old_method)
  218. def new_method(self: "ABIRegistry", *args: Any, **kwargs: Any) -> None:
  219. self.get_encoder.cache_clear()
  220. return old_method(self, *args, **kwargs)
  221. return new_method
  222. def _clear_decoder_cache(old_method: Callable[..., None]) -> Callable[..., None]:
  223. @functools.wraps(old_method)
  224. def new_method(self: "ABIRegistry", *args: Any, **kwargs: Any) -> None:
  225. self.get_decoder.cache_clear()
  226. return old_method(self, *args, **kwargs)
  227. return new_method
  228. class BaseRegistry:
  229. @staticmethod
  230. def _register(mapping, lookup, value, label=None):
  231. if callable(lookup):
  232. mapping.add(lookup, value, label)
  233. return
  234. if isinstance(lookup, str):
  235. mapping.add(Equals(lookup), value, lookup)
  236. return
  237. raise TypeError(
  238. f"Lookup must be a callable or a value of type `str`: got {repr(lookup)}"
  239. )
  240. @staticmethod
  241. def _unregister(mapping, lookup_or_label):
  242. if callable(lookup_or_label):
  243. mapping.remove_by_equality(lookup_or_label)
  244. return
  245. if isinstance(lookup_or_label, str):
  246. mapping.remove_by_label(lookup_or_label)
  247. return
  248. raise TypeError(
  249. "Lookup/label must be a callable or a value of type `str`: got "
  250. f"{repr(lookup_or_label)}"
  251. )
  252. @staticmethod
  253. def _get_registration(mapping, type_str):
  254. try:
  255. value = mapping.find(type_str)
  256. except ValueError as e:
  257. if "No matching" in e.args[0]:
  258. # If no matches found, attempt to parse in case lack of matches
  259. # was due to unparsability
  260. grammar.parse(type_str)
  261. raise
  262. return value
  263. class ABIRegistry(Copyable, BaseRegistry):
  264. def __init__(self):
  265. self._encoders = PredicateMapping("encoder registry")
  266. self._decoders = PredicateMapping("decoder registry")
  267. self.get_encoder = functools.lru_cache(maxsize=None)(self._get_encoder_uncached)
  268. self.get_decoder = functools.lru_cache(maxsize=None)(self._get_decoder_uncached)
  269. def _get_registration(self, mapping, type_str):
  270. coder = super()._get_registration(mapping, type_str)
  271. if isinstance(coder, type) and issubclass(coder, BaseCoder):
  272. return coder.from_type_str(type_str, self)
  273. return coder
  274. @_clear_encoder_cache
  275. def register_encoder(
  276. self, lookup: Lookup, encoder: Encoder, label: Optional[str] = None
  277. ) -> None:
  278. """
  279. Registers the given ``encoder`` under the given ``lookup``. A unique
  280. string label may be optionally provided that can be used to refer to
  281. the registration by name. For more information about arguments, refer
  282. to :any:`register`.
  283. """
  284. self._register(self._encoders, lookup, encoder, label=label)
  285. @_clear_encoder_cache
  286. def unregister_encoder(self, lookup_or_label: Lookup) -> None:
  287. """
  288. Unregisters an encoder in the registry with the given lookup or label.
  289. If ``lookup_or_label`` is a string, the encoder with the label
  290. ``lookup_or_label`` will be unregistered. If it is an function, the
  291. encoder with the lookup function ``lookup_or_label`` will be
  292. unregistered.
  293. """
  294. self._unregister(self._encoders, lookup_or_label)
  295. @_clear_decoder_cache
  296. def register_decoder(
  297. self, lookup: Lookup, decoder: Decoder, label: Optional[str] = None
  298. ) -> None:
  299. """
  300. Registers the given ``decoder`` under the given ``lookup``. A unique
  301. string label may be optionally provided that can be used to refer to
  302. the registration by name. For more information about arguments, refer
  303. to :any:`register`.
  304. """
  305. self._register(self._decoders, lookup, decoder, label=label)
  306. @_clear_decoder_cache
  307. def unregister_decoder(self, lookup_or_label: Lookup) -> None:
  308. """
  309. Unregisters a decoder in the registry with the given lookup or label.
  310. If ``lookup_or_label`` is a string, the decoder with the label
  311. ``lookup_or_label`` will be unregistered. If it is an function, the
  312. decoder with the lookup function ``lookup_or_label`` will be
  313. unregistered.
  314. """
  315. self._unregister(self._decoders, lookup_or_label)
  316. def register(
  317. self,
  318. lookup: Lookup,
  319. encoder: Encoder,
  320. decoder: Decoder,
  321. label: Optional[str] = None,
  322. ) -> None:
  323. """
  324. Registers the given ``encoder`` and ``decoder`` under the given
  325. ``lookup``. A unique string label may be optionally provided that can
  326. be used to refer to the registration by name.
  327. :param lookup: A type string or type string matcher function
  328. (predicate). When the registry is queried with a type string
  329. ``query`` to determine which encoder or decoder to use, ``query``
  330. will be checked against every registration in the registry. If a
  331. registration was created with a type string for ``lookup``, it will
  332. be considered a match if ``lookup == query``. If a registration
  333. was created with a matcher function for ``lookup``, it will be
  334. considered a match if ``lookup(query) is True``. If more than one
  335. registration is found to be a match, then an exception is raised.
  336. :param encoder: An encoder callable or class to use if ``lookup``
  337. matches a query. If ``encoder`` is a callable, it must accept a
  338. python value and return a ``bytes`` value. If ``encoder`` is a
  339. class, it must be a valid subclass of :any:`encoding.BaseEncoder`
  340. and must also implement the :any:`from_type_str` method on
  341. :any:`base.BaseCoder`.
  342. :param decoder: A decoder callable or class to use if ``lookup``
  343. matches a query. If ``decoder`` is a callable, it must accept a
  344. stream-like object of bytes and return a python value. If
  345. ``decoder`` is a class, it must be a valid subclass of
  346. :any:`decoding.BaseDecoder` and must also implement the
  347. :any:`from_type_str` method on :any:`base.BaseCoder`.
  348. :param label: An optional label that can be used to refer to this
  349. registration by name. This label can be used to unregister an
  350. entry in the registry via the :any:`unregister` method and its
  351. variants.
  352. """
  353. self.register_encoder(lookup, encoder, label=label)
  354. self.register_decoder(lookup, decoder, label=label)
  355. def unregister(self, label: Optional[str]) -> None:
  356. """
  357. Unregisters the entries in the encoder and decoder registries which
  358. have the label ``label``.
  359. """
  360. self.unregister_encoder(label)
  361. self.unregister_decoder(label)
  362. def _get_encoder_uncached(self, type_str):
  363. return self._get_registration(self._encoders, type_str)
  364. def has_encoder(self, type_str: abi.TypeStr) -> bool:
  365. """
  366. Returns ``True`` if an encoder is found for the given type string
  367. ``type_str``. Otherwise, returns ``False``. Raises
  368. :class:`~eth_abi.exceptions.MultipleEntriesFound` if multiple encoders
  369. are found.
  370. """
  371. try:
  372. self.get_encoder(type_str)
  373. except Exception as e:
  374. if isinstance(e, MultipleEntriesFound):
  375. raise e
  376. return False
  377. return True
  378. def _get_decoder_uncached(self, type_str, strict=True):
  379. decoder = self._get_registration(self._decoders, type_str)
  380. if hasattr(decoder, "is_dynamic") and decoder.is_dynamic:
  381. # Set a transient flag each time a call is made to ``get_decoder()``.
  382. # Only dynamic decoders should be allowed these looser constraints. All
  383. # other decoders should keep the default value of ``True``.
  384. decoder.strict = strict
  385. return decoder
  386. def copy(self):
  387. """
  388. Copies a registry such that new registrations can be made or existing
  389. registrations can be unregistered without affecting any instance from
  390. which a copy was obtained. This is useful if an existing registry
  391. fulfills most of a user's needs but requires one or two modifications.
  392. In that case, a copy of that registry can be obtained and the necessary
  393. changes made without affecting the original registry.
  394. """
  395. cpy = type(self)()
  396. cpy._encoders = copy.copy(self._encoders)
  397. cpy._decoders = copy.copy(self._decoders)
  398. return cpy
  399. registry = ABIRegistry()
  400. registry.register(
  401. BaseEquals("uint"),
  402. encoding.UnsignedIntegerEncoder,
  403. decoding.UnsignedIntegerDecoder,
  404. label="uint",
  405. )
  406. registry.register(
  407. BaseEquals("int"),
  408. encoding.SignedIntegerEncoder,
  409. decoding.SignedIntegerDecoder,
  410. label="int",
  411. )
  412. registry.register(
  413. BaseEquals("address"),
  414. encoding.AddressEncoder,
  415. decoding.AddressDecoder,
  416. label="address",
  417. )
  418. registry.register(
  419. BaseEquals("bool"),
  420. encoding.BooleanEncoder,
  421. decoding.BooleanDecoder,
  422. label="bool",
  423. )
  424. registry.register(
  425. BaseEquals("ufixed"),
  426. encoding.UnsignedFixedEncoder,
  427. decoding.UnsignedFixedDecoder,
  428. label="ufixed",
  429. )
  430. registry.register(
  431. BaseEquals("fixed"),
  432. encoding.SignedFixedEncoder,
  433. decoding.SignedFixedDecoder,
  434. label="fixed",
  435. )
  436. registry.register(
  437. BaseEquals("bytes", with_sub=True),
  438. encoding.BytesEncoder,
  439. decoding.BytesDecoder,
  440. label="bytes<M>",
  441. )
  442. registry.register(
  443. BaseEquals("bytes", with_sub=False),
  444. encoding.ByteStringEncoder,
  445. decoding.ByteStringDecoder,
  446. label="bytes",
  447. )
  448. registry.register(
  449. BaseEquals("function"),
  450. encoding.BytesEncoder,
  451. decoding.BytesDecoder,
  452. label="function",
  453. )
  454. registry.register(
  455. BaseEquals("string"),
  456. encoding.TextStringEncoder,
  457. decoding.StringDecoder,
  458. label="string",
  459. )
  460. registry.register(
  461. has_arrlist,
  462. encoding.BaseArrayEncoder,
  463. decoding.BaseArrayDecoder,
  464. label="has_arrlist",
  465. )
  466. registry.register(
  467. is_base_tuple,
  468. encoding.TupleEncoder,
  469. decoding.TupleDecoder,
  470. label="is_base_tuple",
  471. )
  472. registry_packed = ABIRegistry()
  473. registry_packed.register_encoder(
  474. BaseEquals("uint"),
  475. encoding.PackedUnsignedIntegerEncoder,
  476. label="uint",
  477. )
  478. registry_packed.register_encoder(
  479. BaseEquals("int"),
  480. encoding.PackedSignedIntegerEncoder,
  481. label="int",
  482. )
  483. registry_packed.register_encoder(
  484. BaseEquals("address"),
  485. encoding.PackedAddressEncoder,
  486. label="address",
  487. )
  488. registry_packed.register_encoder(
  489. BaseEquals("bool"),
  490. encoding.PackedBooleanEncoder,
  491. label="bool",
  492. )
  493. registry_packed.register_encoder(
  494. BaseEquals("ufixed"),
  495. encoding.PackedUnsignedFixedEncoder,
  496. label="ufixed",
  497. )
  498. registry_packed.register_encoder(
  499. BaseEquals("fixed"),
  500. encoding.PackedSignedFixedEncoder,
  501. label="fixed",
  502. )
  503. registry_packed.register_encoder(
  504. BaseEquals("bytes", with_sub=True),
  505. encoding.PackedBytesEncoder,
  506. label="bytes<M>",
  507. )
  508. registry_packed.register_encoder(
  509. BaseEquals("bytes", with_sub=False),
  510. encoding.PackedByteStringEncoder,
  511. label="bytes",
  512. )
  513. registry_packed.register_encoder(
  514. BaseEquals("function"),
  515. encoding.PackedBytesEncoder,
  516. label="function",
  517. )
  518. registry_packed.register_encoder(
  519. BaseEquals("string"),
  520. encoding.PackedTextStringEncoder,
  521. label="string",
  522. )
  523. registry_packed.register_encoder(
  524. has_arrlist,
  525. encoding.PackedArrayEncoder,
  526. label="has_arrlist",
  527. )
  528. registry_packed.register_encoder(
  529. is_base_tuple,
  530. encoding.TupleEncoder,
  531. label="is_base_tuple",
  532. )