From 8a689b29b98316f7c1d186dcb7664a08234e1dec Mon Sep 17 00:00:00 2001 From: antazoey Date: Wed, 23 Oct 2024 14:29:09 -0500 Subject: [PATCH] refactor: lazily initialize managers in ManagerAccessMixin (#2336) --- src/ape/managers/__init__.py | 26 -------- src/ape/utils/basemodel.py | 120 ++++++++++++++++++++++++++++++----- 2 files changed, 105 insertions(+), 41 deletions(-) diff --git a/src/ape/managers/__init__.py b/src/ape/managers/__init__.py index 98ead4e335..e69de29bb2 100644 --- a/src/ape/managers/__init__.py +++ b/src/ape/managers/__init__.py @@ -1,26 +0,0 @@ -from pathlib import Path - -from ape.utils import USER_AGENT, ManagerAccessMixin - -from .accounts import AccountManager -from .chain import ChainManager -from .compilers import CompilerManager -from .config import ConfigManager -from .converters import ConversionManager -from .networks import NetworkManager -from .plugins import PluginManager -from .project import ProjectManager -from .query import QueryManager - -ManagerAccessMixin.plugin_manager = PluginManager() -ManagerAccessMixin.config_manager = ConfigManager( - request_header={"User-Agent": USER_AGENT, "Content-Type": "application/json"}, -) -ManagerAccessMixin.compiler_manager = CompilerManager() -ManagerAccessMixin.network_manager = NetworkManager() -ManagerAccessMixin.query_manager = QueryManager() -ManagerAccessMixin.conversion_manager = ConversionManager() -ManagerAccessMixin.chain_manager = ChainManager() -ManagerAccessMixin.account_manager = AccountManager() -ManagerAccessMixin.local_project = ProjectManager(Path.cwd()) -ManagerAccessMixin.Project = ProjectManager diff --git a/src/ape/utils/basemodel.py b/src/ape/utils/basemodel.py index cc7d9f29d9..4dc3800fe3 100644 --- a/src/ape/utils/basemodel.py +++ b/src/ape/utils/basemodel.py @@ -1,8 +1,10 @@ import inspect from abc import ABC from collections.abc import Callable, Iterator, Sequence +from importlib import import_module +from pathlib import Path from sys import getrecursionlimit -from typing import TYPE_CHECKING, Any, ClassVar, Optional, Union, cast +from typing import TYPE_CHECKING, Any, ClassVar, Optional, Union from ethpm_types import BaseModel as EthpmTypesBaseModel from pydantic import BaseModel as RootBaseModel @@ -11,6 +13,7 @@ from ape.exceptions import ApeAttributeError, ApeIndexError, ProviderNotConnectedError from ape.logging import logger from ape.utils.misc import log_instead_of_fail, raises_not_implemented +from ape.utils.rpc import USER_AGENT if TYPE_CHECKING: from pydantic.main import Model @@ -36,6 +39,19 @@ def __get__(self, obj, owner): return self.fn(owner) +class manager_access: + _cache = None + + def __init__(self, fn): + self.fn = fn + + def __get__(self, obj, owner): + if self._cache is None: + self._cache = self.fn(owner) + + return self._cache + + class _RecursionChecker: # A helper for preventing the recursion errors # that happen in custom __getattr__ methods. @@ -66,6 +82,7 @@ def reset(self, name: str): _recursion_checker = _RecursionChecker() +# TODO: Delete in 0.9 (deprecated & no longer used anywhere) class injected_before_use(property): """ Injected properties are injected class variables that must be set before use. @@ -108,30 +125,103 @@ def wrapper(*args, **kwargs): class ManagerAccessMixin: - # NOTE: cast is used to update the class type returned to mypy - account_manager: ClassVar["AccountManager"] = cast("AccountManager", injected_before_use()) + """ + A mixin for accessing Ape's manager at the class level. - chain_manager: ClassVar["ChainManager"] = cast("ChainManager", injected_before_use()) + Usage example: - compiler_manager: ClassVar["CompilerManager"] = cast("CompilerManager", injected_before_use()) + from ape.utils import ManagerAccessMixin - config_manager: ClassVar["ConfigManager"] = cast("ConfigManager", injected_before_use()) + class MyClass(ManagerAccessMixin): + def my_function(self): + accounts = self.account_manager # And so on! + """ - conversion_manager: ClassVar["ConversionManager"] = cast( - "ConversionManager", injected_before_use() - ) + _test_runner: ClassVar[Optional["PytestApeRunner"]] = None - local_project: ClassVar["ProjectManager"] = cast("ProjectManager", injected_before_use()) + @manager_access + def account_manager(cls) -> "AccountManager": + """ + The :class:`~ape.managers.accounts.AccountManager`. + """ + accounts = import_module("ape.managers.accounts") + return accounts.AccountManager() - network_manager: ClassVar["NetworkManager"] = cast("NetworkManager", injected_before_use()) + @manager_access + def chain_manager(cls) -> "ChainManager": + """ + The :class:`~ape.managers.chain.ChainManager`. + """ + chain = import_module("ape.managers.chain") + return chain.ChainManager() - plugin_manager: ClassVar["PluginManager"] = cast("PluginManager", injected_before_use()) + @manager_access + def compiler_manager(cls) -> "CompilerManager": + """ + The :class:`~ape.managers.compilers.CompilerManager`. + """ + compilers = import_module("ape.managers.compilers") + return compilers.CompilerManager() - Project: ClassVar[type["ProjectManager"]] = cast(type["ProjectManager"], injected_before_use()) + @manager_access + def config_manager(cls) -> "ConfigManager": + """ + The :class:`~ape.managers.config.ConfigManager`. + """ + config = import_module("ape.managers.config") + return config.ConfigManager( + request_header={"User-Agent": USER_AGENT, "Content-Type": "application/json"}, + ) - query_manager: ClassVar["QueryManager"] = cast("QueryManager", injected_before_use()) + @manager_access + def conversion_manager(cls) -> "ConversionManager": + """ + The :class:`~ape.managers.converters.ConversionManager`. + """ + converters = import_module("ape.managers.converters") + return converters.ConversionManager() - _test_runner: ClassVar[Optional["PytestApeRunner"]] = None + @manager_access + def local_project(cls) -> "ProjectManager": + """ + A :class:`~ape.managers.project.ProjectManager` pointed + at the current-working directory. + """ + project = import_module("ape.managers.project") + return project.ProjectManager(Path.cwd()) + + @manager_access + def network_manager(cls) -> "NetworkManager": + """ + The :class:`~ape.managers.networks.NetworkManager`. + """ + networks = import_module("ape.managers.networks") + return networks.NetworkManager() + + @manager_access + def plugin_manager(cls) -> "PluginManager": + """ + The :class:`~ape.managers.plugins.PluginManager`. + """ + plugins = import_module("ape.managers.plugins") + return plugins.PluginManager() + + @manager_access + def Project(cls) -> type["ProjectManager"]: + """ + The ``Project`` factory class for creating + other local-projects. + """ + project = import_module("ape.managers.project") + return project.ProjectManager + + @manager_access + def query_manager(cls) -> "QueryManager": + """ + The :class:`~ape.managers.query.QueryManager`. + """ + query = import_module("ape.managers.query") + return query.QueryManager() @classproperty def provider(cls) -> "ProviderAPI":