Skip to content

Commit

Permalink
feat: allow convert(0, AddressType) to work (#2066)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey committed May 7, 2024
1 parent f001b87 commit 176dc2a
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 13 deletions.
39 changes: 35 additions & 4 deletions src/ape/managers/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
from typing import Any, Dict, List, Sequence, Tuple, Type, Union

from dateutil.parser import parse
from eth_pydantic_types import HexBytes
from eth_pydantic_types import Address, HexBytes
from eth_typing.evm import ChecksumAddress
from eth_utils import (
is_0x_prefixed,
is_checksum_address,
is_hex,
is_hex_address,
to_checksum_address,
to_hex,
to_int,
)
from ethpm_types import ConstructorABI, EventABI, MethodABI
Expand Down Expand Up @@ -149,11 +148,43 @@ class IntAddressConverter(ConverterAPI):
A converter that converts an integer address to an :class:`~ape.types.address.AddressType`.
"""

_cache: Dict[int, Union[AddressType, bool]] = {}

def is_convertible(self, value: Any) -> bool:
return isinstance(value, int) and is_hex_address(to_hex(value))
if not isinstance(value, int):
return False
elif isinstance(self._cache.get(value), str):
return True

val = self._convert(value)
self._cache[value] = val
return isinstance(val, str)

def convert(self, value: Any) -> AddressType:
return to_checksum_address(to_hex(value))
err_msg = f"Failed to convert '{value}' to 'AddressType'."
if cached_val := self._cache.get(value):
if not isinstance(cached_val, str):
# Shouldn't get here in normal execution.
raise ConversionError(err_msg)

return cached_val

# Shouldn't get here in normal execution.
res = self._convert(value)
self._cache[value] = res

if not isinstance(res, str):
raise ConversionError(err_msg)

return res

def _convert(self, value: int) -> Union[AddressType, bool]:
try:
val = Address.__eth_pydantic_validate__(value)
except Exception:
return False

return AddressType(to_checksum_address(val))


class TimestampConverter(ConverterAPI):
Expand Down
44 changes: 44 additions & 0 deletions tests/functional/conversion/test_address.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import pytest

from ape.managers.converters import HexAddressConverter, IntAddressConverter
from ape.types import AddressType


Expand All @@ -19,3 +22,44 @@ def test_convert_address_to_int(convert, owner):
def test_convert_account_to_int(convert, owner):
actual = convert(owner, int)
assert actual == int(owner.address, 16)


def test_convert_0_to_address(convert, zero_address):
assert convert(0, AddressType) == zero_address


class TestHexAddressConverter:
@pytest.fixture(scope="class")
def converter(self):
return HexAddressConverter()

def test_is_convertible_hex_str(self, converter):
assert not converter.is_convertible("0x123")

def test_is_convertible_address(self, converter, owner):
# Is already an address!
assert not converter.is_convertible(str(owner.address))

def test_convert_not_canonical_address(self, converter):
actual = converter.convert("0x0ffffffaaaaaaaabbbbbbb333337777eeeeeee00")
expected = "0x0fFFfffAaAaAaAaBBBbBbb333337777eeeeeEe00"
assert actual == expected


class TestIntAddressConverter:
@pytest.fixture(scope="class")
def converter(self):
return IntAddressConverter()

def test_is_convertible(self, converter, owner):
int_address = int(owner.address, 16)
assert converter.is_convertible(int_address)

def test_is_convertible_random_int(self, converter):
assert converter.is_convertible(0)

@pytest.mark.parametrize("val", (0, 1))
def test_convert_simple_int(self, converter, val, zero_address):
actual = converter.convert(val)
expected = f"{zero_address[:-1]}{val}"
assert actual == expected
17 changes: 8 additions & 9 deletions tests/functional/conversion/test_hex.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
from ape.managers.converters import HexConverter, HexIntConverter


def test_hex_str(convert):
hex_value = "0xA100"
int_str_value = "100"
hex_expected = 41216
int_expected = 100
assert convert(hex_value, int) == hex_expected
assert convert(int_str_value, int) == int_expected
assert convert(hex_value, bytes) == HexBytes(hex_value)
assert convert(int_expected, bytes) == HexBytes(int_expected)
@pytest.mark.parametrize("val", ("0xA100", "0x0A100", "0x00a100"))
def test_hex_str(convert, val):
assert convert(val, int) == 0xA100
assert int(convert(val, bytes).hex(), 16) == int(HexBytes(0xA100).hex(), 16)


def test_int_str(convert):
assert convert("100", int) == 100


def test_missing_prefix(convert):
Expand Down

0 comments on commit 176dc2a

Please sign in to comment.