Skip to content

Commit

Permalink
feat: ape test init
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey committed Oct 18, 2021
1 parent 85c7868 commit 449d9f4
Show file tree
Hide file tree
Showing 43 changed files with 745 additions and 159 deletions.
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ target-version = ['py37', 'py38']
include = '\.pyi?$'

[tool.pytest.ini_options]
norecursedirs = "data"
addopts = "-p no:ape_test" # NOTE: Prevents the ape plugin from activating on our tests
python_files = "test_*.py"
testpaths = "tests"
markers = "fuzzing: Run Hypothesis fuzz test suite"
Expand All @@ -22,7 +24,7 @@ markers = "fuzzing: Run Hypothesis fuzz test suite"
line_length = 100
force_grid_wrap = 0
include_trailing_comma = true
known_third_party = ["IPython", "ape_ethereum", "ape_plugins", "click", "dataclassy", "eth_abi", "eth_account", "eth_typing", "eth_utils", "github", "hexbytes", "hypothesis", "hypothesis_jsonschema", "importlib_metadata", "pluggy", "pytest", "requests", "setuptools", "web3", "yaml"]
known_third_party = ["IPython", "_pytest", "ape_ethereum", "ape_http", "ape_plugins", "ape_test", "click", "dataclassy", "eth_abi", "eth_account", "eth_typing", "eth_utils", "github", "hexbytes", "hypothesis", "hypothesis_jsonschema", "importlib_metadata", "pluggy", "pytest", "requests", "setuptools", "web3", "yaml"]
known_first_party = ["ape_accounts", "ape_console", "ape"]
multi_line_output = 3
use_parentheses = true
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

extras_require = {
"test": [ # `test` GitHub Action jobs uses this
"pytest>=6.0,<7.0", # Core testing package
"pytest-xdist", # multi-process runner
"pytest-cov", # Coverage analyzer plugin
"pytest-mock", # For creating mocks
Expand Down Expand Up @@ -75,17 +74,20 @@
"importlib-metadata",
"singledispatchmethod ; python_version<'3.8'",
"IPython>=7.25",
"pytest>=6.0,<7.0",
"web3[tester]>=5.18.0,<6.0.0",
],
entry_points={
"console_scripts": ["ape=ape._cli:cli"],
"pytest11": ["ape_test=ape_test.plugin"],
"ape_cli_subcommands": [
"ape_accounts=ape_accounts._cli:cli",
"ape_compile=ape_compile._cli:cli",
"ape_console=ape_console._cli:cli",
"ape_plugins=ape_plugins._cli:cli",
"ape_run=ape_run._cli:cli",
"ape_networks=ape_networks._cli:cli",
"ape_test=ape_test._cli:cli",
],
},
python_requires=">=3.7,<3.10",
Expand Down
2 changes: 1 addition & 1 deletion src/ape/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def invoke(self, ctx) -> Any:
except click.UsageError as err:
self._suggest_cmd(err)
except ApeException as err:
raise Abort(str(err)) from err
raise Abort(f"({type(err).__name__}) {err}") from err

@staticmethod
def _suggest_cmd(usage_error):
Expand Down
2 changes: 1 addition & 1 deletion src/ape/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .accounts import AccountAPI, AccountContainerAPI
from .address import Address, AddressAPI
from .contracts import ContractLog
from .contracts import ContractInstance, ContractLog
from .convert import ConverterAPI
from .explorers import ExplorerAPI
from .networks import EcosystemAPI, NetworkAPI, ProviderContextManager, create_network_type
Expand Down
38 changes: 15 additions & 23 deletions src/ape/api/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
)
from ape.utils import cached_property

from ..exceptions import AccountsError, AliasAlreadyInUseError, TransactionError
from ..exceptions import AccountsError, AliasAlreadyInUseError, SignatureError
from .address import AddressAPI
from .base import abstractdataclass, abstractmethod
from .contracts import ContractContainer, ContractInstance
Expand Down Expand Up @@ -55,7 +55,7 @@ def call(self, txn: TransactionAPI, send_everything: bool = False) -> ReceiptAPI
if txn.nonce is None:
txn.nonce = self.nonce
elif txn.nonce < self.nonce:
raise AccountsError("Invalid nonce, will not publish")
raise AccountsError("Invalid nonce, will not publish.")

# TODO: Add `GasEstimationAPI`
if txn.gas_price is None:
Expand All @@ -64,19 +64,21 @@ def call(self, txn: TransactionAPI, send_everything: bool = False) -> ReceiptAPI

# NOTE: Allow overriding gas limit
if txn.gas_limit is None:
txn.gas_limit = self._estimate_gas(txn)
txn.gas_limit = self.provider.estimate_gas_cost(txn)
# else: assume user specified the correct amount or txn will fail and waste gas

if send_everything:
txn.value = self.balance - txn.gas_limit * txn.gas_price

if txn.gas_limit * txn.gas_price + txn.value > self.balance:
raise AccountsError("Transfer value meets or exceeds account balance")
if txn.total_transfer_value > self.balance:
raise AccountsError(
"Transfer value meets or exceeds account balance. "
f"(transfer_value={txn.total_transfer_value}, balance={self.balance})."
)

txn.signature = self.sign_transaction(txn)

if not txn.signature:
raise AccountsError("The transaction was not signed")
raise SignatureError("The transaction was not signed.")

return self.provider.send_transaction(txn)

Expand All @@ -87,16 +89,6 @@ def _convert(self) -> Callable:

return convert

def _estimate_gas(self, txn: TransactionAPI) -> int:
try:
return self.provider.estimate_gas_cost(txn)
except ValueError as err:
message = (
f"Gas estimation failed: '{err}'. This transaction will likely revert. "
"If you wish to broadcast, you must set the gas limit manually."
)
raise TransactionError(message) from err

def transfer(
self,
account: Union[str, AddressType, "AddressAPI"],
Expand Down Expand Up @@ -129,7 +121,7 @@ def deploy(self, contract_type: ContractType, *args, **kwargs) -> ContractInstan
receipt = self.call(txn)

if not receipt.contract_address:
raise AccountsError(f"'{receipt.txn_hash}' did not create a contract")
raise AccountsError(f"'{receipt.txn_hash}' did not create a contract.")

return ContractInstance( # type: ignore
_provider=self.provider,
Expand Down Expand Up @@ -167,25 +159,25 @@ def append(self, account: AccountAPI):
self._verify_account_type(account)

if account.address in self:
raise AccountsError(f"Account '{account.address}' already in container")
raise AccountsError(f"Account '{account.address}' already in container.")

self._verify_unused_alias(account)

self.__setitem__(account.address, account)

def __setitem__(self, address: AddressType, account: AccountAPI):
raise NotImplementedError("Must define this method to use `container.append(acct)`")
raise NotImplementedError("Must define this method to use `container.append(acct)`.")

def remove(self, account: AccountAPI):
self._verify_account_type(account)

if account.address not in self:
raise AccountsError(f"Account '{account.address}' not known")
raise AccountsError(f"Account '{account.address}' not known.")

self.__delitem__(account.address)

def __delitem__(self, address: AddressType):
raise NotImplementedError("Must define this method to use `container.remove(acct)`")
raise NotImplementedError("Must define this method to use `container.remove(acct)`.")

def __contains__(self, address: AddressType) -> bool:
try:
Expand All @@ -199,7 +191,7 @@ def _verify_account_type(self, account):
if not isinstance(account, self.account_type):
message = (
f"Container '{type(account).__name__}' is an incorrect "
f"type for container '{type(self.account_type).__name__}'"
f"type for container '{type(self.account_type).__name__}'."
)
raise AccountsError(message)

Expand Down
12 changes: 6 additions & 6 deletions src/ape/api/contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ape.logging import logger
from ape.types import ABI, AddressType, ContractType

from ..exceptions import ContractCallError, ContractDeployError
from ..exceptions import ArgumentsLengthError, ContractDeployError, TransactionError
from .address import Address, AddressAPI
from .base import dataclass
from .providers import ProviderAPI, ReceiptAPI, TransactionAPI
Expand All @@ -23,7 +23,7 @@ class ContractConstructor:

def __post_init__(self):
if len(self.deployment_bytecode) == 0:
raise ContractDeployError("No bytecode to deploy")
raise ContractDeployError("No bytecode to deploy.")

def __repr__(self) -> str:
return self.abi.signature if self.abi else "constructor()"
Expand Down Expand Up @@ -95,7 +95,7 @@ def __repr__(self) -> str:
def __call__(self, *args, **kwargs) -> Any:
selected_abi = _select_abi(self.abis, args)
if not selected_abi:
raise ContractCallError()
raise ArgumentsLengthError()

return ContractCall( # type: ignore
abi=selected_abi,
Expand Down Expand Up @@ -137,7 +137,7 @@ def __call__(self, *args, **kwargs) -> ReceiptAPI:
return sender.call(txn)

else:
raise ContractCallError("Must specify a `sender`")
raise TransactionError(message="Must specify a `sender`.")


@dataclass
Expand All @@ -153,7 +153,7 @@ def __repr__(self) -> str:
def __call__(self, *args, **kwargs) -> ReceiptAPI:
selected_abi = _select_abi(self.abis, args)
if not selected_abi:
raise ContractCallError()
raise ArgumentsLengthError()

return ContractTransaction( # type: ignore
abi=selected_abi,
Expand Down Expand Up @@ -219,7 +219,7 @@ def get_handler(abi_type: str) -> Any:

except Exception as e:
# NOTE: Just a hack, because `__getattr__` *must* raise `AttributeError`
raise AttributeError from e
raise AttributeError(str(e)) from e

# Reverse search for the proper handler for this ABI name, if one exists
for abi_type in handlers:
Expand Down
2 changes: 1 addition & 1 deletion src/ape/api/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def __exit__(self, *args, **kwargs):
# Put our providers back the way it was
provider = self._connected_providers.pop()
if self.provider != provider:
raise ValueError("Previous provider value unknown")
raise ValueError("Previous provider value unknown.")

provider.disconnect()

Expand Down
16 changes: 13 additions & 3 deletions src/ape/api/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,17 @@ class TransactionAPI:

def __post_init__(self):
if not self.is_valid:
raise ProviderError("Transaction is not valid")
raise ProviderError("Transaction is not valid.")

@property
def total_transfer_value(self) -> int:
"""
The total amount of WEI that a transaction could use.
Useful for determining if an account balance can afford
to submit the transaction.
"""
# TODO Support EIP-1559
return (self.gas_limit or 0) * (self.gas_price or 0) + self.value

@property
@abstractmethod
Expand Down Expand Up @@ -63,8 +73,8 @@ def __str__(self) -> str:


class TransactionStatusEnum(IntEnum):
failing = 0
no_error = 1
FAILING = 0
NO_ERROR = 1


@abstractdataclass
Expand Down
2 changes: 1 addition & 1 deletion src/ape/cli/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def convert(
choice = self.choices[self.choice_index]
return choice
except Exception:
self.fail("Invalid choice", param=param)
return self.fail("Invalid choice", param=param)


class NetworkChoice(click.Choice):
Expand Down
2 changes: 1 addition & 1 deletion src/ape/cli/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def decorator(f):
def _set_level(ctx, param, value):
log_level = getattr(LogLevel, value.upper(), None)
if log_level is None:
raise click.BadParameter(f"Must be one of {names_str}, not {value}")
raise click.BadParameter(f"Must be one of {names_str}, not {value}.")

cli_logger.set_level(log_level.name)

Expand Down
Loading

0 comments on commit 449d9f4

Please sign in to comment.