Skip to content

Commit

Permalink
refactor: lazily initialize managers in ManagerAccessMixin (#2336)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Oct 23, 2024
1 parent 2ef3892 commit 8a689b2
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 41 deletions.
26 changes: 0 additions & 26 deletions src/ape/managers/__init__.py
Original file line number Diff line number Diff line change
@@ -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
120 changes: 105 additions & 15 deletions src/ape/utils/basemodel.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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":
Expand Down

0 comments on commit 8a689b2

Please sign in to comment.