diff --git a/src/ape/__init__.py b/src/ape/__init__.py index 7fb72453ba..efb1345297 100644 --- a/src/ape/__init__.py +++ b/src/ape/__init__.py @@ -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", @@ -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 diff --git a/src/ape/_cli.py b/src/ape/_cli.py index 8afe38318b..545fed61c2 100644 --- a/src/ape/_cli.py +++ b/src/ape/_cli.py @@ -4,7 +4,6 @@ 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 @@ -65,6 +64,7 @@ def parse_args(self, ctx: "Context", args: list[str]) -> list[str]: return super().parse_args(ctx, args) def format_commands(self, ctx, formatter) -> None: + from ape.plugins._utils import PluginMetadataList from ape.utils.basemodel import ManagerAccessMixin as access commands = [] @@ -86,10 +86,8 @@ 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.load(plugin_manager, include_available=False) for cli_name, cmd in commands: help = cmd.get_short_help_str(limit) plugin = pl_metadata.get_plugin(cli_name, check_available=False) diff --git a/src/ape/cli/choices.py b/src/ape/cli/choices.py index e7dc752107..d880e262bc 100644 --- a/src/ape/cli/choices.py +++ b/src/ape/cli/choices.py @@ -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]: @@ -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) @@ -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] diff --git a/src/ape/cli/commands.py b/src/ape/cli/commands.py index 63a8a2f246..ea6110d42c 100644 --- a/src/ape/cli/commands.py +++ b/src/ape/cli/commands.py @@ -1,5 +1,4 @@ import inspect -from importlib import import_module from typing import TYPE_CHECKING, Any, Optional import click @@ -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") @@ -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): @@ -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 diff --git a/src/ape/cli/options.py b/src/ape/cli/options.py index 508051e13c..b2d7829440 100644 --- a/src/ape/cli/options.py +++ b/src/ape/cli/options.py @@ -1,7 +1,6 @@ import inspect 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 @@ -347,11 +346,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 diff --git a/src/ape/exceptions.py b/src/ape/exceptions.py index db50583303..14e4aed00a 100644 --- a/src/ape/exceptions.py +++ b/src/ape/exceptions.py @@ -5,7 +5,6 @@ import traceback from collections.abc import Collection, Iterable from functools import cached_property -from importlib import import_module from inspect import getframeinfo, stack from pathlib import Path from types import CodeType, TracebackType @@ -922,7 +921,8 @@ def _get_custom_python_traceback( # https://github.com/pallets/jinja/blob/main/src/jinja2/debug.py#L142 if project is None: - access = import_module("ape.utils.basemodel").ManagerAccessMixin + from ape.utils.basemodel import ManagerAccessMixin as access + project = access.local_project if not (base_path := getattr(project, "path", None)): diff --git a/src/ape/managers/__init__.py b/src/ape/managers/__init__.py index 653548550f..8378c05871 100644 --- a/src/ape/managers/__init__.py +++ b/src/ape/managers/__init__.py @@ -1,43 +1,48 @@ -from importlib import import_module -from typing import Any - - -def __getattr__(name: str) -> Any: +def __getattr__(name: str): if name == "AccountManager": - module = import_module("ape.managers.accounts") - return module.AccountManager + from ape.managers.accounts import AccountManager + + return AccountManager elif name == "ChainManager": - module = import_module("ape.managers.chain") - return module.ChainManager + from ape.managers.chain import ChainManager + + return ChainManager elif name == "CompilerManager": - module = import_module("ape.managers.compilers") - return module.CompilerManager + from ape.managers.compilers import CompilerManager + + return CompilerManager elif name == "ConfigManager": - module = import_module("ape.managers.config") - return module.ConfigManager + from ape.managers.config import ConfigManager + + return ConfigManager elif name == "ConversionManager": - module = import_module("ape.managers.converters") - return module.ConversionManager + from ape.managers.converters import ConversionManager + + return ConversionManager elif name == "NetworkManager": - module = import_module("ape.managers.networks") - return module.NetworkManager + from ape.managers.networks import NetworkManager + + return NetworkManager elif name == "PluginManager": - module = import_module("ape.managers.plugins") - return module.PluginManager + from ape.managers.plugins import PluginManager + + return PluginManager elif name == "ProjectManager": - module = import_module("ape.managers.project") - return module.ProjectManager + from ape.managers.project import ProjectManager + + return ProjectManager elif name == "QueryManager": - module = import_module("ape.managers.query") - return module.QueryManager + from ape.managers.query import QueryManager + + return QueryManager else: raise AttributeError(name) diff --git a/src/ape/types/__init__.py b/src/ape/types/__init__.py index 3893f7bcb5..0b4cc07792 100644 --- a/src/ape/types/__init__.py +++ b/src/ape/types/__init__.py @@ -26,12 +26,26 @@ from ape.types.trace import ContractFunctionPath, ControlFlow, GasReport, SourceTraceback from ape.types.units import CurrencyValue, CurrencyValueComparable from ape.types.vm import BlockID, ContractCode, SnapshotID +from ape.utils.basemodel import ( + BaseInterface, + BaseInterfaceModel, + BaseModel, + ExtraAttributesMixin, + ExtraModelAttributes, + ManagerAccessMixin, + get_attribute_with_extras, + get_item_with_extras, + only_raise_attribute_error, +) __all__ = [ "_LazySequence", "ABI", "AddressType", "AutoGasLimit", + "BaseInterface", + "BaseInterfaceModel", + "BaseModel", "BlockID", "Bytecode", "Checksum", @@ -50,13 +64,19 @@ "CoverageStatement", "CurrencyValue", "CurrencyValueComparable", + "ExtraAttributesMixin", + "ExtraModelAttributes", "GasLimit", "GasReport", + "get_attribute_with_extras", + "get_item_with_extras", "HexInt", "HexBytes", "LogFilter", + "ManagerAccessMixin", "MessageSignature", "MockContractLog", + "only_raise_attribute_error", "PackageManifest", "PackageMeta", "RawAddress", diff --git a/src/ape/types/basic.py b/src/ape/types/basic.py index 56899b1209..b5e54c815a 100644 --- a/src/ape/types/basic.py +++ b/src/ape/types/basic.py @@ -1,5 +1,4 @@ from collections.abc import Callable, Iterator, Sequence -from importlib import import_module from typing import Annotated, TypeVar, Union, overload from pydantic import BeforeValidator @@ -11,7 +10,7 @@ def _hex_int_validator(value, info): return value # NOTE: Allows this module to load lazier. - access = import_module("ape.utils.basemodel").ManagerAccessMixin + from ape.utils.basemodel import ManagerAccessMixin as access convert = access.conversion_manager.convert return convert(value, int) diff --git a/src/ape/utils/basemodel.py b/src/ape/utils/basemodel.py index 03f1bf4c58..cabac370df 100644 --- a/src/ape/utils/basemodel.py +++ b/src/ape/utils/basemodel.py @@ -1,3 +1,7 @@ +""" +TODO: In 0.9, move this module to `ape.types`. +""" + import inspect from abc import ABC from collections.abc import Callable, Iterator, Sequence diff --git a/src/ape_accounts/__init__.py b/src/ape_accounts/__init__.py index b3af3607f3..2c9148cf2a 100644 --- a/src/ape_accounts/__init__.py +++ b/src/ape_accounts/__init__.py @@ -1,5 +1,4 @@ from importlib import import_module -from typing import Any from ape.plugins import AccountPlugin, register @@ -11,7 +10,7 @@ def account_types(): return AccountContainer, KeyfileAccount -def __getattr__(name: str) -> Any: +def __getattr__(name: str): return getattr(import_module("ape_accounts.accounts"), name) diff --git a/src/ape_cache/_cli.py b/src/ape_cache/_cli.py index 89c949375b..07f0fe918f 100644 --- a/src/ape_cache/_cli.py +++ b/src/ape_cache/_cli.py @@ -12,8 +12,9 @@ def get_engine() -> "CacheQueryProvider": - basemodel = import_module("ape.utils.basemodel") - return basemodel.ManagerAccessMixin.query_manager.engines["cache"] + from ape.utils.basemodel import ManagerAccessMixin + + return ManagerAccessMixin.query_manager.engines["cache"] @click.group(short_help="Query from caching database") diff --git a/src/ape_networks/__init__.py b/src/ape_networks/__init__.py index 51e382e1f8..d5958b94e5 100644 --- a/src/ape_networks/__init__.py +++ b/src/ape_networks/__init__.py @@ -1,5 +1,4 @@ from importlib import import_module -from typing import Any from ape.plugins import Config, register @@ -11,7 +10,7 @@ def config_class(): return NetworksConfig -def __getattr__(name: str) -> Any: +def __getattr__(name: str): if name in ("NetworksConfig", "CustomNetwork"): return getattr(import_module("ape_networks.config"), name) diff --git a/src/ape_networks/_cli.py b/src/ape_networks/_cli.py index 41ff326e42..a8b2095f46 100644 --- a/src/ape_networks/_cli.py +++ b/src/ape_networks/_cli.py @@ -1,6 +1,5 @@ import json from collections.abc import Callable, Sequence -from importlib import import_module from typing import TYPE_CHECKING import click @@ -118,10 +117,11 @@ def run(cli_ctx, provider): Start a subprocess node as if running independently and stream stdout and stderr. """ + from ape.api.providers import SubprocessProvider + # Ignore extra loggers, such as web3 loggers. cli_ctx.logger._extra_loggers = {} - providers_module = import_module("ape.api.providers") - if not isinstance(provider, providers_module.SubprocessProvider): + if not isinstance(provider, SubprocessProvider): cli_ctx.abort( f"`ape networks run` requires a provider that manages a process, not '{provider.name}'." )