Skip to content

Commit

Permalink
Merge pull request #177 from njgheorghita/use-eth-typing
Browse files Browse the repository at this point in the history
Update eth-typing dependency
  • Loading branch information
njgheorghita authored Nov 25, 2019
2 parents f7d39f7 + 7445b0c commit d05279c
Show file tree
Hide file tree
Showing 16 changed files with 133 additions and 94 deletions.
45 changes: 27 additions & 18 deletions docs/utilities.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Utilities
-------------

.. _ChecksumAddress: https://eth-typing.readthedocs.io/en/latest/types.html#checksumaddress
.. _HexAddress: https://eth-typing.readthedocs.io/en/latest/types.html#hexaddress
.. _Address: https://eth-typing.readthedocs.io/en/latest/types.html#address
.. _HexStr: https://eth-typing.readthedocs.io/en/latest/types.html#hexstr

All functions can be imported directly from the ``eth_utils`` module

Alternatively, you can get the curried version of the functions by
Expand Down Expand Up @@ -520,8 +525,9 @@ the same address.
>>> is_same_address('0xd3cda913deb6f67967b99d67acdfa1712c293601', b'\xd3\xcd\xa9\x13\xde\xb6\xf6yg\xb9\x9dg\xac\xdf\xa1q,)6\x01')
True
``to_canonical_address(value)`` -> bytes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``to_canonical_address(value)`` -> Address_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Given any valid representation of an address return its canonical form.
Expand All @@ -537,8 +543,8 @@ Given any valid representation of an address return its canonical form.
>>> to_canonical_address(b'\xd3\xcd\xa9\x13\xde\xb6\xf6yg\xb9\x9dg\xac\xdf\xa1q,)6\x01')
b'\xd3\xcd\xa9\x13\xde\xb6\xf6yg\xb9\x9dg\xac\xdf\xa1q,)6\x01'
``to_checksum_address(value)`` -> text
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``to_checksum_address(value)`` -> ChecksumAddress_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Given any valid representation of an address return the checksummed
representation.
Expand All @@ -555,8 +561,9 @@ representation.
>>> to_checksum_address(b'\xd3\xcd\xa9\x13\xde\xb6\xf6yg\xb9\x9dg\xac\xdf\xa1q,)6\x01')
'0xd3CdA913deB6f67967B99D67aCDFa1712C293601'
``to_normalized_address(value)`` -> text
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``to_normalized_address(value)`` -> HexAddress_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Given any valid representation of an address return the normalized
representation.
Expand Down Expand Up @@ -615,8 +622,8 @@ encoded as UTF-8.
>>> to_bytes(text='cowmö')
b'cowm\xc3\xb6'
``to_hex(<bytes/int/bool>, text=<str>, hexstr=<str>)`` -> str
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``to_hex(<bytes/int/bool>, text=<str>, hexstr=<str>)`` -> HexStr_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Takes a variety of inputs and returns it in its hexadecimal
representation. It follows the rules for converting to hex in the
Expand Down Expand Up @@ -1141,18 +1148,19 @@ returned by the callable.
Hexidecimal Utils
~~~~~~~~~~~~~~~~~
``add_0x_prefix(value)`` -> string
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``add_0x_prefix(value: HexStr)`` -> HexStr_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Returns ``value`` with a ``0x`` prefix. If the value is already prefixed
it is returned as-is. Value must be a string literal.
it is returned as-is. Value must be a HexStr_.
.. doctest::
>>> from eth_utils import add_0x_prefix
>>> add_0x_prefix('12345')
>>> from eth_typing import HexStr
>>> add_0x_prefix(HexStr('12345'))
'0x12345'
>>> add_0x_prefix('0x12345')
>>> add_0x_prefix(HexStr('0x12345'))
'0x12345'
``decode_hex(value)`` -> bytes
Expand Down Expand Up @@ -1236,18 +1244,19 @@ type.
Traceback (most recent call last):
TypeError: is_hex requires text typed arguments.
``remove_0x_prefix(value)`` -> string
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``remove_0x_prefix(value: HexStr)`` -> HexStr_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Returns ``value`` with the ``0x`` prefix stripped. If the value does not
have a ``0x`` prefix it is returned as-is. Value must be string literal.
have a ``0x`` prefix it is returned as-is. Value must be a HexStr_.
.. doctest::
>>> from eth_utils import remove_0x_prefix
>>> remove_0x_prefix('12345')
>>> from eth_typing import HexStr
>>> remove_0x_prefix(HexStr('12345'))
'12345'
>>> remove_0x_prefix('0x12345')
>>> remove_0x_prefix(HexStr('0x12345'))
'12345'
Expand Down
2 changes: 1 addition & 1 deletion eth_utils/abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from .crypto import keccak


def collapse_if_tuple(abi):
def collapse_if_tuple(abi: Dict[str, Any]) -> str:
"""Converts a tuple from a dict to a parenthesized list of its types.
>>> from eth_utils.abi import collapse_if_tuple
Expand Down
19 changes: 11 additions & 8 deletions eth_utils/address.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from typing import Any, AnyStr

from eth_typing import Address, AnyAddress, ChecksumAddress, HexAddress, HexStr

from .conversions import hexstr_if_str, to_hex
from .crypto import keccak
from .hexadecimal import add_0x_prefix, decode_hex, encode_hex, is_hex, remove_0x_prefix
from .types import is_bytes, is_text
from .typing import Address, AnyAddress, ChecksumAddress, HexAddress


def is_hex_address(value: Any) -> bool:
Expand Down Expand Up @@ -107,16 +108,18 @@ def to_checksum_address(value: AnyStr) -> ChecksumAddress:
Makes a checksum address given a supported format.
"""
norm_address = to_normalized_address(value)
address_hash = encode_hex(keccak(text=remove_0x_prefix(norm_address)))
address_hash = encode_hex(keccak(text=remove_0x_prefix(HexStr(norm_address))))

checksum_address = add_0x_prefix(
"".join(
(
norm_address[i].upper()
if int(address_hash[i], 16) > 7
else norm_address[i]
HexStr(
"".join(
(
norm_address[i].upper()
if int(address_hash[i], 16) > 7
else norm_address[i]
)
for i in range(2, 42)
)
for i in range(2, 42)
)
)
return ChecksumAddress(HexAddress(checksum_address))
Expand Down
21 changes: 13 additions & 8 deletions eth_utils/conversions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from typing import Callable, Union, cast
from typing import Callable, TypeVar, Union

from eth_typing import HexStr, Primitives

from .decorators import validate_conversion_arguments
from .encoding import big_endian_to_int, int_to_big_endian
from .hexadecimal import add_0x_prefix, decode_hex, encode_hex, is_hex, remove_0x_prefix
from .types import is_boolean, is_integer, is_string
from .typing import HexStr, Primitives, T

T = TypeVar("T")


@validate_conversion_arguments
Expand All @@ -17,24 +20,24 @@ def to_hex(
https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding
"""
if hexstr is not None:
return HexStr(add_0x_prefix(hexstr.lower()))
return add_0x_prefix(HexStr(hexstr.lower()))

if text is not None:
return HexStr(encode_hex(text.encode("utf-8")))
return encode_hex(text.encode("utf-8"))

if is_boolean(primitive):
return HexStr("0x1") if primitive else HexStr("0x0")

if isinstance(primitive, (bytes, bytearray)):
return HexStr(encode_hex(primitive))
return encode_hex(primitive)
elif is_string(primitive):
raise TypeError(
"Unsupported type: The primitive argument must be one of: bytes,"
"bytearray, int or bool and not str"
)

if is_integer(primitive):
return HexStr(hex(cast(int, primitive)))
return HexStr(hex(primitive))

raise TypeError(
"Unsupported type: '{0}'. Must be one of: bool, str, bytes, bytearray"
Expand Down Expand Up @@ -111,7 +114,7 @@ def to_text(
elif isinstance(primitive, (bytes, bytearray)):
return primitive.decode("utf-8")
elif is_integer(primitive):
byte_encoding = int_to_big_endian(primitive) # type: ignore
byte_encoding = int_to_big_endian(primitive)
return to_text(byte_encoding)
raise TypeError("Expected an int, bytes, bytearray or hexstr.")

Expand Down Expand Up @@ -143,7 +146,9 @@ def hexstr_if_str(
:param hexstr_or_primitive bytes, str, int: value to convert
"""
if isinstance(hexstr_or_primitive, str):
if remove_0x_prefix(hexstr_or_primitive) and not is_hex(hexstr_or_primitive):
if remove_0x_prefix(HexStr(hexstr_or_primitive)) and not is_hex(
hexstr_or_primitive
):
raise ValueError(
"when sending a str, it must be a hex string. Got: {0!r}".format(
hexstr_or_primitive
Expand Down
2 changes: 1 addition & 1 deletion eth_utils/currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def to_wei(number: int, unit: str) -> int:
s_number = str(number)
unit_value = units[unit.lower()]

if d_number == 0:
if d_number == decimal.Decimal(0):
return 0

if d_number < 1 and "." in s_number:
Expand Down
32 changes: 17 additions & 15 deletions eth_utils/decorators.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import functools
import itertools
from typing import Any, Callable, Dict, Iterable, Type
from typing import Any, Callable, Dict, Iterable, Type, TypeVar

from .types import is_text

T = TypeVar("T")


class combomethod(object):
def __init__(self, method):
def __init__(self, method: Callable[..., Any]) -> None:
self.method = method

def __get__(self, obj=None, objtype=None):
def __get__(self, obj: T = None, objtype: Type[T] = None) -> Callable[..., T]:
@functools.wraps(self.method)
def _wrapper(*args, **kwargs):
def _wrapper(*args: Any, **kwargs: Any) -> Any:
if obj is not None:
return self.method(obj, *args, **kwargs)
else:
Expand All @@ -20,42 +22,42 @@ def _wrapper(*args, **kwargs):
return _wrapper


def _has_one_val(*args, **kwargs):
def _has_one_val(*args: T, **kwargs: T) -> bool:
vals = itertools.chain(args, kwargs.values())
not_nones = list(filter(lambda val: val is not None, vals))
return len(not_nones) == 1


def _assert_one_val(*args, **kwargs):
def _assert_one_val(*args: T, **kwargs: T) -> None:
if not _has_one_val(*args, **kwargs):
raise TypeError(
"Exactly one of the passed values can be specified. "
"Instead, values were: %r, %r" % (args, kwargs)
)


def _hexstr_or_text_kwarg_is_text_type(**kwargs):
def _hexstr_or_text_kwarg_is_text_type(**kwargs: T) -> bool:
value = kwargs["hexstr"] if "hexstr" in kwargs else kwargs["text"]
return is_text(value)


def _assert_hexstr_or_text_kwarg_is_text_type(**kwargs):
def _assert_hexstr_or_text_kwarg_is_text_type(**kwargs: T) -> None:
if not _hexstr_or_text_kwarg_is_text_type(**kwargs):
raise TypeError(
"Arguments passed as hexstr or text must be of text type. "
"Instead, value was: %r" % (repr(next(list(kwargs.values()))))
"Instead, value was: %r" % (repr(next(iter(list(kwargs.values())))))
)


def _validate_supported_kwarg(kwargs):
def _validate_supported_kwarg(kwargs: Any) -> None:
if next(iter(kwargs)) not in ["primitive", "hexstr", "text"]:
raise TypeError(
"Kwarg must be 'primitive', 'hexstr', or 'text'. "
"Instead, kwarg was: %r" % (next(iter(kwargs)))
)


def validate_conversion_arguments(to_wrap):
def validate_conversion_arguments(to_wrap: Callable[..., T]) -> Callable[..., T]:
"""
Validates arguments for conversion functions.
- Only a single argument is present
Expand All @@ -64,7 +66,7 @@ def validate_conversion_arguments(to_wrap):
"""

@functools.wraps(to_wrap)
def wrapper(*args, **kwargs):
def wrapper(*args: Any, **kwargs: Any) -> T:
_assert_one_val(*args, **kwargs)
if kwargs:
_validate_supported_kwarg(kwargs)
Expand All @@ -76,14 +78,14 @@ def wrapper(*args, **kwargs):
return wrapper


def return_arg_type(at_position):
def return_arg_type(at_position: int) -> Callable[..., Callable[..., T]]:
"""
Wrap the return value with the result of `type(args[at_position])`
"""

def decorator(to_wrap):
def decorator(to_wrap: Callable[..., Any]) -> Callable[..., T]:
@functools.wraps(to_wrap)
def wrapper(*args, **kwargs):
def wrapper(*args: Any, **kwargs: Any) -> T:
result = to_wrap(*args, **kwargs)
ReturnType = type(args[at_position])
return ReturnType(result)
Expand Down
2 changes: 1 addition & 1 deletion eth_utils/functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def combine(
def apply_to_return_value(
callback: Callable[..., T]
) -> Callable[..., Callable[..., T]]:
def outer(fn):
def outer(fn: Callable[..., T]) -> Callable[..., T]:
# We would need to type annotate *args and **kwargs but doing so segfaults
# the PyPy builds. We ignore instead.
@functools.wraps(fn)
Expand Down
6 changes: 3 additions & 3 deletions eth_utils/hexadecimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def encode_hex(value: AnyStr) -> HexStr:
if not is_string(value):
raise TypeError("Value must be an instance of str or unicode")
binary_hex = codecs.encode(value, "hex") # type: ignore
return add_0x_prefix(binary_hex.decode("ascii"))
return add_0x_prefix(HexStr(binary_hex.decode("ascii")))


def is_0x_prefixed(value: Any) -> bool:
Expand All @@ -35,14 +35,14 @@ def is_0x_prefixed(value: Any) -> bool:

def remove_0x_prefix(value: HexStr) -> HexStr:
if is_0x_prefixed(value):
return value[2:]
return HexStr(value[2:])
return value


def add_0x_prefix(value: HexStr) -> HexStr:
if is_0x_prefixed(value):
return value
return "0x" + value
return HexStr("0x" + value)


def is_hex(value: Any) -> bool:
Expand Down
Loading

0 comments on commit d05279c

Please sign in to comment.