Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: make CLI and init module load faster #63

Merged
merged 1 commit into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: v5.0.0
hooks:
- id: check-yaml

Expand All @@ -10,25 +10,25 @@ repos:
- id: isort

- repo: https://github.com/psf/black
rev: 24.4.2
rev: 24.10.0

hooks:
- id: black
name: black

- repo: https://github.com/pycqa/flake8
rev: 7.0.0
rev: 7.1.1
hooks:
- id: flake8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.0
rev: v1.13.0
hooks:
- id: mypy
additional_dependencies: [types-requests, types-setuptools, pydantic]

- repo: https://github.com/executablebooks/mdformat
rev: 0.7.17
rev: 0.7.18
hooks:
- id: mdformat
additional_dependencies: [mdformat-gfm, mdformat-frontmatter, mdformat-pyproject]
Expand Down
22 changes: 18 additions & 4 deletions ape_safe/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
from typing import Optional
from importlib import import_module
from typing import Any, Optional

from ape import plugins
from ape.api import PluginConfig

from .accounts import SafeAccount, SafeContainer
from .multisend import MultiSend


class SafeConfig(PluginConfig):
default_safe: Optional[str] = None
Expand All @@ -19,10 +17,26 @@ def config_class():

@plugins.register(plugins.AccountPlugin)
def account_types():
from .accounts import SafeAccount, SafeContainer

return SafeContainer, SafeAccount


def __getattr__(name: str) -> Any:
if name == "MultiSend":
from .multisend import MultiSend

return MultiSend

elif name in ("SafeAccount", "SafeContainer"):
return getattr(import_module("ape_safe.accounts"), name)

else:
raise AttributeError(name)


__all__ = [
"MultiSend",
"SafeAccount",
"SafeContainer",
]
9 changes: 6 additions & 3 deletions ape_safe/_cli/click_ext.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from collections.abc import Sequence
from typing import NoReturn, Optional, Union, cast
from typing import TYPE_CHECKING, NoReturn, Optional, Union, cast

import click
from ape.api import AccountAPI
Expand All @@ -8,15 +8,18 @@
from ape.utils import ManagerAccessMixin
from click import BadOptionUsage, MissingParameter

from ape_safe.accounts import SafeContainer
if TYPE_CHECKING:
from ape_safe.accounts import SafeContainer


class SafeCliContext(ApeCliContextObject):
@property
def safes(self) -> SafeContainer:
def safes(self) -> "SafeContainer":
# NOTE: Would only happen in local development of this plugin.
assert "safe" in self.account_manager.containers, "Are all API methods implemented?"

from ape_safe.accounts import SafeContainer

safe_container = self.account_manager.containers["safe"]
return cast(SafeContainer, safe_container)

Expand Down
28 changes: 20 additions & 8 deletions ape_safe/_cli/pending.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
from collections.abc import Sequence
from typing import Optional, Union, cast
from typing import TYPE_CHECKING, Optional, Union, cast

import click
import rich
from ape.api import AccountAPI
from ape.cli import ConnectedProviderCommand
from ape.exceptions import SignatureError
from ape.types import AddressType, MessageSignature
from eth_typing import ChecksumAddress, Hash32
from eth_utils import humanize_hash
from hexbytes import HexBytes

from ape_safe import SafeAccount
from ape_safe._cli.click_ext import (
SafeCliContext,
execute_option,
Expand All @@ -21,9 +19,12 @@
submitter_option,
txn_ids_argument,
)
from ape_safe.accounts import get_signatures
from ape_safe.client import UnexecutedTxData
from ape_safe.utils import get_safe_tx_hash

if TYPE_CHECKING:
from ape.api import AccountAPI

from ape_safe.accounts import SafeAccount
from ape_safe.client import UnexecutedTxData


@click.group()
Expand Down Expand Up @@ -119,6 +120,11 @@ def propose(cli_ctx, ecosystem, safe, data, gas_price, value, receiver, nonce, s
"""
Create a new transaction
"""
from ape.api import AccountAPI

from ape_safe.accounts import get_signatures
from ape_safe.utils import get_safe_tx_hash

nonce = safe.new_nonce if nonce is None else nonce
txn = ecosystem.create_transaction(
value=value,
Expand Down Expand Up @@ -178,6 +184,10 @@ def propose(cli_ctx, ecosystem, safe, data, gas_price, value, receiver, nonce, s
@txn_ids_argument
@execute_option
def approve(cli_ctx: SafeCliContext, safe, txn_ids, execute):
from ape.api import AccountAPI

from ape_safe.utils import get_safe_tx_hash

submitter: Optional[AccountAPI] = execute if isinstance(execute, AccountAPI) else None
pending_transactions = list(
safe.client.get_transactions(confirmed=False, starting_nonce=safe.next_nonce)
Expand Down Expand Up @@ -258,7 +268,7 @@ def execute(cli_ctx, safe, txn_ids, submitter, nonce):
cli_ctx.abort_txns_not_found(txn_ids)


def _execute(safe: SafeAccount, txn: UnexecutedTxData, submitter: AccountAPI, **tx_kwargs):
def _execute(safe: "SafeAccount", txn: "UnexecutedTxData", submitter: "AccountAPI", **tx_kwargs):
safe_tx = safe.create_safe_tx(**txn.model_dump(mode="json", by_alias=True))
signatures: dict[AddressType, MessageSignature] = {
c.owner: MessageSignature.from_rsv(c.signature) for c in txn.confirmations
Expand All @@ -276,6 +286,8 @@ def reject(cli_ctx: SafeCliContext, safe, txn_ids, execute):
"""
Reject one or more pending transactions
"""
from ape.api import AccountAPI

submit = False if execute in (False, None) else True
submitter = execute if isinstance(execute, AccountAPI) else None
if submitter is None and submit:
Expand Down Expand Up @@ -369,7 +381,7 @@ def _show_confs(confs, extra_line: bool = True, prefix: Optional[str] = None):

# Helper method for handling transactions in a loop.
def _filter_tx_from_ids(
txn_ids: Sequence[Union[int, str]], txn: UnexecutedTxData
txn_ids: Sequence[Union[int, str]], txn: "UnexecutedTxData"
) -> Sequence[Union[int, str]]:
if txn.nonce in txn_ids:
# Filter out all transactions with the same nonce
Expand Down
7 changes: 5 additions & 2 deletions ape_safe/_cli/safe_mgmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
skip_confirmation_option,
)
from ape.exceptions import ChainError, ProviderNotConnectedError
from ape.types import AddressType
from eth_typing import ChecksumAddress

from ape_safe._cli.click_ext import SafeCliContext, safe_argument, safe_cli_ctx
from ape_safe.client import ExecutedTxData


@click.command(name="list")
Expand Down Expand Up @@ -90,6 +88,7 @@ def add(cli_ctx: SafeCliContext, ecosystem, network, address, alias):
"""
Add a Safe to locally tracked Safes
"""
from ape.types import AddressType

address = cli_ctx.conversion_manager.convert(address, AddressType)
safe_contract = cli_ctx.chain_manager.contracts.instance_at(address)
Expand Down Expand Up @@ -138,6 +137,10 @@ def all_txns(cli_ctx: SafeCliContext, account, confirmed):
"""
View and filter all transactions for a given Safe using Safe API
"""
from ape.types import AddressType

from ape_safe.client import ExecutedTxData

if account in cli_ctx.account_manager.aliases:
account = cli_ctx.account_manager.load(account)

Expand Down
4 changes: 2 additions & 2 deletions ape_safe/client/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ class SignatureType(str, Enum):
class SafeTxConfirmation(BaseModel):
owner: AddressType
submission_date: datetime = Field(alias="submissionDate")
transaction_hash: Optional[HexBytes] = Field(None, alias="transactionHash")
transaction_hash: Optional[HexBytes] = Field(default=None, alias="transactionHash")
signature: HexBytes
signature_type: Optional[SignatureType] = Field(None, alias="signatureType")
signature_type: Optional[SignatureType] = Field(default=None, alias="signatureType")


class OperationType(int, Enum):
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ include = '\.pyi?$'
[tool.pytest.ini_options]
# NOTE: can't use xdist
addopts = """
-p no:pytest_ethereum
-n 0
--cov-branch
--cov-report term
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
"ape-solidity>=0.8", # Needed for compiling the Safe contracts
],
"lint": [
"black>=24.8.0,<25", # Auto-formatter and linter
"mypy>=1.11.1,<2", # Static type analyzer
"black>=24.10.0,<25", # Auto-formatter and linter
"mypy>=1.13.0,<2", # Static type analyzer
"types-requests", # Needed for mypy type shed
"types-setuptools", # Needed for mypy type shed
"flake8>=7.1.1,<8", # Style linter
"isort>=5.13.2,<6", # Import sorting linter
"mdformat>=0.7.17,<0.8", # Docs formatter and linter
"mdformat>=0.7.18,<0.8", # Docs formatter and linter
"mdformat-pyproject>=0.0.1", # Allows configuring in pyproject.toml
],
"release": [ # `release` GitHub Action job uses this
Expand Down
Loading