Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: show nicer errors during bad network choices #2234

Merged
merged 3 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/ape/cli/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@

from ape.api.accounts import AccountAPI
from ape.api.providers import ProviderAPI
from ape.exceptions import AccountsError
from ape.exceptions import (
AccountsError,
EcosystemNotFoundError,
NetworkNotFoundError,
ProviderNotFoundError,
)
from ape.types import _LazySequence
from ape.utils.basemodel import ManagerAccessMixin

Expand Down Expand Up @@ -369,6 +374,12 @@ def convert(self, value: Any, param: Optional[Parameter], ctx: Optional[Context]
# (as-is the case for custom-forked networks).
try:
choice = networks.get_provider_from_choice(network_choice=value)

except (EcosystemNotFoundError, NetworkNotFoundError, ProviderNotFoundError) as err:
# This error makes more sense, as it has attempted parsing.
# Show this message as the BadParameter message.
raise click.BadParameter(str(err)) from err

except Exception as err:
# If an error was not raised for some reason, raise a simpler error.
# NOTE: Still avoid showing the massive network options list.
Expand Down
2 changes: 1 addition & 1 deletion src/ape/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ def __init__(
if options:
close_matches = difflib.get_close_matches(provider, options, cutoff=0.6)
if close_matches:
message = f"{message} Did you mean '{', '.join(close_matches)}'?"
message = f"{message}. Did you mean '{', '.join(close_matches)}'?"
else:
# No close matches. Show all provider options.
options_str = "\n".join(sorted(options))
Expand Down
104 changes: 61 additions & 43 deletions tests/functional/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import click
import pytest
from click import BadParameter

from ape.cli import (
AccountAliasPromptChoice,
Expand Down Expand Up @@ -839,49 +840,66 @@ def test_parse_network_when_explicit_none(mocker):
assert network_ctx is None


def test_network_choice():
network_choice = NetworkChoice()
actual = network_choice.convert("ethereum:local:test", None, None)
assert actual.name == "test"
assert actual.network.name == "local"


@pytest.mark.parametrize("prefix", ("", "ethereum:custom:"))
def test_network_choice_custom_adhoc_network(prefix):
network_choice = NetworkChoice()
uri = "https://example.com"
actual = network_choice.convert(f"{prefix}{uri}", None, None)
assert actual.uri == uri
assert actual.network.name == "custom"


def test_network_choice_custom_config_network(custom_networks_config_dict, project):
data = copy.deepcopy(custom_networks_config_dict)

# Was a bug where couldn't have this name.
data["networks"]["custom"][0]["name"] = "custom"

_get_networks_sequence_from_cache.cache_clear()

network_choice = NetworkChoice()
with project.temp_config(**data):
actual = network_choice.convert("ethereum:custom", None, None)

assert actual.network.name == "custom"


def test_network_choice_when_custom_local_network():
network_choice = NetworkChoice()
uri = "https://example.com"
actual = network_choice.convert(f"ethereum:local:{uri}", None, None)
assert actual.uri == uri
assert actual.network.name == "local"


def test_network_choice_explicit_none():
network_choice = NetworkChoice()
actual = network_choice.convert("None", None, None)
assert actual == _NONE_NETWORK
class TestNetworkChoice:
@pytest.fixture
def network_choice(self):
return NetworkChoice()

def test_test(self, network_choice):
actual = network_choice.convert("ethereum:local:test", None, None)
assert actual.name == "test"
assert actual.network.name == "local"

@pytest.mark.parametrize("prefix", ("", "ethereum:custom:"))
def test_adhoc(self, network_choice, prefix):
uri = "https://example.com"
actual = network_choice.convert(f"{prefix}{uri}", None, None)
assert actual.uri == uri
assert actual.network.name == "custom"

def test_custom_config_network(self, custom_networks_config_dict, project, network_choice):
data = copy.deepcopy(custom_networks_config_dict)

# Was a bug where couldn't have this name.
data["networks"]["custom"][0]["name"] = "custom"

_get_networks_sequence_from_cache.cache_clear()

with project.temp_config(**data):
actual = network_choice.convert("ethereum:custom", None, None)

assert actual.network.name == "custom"

def test_custom_local_network(self, network_choice):
uri = "https://example.com"
actual = network_choice.convert(f"ethereum:local:{uri}", None, None)
assert actual.uri == uri
assert actual.network.name == "local"

def test_explicit_none(self, network_choice):
actual = network_choice.convert("None", None, None)
assert actual == _NONE_NETWORK

def test_bad_ecosystem(self, network_choice):
# NOTE: "ethereum" is spelled wrong.
expected = r"No ecosystem named 'etheruem'\. Did you mean 'ethereum'\?"
with pytest.raises(BadParameter, match=expected):
network_choice.convert("etheruem:local:test", None, None)

def test_bad_network(self, network_choice):
# NOTE: "local" is spelled wrong.
expected = r"No network in 'ethereum' named 'lokal'\. Did you mean 'local'\?"
with pytest.raises(BadParameter, match=expected):
network_choice.convert("ethereum:lokal:test", None, None)

def test_bad_provider(self, network_choice):
# NOTE: "test" is spelled wrong.
expected = (
r"No provider named 'teest' in network 'local' in "
r"ecosystem 'ethereum'\. Did you mean 'test'\?"
)
with pytest.raises(BadParameter, match=expected):
network_choice.convert("ethereum:local:teest", None, None)


def test_config_override_option(runner):
Expand Down
Loading