Skip to content

Commit

Permalink
feat: ability to get conversion classes by name (#2497)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Feb 11, 2025
1 parent c8022fe commit 5455d4a
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 2 deletions.
11 changes: 11 additions & 0 deletions src/ape/api/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,14 @@ def convert(self, value: Any) -> ConvertedType:
# '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
"""

@property
def name(self) -> str:
"""
The calculated name of the converter class.
Typically, it is the lowered prefix of the class without
the "Converter" or "Conversions" suffix.
"""
class_name = self.__class__.__name__
name = class_name.replace("Converter", "").replace("Conversions", "")
return name.lower()
29 changes: 29 additions & 0 deletions src/ape/managers/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,35 @@ def convert(self, value: Any, to_type: Union[type, tuple, list]) -> Any:

raise ConversionError(f"No conversion registered to handle '{value}'.")

def get_converters_by_type(self, converter_type: type) -> list[ConverterAPI]:
"""
Get all the converters for the given type.
Args:
converter_type (type): The type to get converters for.
Returns:
list[ConverterAPI]: All registered converters for the given type.
"""
return self._converters.get(converter_type, [])

def get_converter(self, name: str) -> ConverterAPI:
"""
Get a converter plugin by name.
Args:
name (str): The name of the converter.
Returns:
:class:`~ape.api.converters.ConverterAPI`: The converter.
"""
for converter_ls in self._converters.values():
for converter in converter_ls:
if converter.name == name.lower():
return converter

raise ConversionError("No converters with name '{name}'.'")

def convert_method_args(
self,
abi: Union["MethodABI", "ConstructorABI", "EventABI"],
Expand Down
4 changes: 2 additions & 2 deletions src/ape_ethereum/_converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@


class WeiConversions(ConverterAPI):
"""Converts units like `1 ether` to 1e18 wei"""
"""Converts units like `1 ether` to 1e18 wei."""

def is_convertible(self, value: str) -> bool:
if not isinstance(value, str):
return False

if " " not in value or len(value.split(" ")) > 2:
elif " " not in value or len(value.split(" ")) > 2:
return False

val, unit = value.split(" ")
Expand Down
43 changes: 43 additions & 0 deletions tests/functional/test_converters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import pytest

from ape.managers.converters import (
AddressAPIConverter,
BytesAddressConverter,
HexConverter,
HexIntConverter,
IntAddressConverter,
StringDecimalConverter,
StringIntConverter,
)
from ape.types.address import AddressType
from ape.utils.basemodel import ManagerAccessMixin
from ape_ethereum._converters import WeiConversions

NAME_TO_CONVERTER = {
"AddressAPI": AddressAPIConverter,
"BytesAddress": BytesAddressConverter,
"hex": HexConverter,
"HexInt": HexIntConverter,
"IntAddress": IntAddressConverter,
"StringDecimal": StringDecimalConverter,
"StringInt": StringIntConverter,
"wei": WeiConversions,
}


@pytest.fixture
def conversion_manager():
return ManagerAccessMixin.conversion_manager


@pytest.mark.parametrize("name", NAME_TO_CONVERTER)
def test_get_converter(name, conversion_manager):
actual = conversion_manager.get_converter(name)
expected = NAME_TO_CONVERTER[name]
assert isinstance(actual, expected)


def test_get_converters_by_type(conversion_manager):
converters = conversion_manager.get_converters_by_type(AddressType)
for expected in (AddressAPIConverter, IntAddressConverter, BytesAddressConverter):
assert any(isinstance(c, expected) for c in converters)

0 comments on commit 5455d4a

Please sign in to comment.