From 7195a46fb07f5dc255ec40611233d75fa223e0b9 Mon Sep 17 00:00:00 2001 From: Juliya Smith Date: Thu, 26 Oct 2023 11:22:21 -0500 Subject: [PATCH] fix: network filtering --- src/ape/api/networks.py | 22 +++++++++++-- src/ape/managers/networks.py | 31 +++++++++++++++--- src/ape_networks/_cli.py | 45 +++++++++++++++++--------- tests/integration/cli/test_networks.py | 8 +++++ 4 files changed, 84 insertions(+), 22 deletions(-) diff --git a/src/ape/api/networks.py b/src/ape/api/networks.py index 6b22f32841..e579c58730 100644 --- a/src/ape/api/networks.py +++ b/src/ape/api/networks.py @@ -1,7 +1,18 @@ from functools import partial from pathlib import Path from tempfile import mkdtemp -from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional, Tuple, Type, Union +from typing import ( + TYPE_CHECKING, + Any, + Collection, + Dict, + Iterator, + List, + Optional, + Tuple, + Type, + Union, +) from eth_account import Account as EthAccount from eth_account._utils.legacy_transactions import ( @@ -430,7 +441,9 @@ def get_network(self, network_name: str) -> "NetworkAPI": raise NetworkNotFoundError(network_name, ecosystem=self.name, options=self.networks) - def get_network_data(self, network_name: str) -> Dict: + def get_network_data( + self, network_name: str, provider_filter: Optional[Collection[str]] = None + ) -> Dict: """ Get a dictionary of data about providers in the network. @@ -439,6 +452,8 @@ def get_network_data(self, network_name: str) -> Dict: Args: network_name (str): The name of the network to get provider data from. + provider_filter (Optional[Collection[str]]): Optional filter the providers + by name. Returns: dict: A dictionary containing the providers in a network. @@ -456,6 +471,9 @@ def get_network_data(self, network_name: str) -> Dict: data["explorer"] = str(network.explorer.name) for provider_name in network.providers: + if provider_filter and provider_name not in provider_filter: + continue + provider_data: Dict = {"name": str(provider_name)} # Only add isDefault key when True diff --git a/src/ape/managers/networks.py b/src/ape/managers/networks.py index 64cf8ae044..8d26beefa9 100644 --- a/src/ape/managers/networks.py +++ b/src/ape/managers/networks.py @@ -1,6 +1,6 @@ import json from functools import cached_property -from typing import Dict, Iterator, List, Optional, Set, Union +from typing import Collection, Dict, Iterator, List, Optional, Set, Union import yaml @@ -500,15 +500,33 @@ def network_data(self) -> Dict: Returns: dict """ + return self.get_network_data() + + def get_network_data( + self, + ecosystem_filter: Optional[Collection[str]] = None, + network_filter: Optional[Collection[str]] = None, + provider_filter: Optional[Collection[str]] = None, + ): data: Dict = {"ecosystems": []} for ecosystem_name in self: - ecosystem_data = self._get_ecosystem_data(ecosystem_name) + if ecosystem_filter and ecosystem_name not in ecosystem_filter: + continue + + ecosystem_data = self._get_ecosystem_data( + ecosystem_name, network_filter=network_filter, provider_filter=provider_filter + ) data["ecosystems"].append(ecosystem_data) return data - def _get_ecosystem_data(self, ecosystem_name: str) -> Dict: + def _get_ecosystem_data( + self, + ecosystem_name: str, + network_filter: Optional[Collection[str]] = None, + provider_filter: Optional[Collection[str]] = None, + ) -> Dict: ecosystem = self[ecosystem_name] ecosystem_data: Dict = {"name": str(ecosystem_name)} @@ -519,16 +537,21 @@ def _get_ecosystem_data(self, ecosystem_name: str) -> Dict: ecosystem_data["networks"] = [] for network_name in getattr(self, ecosystem_name).networks: - network_data = ecosystem.get_network_data(network_name) + if network_filter and network_name not in network_filter: + continue + + network_data = ecosystem.get_network_data(network_name, provider_filter=provider_filter) ecosystem_data["networks"].append(network_data) return ecosystem_data @property + # TODO: Remove in 0.7 def networks_yaml(self) -> str: """ Get a ``yaml`` ``str`` representing all the networks in all the ecosystems. + **NOTE**: Deprecated. View the result via CLI command ``ape networks list --format yaml``. diff --git a/src/ape_networks/_cli.py b/src/ape_networks/_cli.py index 9acb336172..9606b5d030 100644 --- a/src/ape_networks/_cli.py +++ b/src/ape_networks/_cli.py @@ -1,6 +1,8 @@ +import json from typing import Callable, Dict import click +import yaml from rich import print as echo_rich_text from rich.tree import Tree @@ -9,6 +11,7 @@ from ape.cli import ape_cli_context, network_option from ape.cli.choices import OutputFormat from ape.cli.options import output_format_option +from ape.exceptions import NetworkError from ape.logging import LogLevel from ape.types import _LazySequence @@ -45,10 +48,16 @@ def gen(): @_filter_option("network", _lazy_get("network")) @_filter_option("provider", _lazy_get("provider")) def _list(cli_ctx, output_format, ecosystem_filter, network_filter, provider_filter): + network_data = cli_ctx.network_manager.get_network_data( + ecosystem_filter=ecosystem_filter, + network_filter=network_filter, + provider_filter=provider_filter, + ) + if output_format == OutputFormat.TREE: default_suffix = "[dim default] (default)" - ecosystems = {e["name"]: e for e in cli_ctx.network_manager.network_data["ecosystems"]} - ecosystems = {n: ecosystems[n] for n in sorted(ecosystems)} + ecosystems = network_data["ecosystems"] + ecosystems = sorted(ecosystems, key=lambda e: e["name"]) def make_sub_tree(data: Dict, create_tree: Callable) -> Tree: name = f"[bold green]{data['name']}" @@ -58,24 +67,12 @@ def make_sub_tree(data: Dict, create_tree: Callable) -> Tree: sub_tree = create_tree(name) return sub_tree - for ecosystem_name, ecosystem in ecosystems.items(): - if ecosystem_filter and ecosystem["name"] not in ecosystem_filter: - continue - + for ecosystem in ecosystems: ecosystem_tree = make_sub_tree(ecosystem, Tree) _networks = {n["name"]: n for n in ecosystem["networks"]} _networks = {n: _networks[n] for n in sorted(_networks)} - if network_filter: - _networks = {n: v for n, v in _networks.items() if n in network_filter} - for network_name, network in _networks.items(): - if network_filter and network_name not in network_filter: - continue - providers = network["providers"] - if provider_filter: - providers = [p for p in providers if p["name"] in provider_filter] - if providers: network_tree = make_sub_tree(network, ecosystem_tree.add) providers = sorted(providers, key=lambda p: p["name"]) @@ -86,7 +83,23 @@ def make_sub_tree(data: Dict, create_tree: Callable) -> Tree: echo_rich_text(ecosystem_tree) elif output_format == OutputFormat.YAML: - click.echo(cli_ctx.network_manager.networks_yaml.strip()) + if not isinstance(network_data, dict): + raise TypeError( + f"Unexpected network data type: {type(network_data)}. " + f"Expecting dict. YAML dump will fail." + ) + + try: + click.echo(yaml.dump(network_data, sort_keys=True).strip()) + except ValueError as err: + try: + data_str = json.dumps(network_data) + except Exception: + data_str = str(network_data) + + raise NetworkError( + f"Network data did not dump to YAML: {data_str}\nActual err: {err}" + ) from err @cli.command() diff --git a/tests/integration/cli/test_networks.py b/tests/integration/cli/test_networks.py index fd763e1332..966eede1e1 100644 --- a/tests/integration/cli/test_networks.py +++ b/tests/integration/cli/test_networks.py @@ -109,6 +109,14 @@ def test_list_yaml(ape_cli, runner): # Skip these lines in case test-runner has installed providers continue + if ( + expected_line.lstrip().startswith("- name:") + and expected_line not in result.output + and "explorer:" in result.output + ): + # May have explorers installed - ignore that. + expected_line = expected_line.lstrip(" -") + assert expected_line in result.output, result.output