Skip to content

Commit

Permalink
Merge pull request #130 from davesque/tools-submodule
Browse files Browse the repository at this point in the history
Add strategies to package release for generating test ABI values
  • Loading branch information
davesque committed May 20, 2019
2 parents 0a5cab0 + 2f7ef13 commit e0e9286
Show file tree
Hide file tree
Showing 12 changed files with 536 additions and 58 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ test-all:
tox

build-docs:
sphinx-apidoc -o docs/ . setup.py 'eth_abi/utils/*' 'tests/*'
sphinx-apidoc -o docs/ . setup.py 'eth_abi/utils/*' 'eth_abi/tools/*' 'tests/*'
$(MAKE) -C docs clean
$(MAKE) -C docs html

Expand Down
15 changes: 11 additions & 4 deletions docs/eth_abi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ API
===

eth\_abi.abi module
--------------------
-------------------

.. automodule:: eth_abi.abi
:members:
:undoc-members:
:show-inheritance:

eth\_abi.base module
------------------------
--------------------

.. automodule:: eth_abi.base
:members: BaseCoder
Expand Down Expand Up @@ -39,15 +39,15 @@ eth\_abi.encoding module
:show-inheritance:

eth\_abi.exceptions module
---------------------------
--------------------------

.. automodule:: eth_abi.exceptions
:members:
:undoc-members:
:show-inheritance:

eth\_abi.registry module
-------------------------
------------------------

.. automodule:: eth_abi.registry

Expand All @@ -67,3 +67,10 @@ eth\_abi.grammar module
:members:
.. autofunction:: normalize
.. autofunction:: parse(type_str)

eth\_abi.tools module
---------------------

.. automodule:: eth_abi.tools

.. autofunction:: get_abi_strategy
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Table of Contents
codecs
nested_dynamic_arrays
grammar
tools
eth_abi
releases

Expand Down
66 changes: 66 additions & 0 deletions docs/tools.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
.. _tools:

Tools
=====

The ``eth_abi.tools`` module provides extra resources to users of ``eth-abi``
that are not required for typical use. It can be installed with ``pip`` as an
extra requirement:

.. code-block:: bash
pip install eth-abi[tools]
ABI Type Strategies
-------------------

The ``tools`` module provides the :meth:`~eth_abi.tools.get_abi_strategy`
function. This function returns a hypothesis strategy (value generator) for any
given ABI type specified by its canonical string representation:

.. doctest::

>>> from eth_abi.tools import get_abi_strategy
>>> import random

>>> uint_st = get_abi_strategy('uint8')
>>> uint_st
integers(min_value=0, max_value=255)
>>> uint_st.example(random.Random(0))
10

>>> uint_list_st = get_abi_strategy('uint8[2]')
>>> uint_list_st
lists(elements=integers(min_value=0, max_value=255), min_size=2, max_size=2)
>>> uint_list_st.example(random.Random(0))
[66, 247]

>>> fixed_st = get_abi_strategy('fixed8x1')
>>> fixed_st
decimals(min_value=-128, max_value=127, places=0).map(scale_by_Eneg1)
>>> fixed_st.example(random.Random(0))
Decimal('9.8')

>>> tuple_st = get_abi_strategy('(bool,string)')
>>> tuple_st
tuples(booleans(), text())
>>> tuple_st.example(random.Random(0))
(False, '')

.. warning::

In the above code snippet, we use the ``example`` method on hypothesis
strategy objects with an explicit random number generator with seed zero.
Both the use of that method and the use of zero-seeded random number
generators are *not* recommended for normal use. Their use in this context
is intended to make the code snippet reproducible. In normal test code,
use of the ``example`` method may prevent hypothesis from finding a useful
sample of values. See `here
<https://github.com/HypothesisWorks/hypothesis/blob/31a181fb7c5ce4227d4475f667a776ce86cd412c/hypothesis-python/src/hypothesis/searchstrategy/strategies.py#L258-L266>`_
for more information.

Hypothesis strategies can be used to conduct property testing on contract code.
For more information on property testing, visit the `Hypothesis homepage
<https://hypothesis.works>`_ or the `Hypothesis readthedocs site
<https://hypothesis.readthedocs.io/en/latest/>`_.
42 changes: 25 additions & 17 deletions eth_abi/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,19 +297,15 @@ def new_method(self, *args, **kwargs):
return new_method


class ABIRegistry(Copyable):
def __init__(self):
self._encoders = PredicateMapping('encoder registry')
self._decoders = PredicateMapping('decoder registry')

class BaseRegistry:
@staticmethod
def _register_coder(mapping, lookup, coder, label=None):
def _register(mapping, lookup, value, label=None):
if callable(lookup):
mapping.add(lookup, coder, label)
mapping.add(lookup, value, label)
return

if isinstance(lookup, str):
mapping.add(Equals(lookup), coder, lookup)
mapping.add(Equals(lookup), value, lookup)
return

raise TypeError(
Expand All @@ -319,7 +315,7 @@ def _register_coder(mapping, lookup, coder, label=None):
)

@staticmethod
def _unregister_coder(mapping, lookup_or_label):
def _unregister(mapping, lookup_or_label):
if callable(lookup_or_label):
mapping.remove_by_equality(lookup_or_label)
return
Expand All @@ -334,9 +330,10 @@ def _unregister_coder(mapping, lookup_or_label):
)
)

def _get_coder(self, mapping, type_str):
@staticmethod
def _get_registration(mapping, type_str):
try:
coder = mapping.find(type_str)
value = mapping.find(type_str)
except ValueError as e:
if 'No matching' in e.args[0]:
# If no matches found, attempt to parse in case lack of matches
Expand All @@ -345,6 +342,17 @@ def _get_coder(self, mapping, type_str):

raise

return value


class ABIRegistry(Copyable, BaseRegistry):
def __init__(self):
self._encoders = PredicateMapping('encoder registry')
self._decoders = PredicateMapping('decoder registry')

def _get_registration(self, mapping, type_str):
coder = super()._get_registration(mapping, type_str)

if isinstance(coder, type) and issubclass(coder, BaseCoder):
return coder.from_type_str(type_str, self)

Expand All @@ -358,7 +366,7 @@ def register_encoder(self, lookup: Lookup, encoder: Encoder, label: str=None) ->
the registration by name. For more information about arguments, refer
to :any:`register`.
"""
self._register_coder(self._encoders, lookup, encoder, label=label)
self._register(self._encoders, lookup, encoder, label=label)

@_clear_encoder_cache
def unregister_encoder(self, lookup_or_label: Lookup) -> None:
Expand All @@ -369,7 +377,7 @@ def unregister_encoder(self, lookup_or_label: Lookup) -> None:
encoder with the lookup function ``lookup_or_label`` will be
unregistered.
"""
self._unregister_coder(self._encoders, lookup_or_label)
self._unregister(self._encoders, lookup_or_label)

@_clear_decoder_cache
def register_decoder(self, lookup: Lookup, decoder: Decoder, label: str=None) -> None:
Expand All @@ -379,7 +387,7 @@ def register_decoder(self, lookup: Lookup, decoder: Decoder, label: str=None) ->
the registration by name. For more information about arguments, refer
to :any:`register`.
"""
self._register_coder(self._decoders, lookup, decoder, label=label)
self._register(self._decoders, lookup, decoder, label=label)

@_clear_decoder_cache
def unregister_decoder(self, lookup_or_label: Lookup) -> None:
Expand All @@ -390,7 +398,7 @@ def unregister_decoder(self, lookup_or_label: Lookup) -> None:
decoder with the lookup function ``lookup_or_label`` will be
unregistered.
"""
self._unregister_coder(self._decoders, lookup_or_label)
self._unregister(self._decoders, lookup_or_label)

def register(self, lookup: Lookup, encoder: Encoder, decoder: Decoder, label: str=None) -> None:
"""
Expand Down Expand Up @@ -440,7 +448,7 @@ def unregister(self, label: str) -> None:

@functools.lru_cache(maxsize=None)
def get_encoder(self, type_str):
return self._get_coder(self._encoders, type_str)
return self._get_registration(self._encoders, type_str)

def has_encoder(self, type_str: abi.TypeStr) -> bool:
"""
Expand All @@ -458,7 +466,7 @@ def has_encoder(self, type_str: abi.TypeStr) -> bool:

@functools.lru_cache(maxsize=None)
def get_decoder(self, type_str):
return self._get_coder(self._decoders, type_str)
return self._get_registration(self._decoders, type_str)

def copy(self):
"""
Expand Down
3 changes: 3 additions & 0 deletions eth_abi/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from ._strategies import ( # noqa: F401
get_abi_strategy,
)

0 comments on commit e0e9286

Please sign in to comment.