Skip to content

Commit

Permalink
Merge branch 'feat/0-9' of github.com:ApeWorX/ape into feat/0-9
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/ape_ethereum/provider.py
  • Loading branch information
bitwise-constructs committed Nov 5, 2024
2 parents df3cb55 + ffec552 commit a3b94e4
Show file tree
Hide file tree
Showing 33 changed files with 677 additions and 322 deletions.
61 changes: 59 additions & 2 deletions docs/methoddocs/utils.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,65 @@
# ape.utils

## ABI

```{eval-rst}
.. automodule:: ape.utils.abi
:members:
:show-inheritance:
```

## Basemodel

```{eval-rst}
.. automodule:: ape.utils.basemodel
:members:
:show-inheritance:
```

## Miscellaneous

```{eval-rst}
.. automodule:: ape.utils.misc
:members:
:show-inheritance:
```

## OS

```{eval-rst}
.. automodule:: ape.utils.os
:members:
:show-inheritance:
```

## Process

```{eval-rst}
.. automodule:: ape.utils.process
:members:
:show-inheritance:
```

## RPC

```{eval-rst}
.. automodule:: ape.utils.rpc
:members:
:show-inheritance:
```

## Testing

```{eval-rst}
.. automodule:: ape.utils.testing
:members:
:show-inheritance:
```

## Trace

```{eval-rst}
.. automodule:: ape.utils
.. automodule:: ape.utils.trace
:members:
:show-inheritance:
:exclude-members: abstractmethod, dataclass, __init__
```
12 changes: 6 additions & 6 deletions src/ape/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import signal
import threading
from typing import Any

if threading.current_thread() is threading.main_thread():
# If we are in the main thread, we can safely set the signal handler
signal.signal(signal.SIGINT, lambda s, f: _sys.exit(130))

import sys as _sys
from importlib import import_module

__all__ = [
"accounts",
Expand All @@ -23,16 +21,18 @@
]


def __getattr__(name: str) -> Any:
def __getattr__(name: str):
if name not in __all__:
raise AttributeError(name)

elif name == "reverts":
contextmanagers = import_module("ape.pytest.contextmanagers")
return contextmanagers.RevertsContextManager
from ape.pytest.contextmanagers import RevertsContextManager

return RevertsContextManager

else:
access = import_module("ape.managers.project").ManagerAccessMixin
from ape.utils.basemodel import ManagerAccessMixin as access

if name == "Contract":
return access.chain_manager.contracts.instance_at

Expand Down
37 changes: 4 additions & 33 deletions src/ape/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,18 @@
from collections.abc import Iterable
from functools import cached_property
from gettext import gettext
from importlib import import_module
from importlib.metadata import entry_points
from pathlib import Path
from typing import TYPE_CHECKING, Any, Optional
from typing import Any, Optional
from warnings import catch_warnings, simplefilter

import click
import rich
import yaml

from ape.cli.options import ape_cli_context
from ape.exceptions import Abort, ApeException, ConfigError, handle_ape_exception
from ape.exceptions import Abort, ApeException, handle_ape_exception
from ape.logging import logger

if TYPE_CHECKING:
from click import Context

_DIFFLIB_CUT_OFF = 0.6


Expand All @@ -40,32 +35,11 @@ def display_config(ctx, param, value):
ctx.exit() # NOTE: Must exit to bypass running ApeCLI


def _validate_config():
from ape.utils.basemodel import ManagerAccessMixin as access

project = access.local_project
try:
_ = project.config
except ConfigError as err:
rich.print(err)
# Exit now to avoid weird problems.
sys.exit(1)


class ApeCLI(click.MultiCommand):
_CLI_GROUP_NAME = "ape_cli_subcommands"

def parse_args(self, ctx: "Context", args: list[str]) -> list[str]:
# Validate the config before any argument parsing,
# as arguments may utilize config.
if "--help" not in args and args != []:
# perf: don't bother w/ config if only doing --help.
_validate_config()

return super().parse_args(ctx, args)

def format_commands(self, ctx, formatter) -> None:
from ape.utils.basemodel import ManagerAccessMixin as access
from ape.plugins._utils import PluginMetadataList

commands = []
for subcommand in self.list_commands(ctx):
Expand All @@ -86,10 +60,7 @@ def format_commands(self, ctx, formatter) -> None:
"Plugin": [],
"3rd-Party Plugin": [],
}
plugin_utils = import_module("ape.plugins._utils")
metadata_cls = plugin_utils.PluginMetadataList
plugin_manager = access.plugin_manager
pl_metadata = metadata_cls.load(plugin_manager, include_available=False)
pl_metadata = PluginMetadataList.from_package_names(f"ape_{c[0]}" for c in commands)
for cli_name, cmd in commands:
help = cmd.get_short_help_str(limit)
plugin = pl_metadata.get_plugin(cli_name, check_available=False)
Expand Down
100 changes: 76 additions & 24 deletions src/ape/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,79 @@
from .accounts import (
AccountAPI,
AccountContainerAPI,
ImpersonatedAccount,
TestAccountAPI,
TestAccountContainerAPI,
)
from .address import Address
from .compiler import CompilerAPI
from .config import ConfigDict, ConfigEnum, PluginConfig
from .convert import ConverterAPI
from .explorers import ExplorerAPI
from .networks import (
EcosystemAPI,
ForkedNetworkAPI,
NetworkAPI,
ProviderContextManager,
create_network_type,
)
from .projects import DependencyAPI, ProjectAPI
from .providers import BlockAPI, ProviderAPI, SubprocessProvider, TestProviderAPI, UpstreamProvider
from .query import QueryAPI, QueryType
from .trace import TraceAPI
from .transactions import ReceiptAPI, TransactionAPI
def __getattr__(name: str):
if name in (
"AccountAPI",
"AccountContainerAPI",
"ImpersonatedAccount",
"TestAccountAPI",
"TestAccountContainerAPI",
):
import ape.api.accounts as accounts_module

return getattr(accounts_module, name)

elif name in ("Address",):
import ape.api.address as address_module

return getattr(address_module, name)

elif name in ("CompilerAPI",):
import ape.api.compiler as compiler_module

return getattr(compiler_module, name)

elif name in ("ConfigDict", "ConfigEnum", "PluginConfig"):
import ape.api.config as config_module

return getattr(config_module, name)

elif name in ("ConverterAPI",):
import ape.api.convert as convert_module

return getattr(convert_module, name)

elif name in ("ExplorerAPI",):
import ape.api.explorers as explorer_module

return getattr(explorer_module, name)

elif name in ("BlockAPI, ProviderAPI, SubprocessProvider, TestProviderAPI, UpstreamProvider"):
import ape.api.providers as provider_module

return getattr(provider_module, name)

elif name in (
"EcosystemAPI",
"ForkedNetworkAPI",
"NetworkAPI",
"ProviderContextManager",
"create_network_type",
):
import ape.api.networks as network_module

return getattr(network_module, name)

elif name in ("DependencyAPI", "ProjectAPI"):
import ape.api.projects as project_module

return getattr(project_module, name)

elif name in ("QueryAPI", "QueryType"):
import ape.api.query as query_module

return getattr(query_module, name)

elif name in ("TraceAPI",):
import ape.api.trace as trace_module

return getattr(trace_module, name)

elif name in ("ReceiptAPI", "TransactionAPI"):
import ape.api.transactions as tx_module

return getattr(tx_module, name)

else:
raise AttributeError(name)


__all__ = [
"AccountAPI",
Expand Down
18 changes: 12 additions & 6 deletions src/ape/cli/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ def __init__(self, key: _ACCOUNT_TYPE_FILTER = None):

@cached_property
def choices(self) -> Sequence: # type: ignore[override]
module = import_module("ape.types.basic")
return module._LazySequence(self._choices_iterator)
from ape.types.basic import _LazySequence

return _LazySequence(self._choices_iterator)

@property
def _choices_iterator(self) -> Iterator[str]:
Expand Down Expand Up @@ -172,8 +173,9 @@ def select_account(
Returns:
:class:`~ape.api.accounts.AccountAPI`
"""
account_module = import_module("ape.api.accounts")
if key and isinstance(key, type) and not issubclass(key, account_module.AccountAPI):
from ape.api.accounts import AccountAPI

if key and isinstance(key, type) and not issubclass(key, AccountAPI):
raise AccountsError(f"Cannot return accounts with type '{key}'.")

prompt = AccountAliasPromptChoice(prompt_message=prompt_message, key=key)
Expand All @@ -196,8 +198,12 @@ def __init__(
self._key_filter = key
self._prompt_message = prompt_message or "Select an account"
self.name = name
module = import_module("ape.types.basic")
self.choices = module._LazySequence(self._choices_iterator)

@cached_property
def choices(self) -> Sequence[str]: # type: ignore[override]
from ape.types.basic import _LazySequence

return _LazySequence(self._choices_iterator)

def convert(
self, value: Any, param: Optional[Parameter], ctx: Optional[Context]
Expand Down
10 changes: 5 additions & 5 deletions src/ape/cli/commands.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import inspect
from importlib import import_module
from typing import TYPE_CHECKING, Any, Optional

import click
Expand All @@ -26,6 +25,7 @@ def get_param_from_ctx(ctx: "Context", param: str) -> Optional[Any]:


def parse_network(ctx: "Context") -> Optional["ProviderContextManager"]:
from ape.api.providers import ProviderAPI
from ape.utils.basemodel import ManagerAccessMixin as access

interactive = get_param_from_ctx(ctx, "interactive")
Expand All @@ -36,8 +36,7 @@ def parse_network(ctx: "Context") -> Optional["ProviderContextManager"]:
return provider.network.use_provider(provider, disconnect_on_exit=not interactive)

provider = get_param_from_ctx(ctx, "network")
provider_module = import_module("ape.api.providers")
if provider is not None and isinstance(provider, provider_module.ProviderAPI):
if provider is not None and isinstance(provider, ProviderAPI):
return provider.network.use_provider(provider, disconnect_on_exit=not interactive)

elif provider not in (None, _NONE_NETWORK) and isinstance(provider, str):
Expand Down Expand Up @@ -72,9 +71,10 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def parse_args(self, ctx: "Context", args: list[str]) -> list[str]:
from ape.api.providers import ProviderAPI

arguments = args # Renamed for better pdb support.
provider_module = import_module("ape.api.providers")
base_type = provider_module.ProviderAPI if self._use_cls_types else str
base_type = ProviderAPI if self._use_cls_types else str
if existing_option := next(
iter(
x
Expand Down
10 changes: 7 additions & 3 deletions src/ape/cli/options.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import inspect
import sys
from collections.abc import Callable
from functools import partial
from importlib import import_module
from pathlib import Path
from typing import TYPE_CHECKING, Any, NoReturn, Optional, Union

Expand Down Expand Up @@ -347,11 +347,10 @@ def _update_context_with_network(ctx, provider, requested_network_objects):


def _get_provider(value, default, keep_as_choice_str):
from ape.api.providers import ProviderAPI
from ape.utils.basemodel import ManagerAccessMixin

use_default = value is None and default == "auto"
provider_module = import_module("ape.api.providers")
ProviderAPI = provider_module.ProviderAPI

if not keep_as_choice_str and use_default:
default_ecosystem = ManagerAccessMixin.network_manager.default_ecosystem
Expand Down Expand Up @@ -530,6 +529,11 @@ def handle_parse_result(self, ctx, opts, args):


def _project_callback(ctx, param, val):
if "--help" in sys.argv or "-h" in sys.argv:
# Perf: project option is eager; have to check sys.argv to
# know to exit early when only doing --help.
return

from ape.utils.basemodel import ManagerAccessMixin

pm = None
Expand Down
Loading

0 comments on commit a3b94e4

Please sign in to comment.