Skip to content

Commit

Permalink
Merge branch 'main' into include-compilers-in-manifest
Browse files Browse the repository at this point in the history
  • Loading branch information
z80dev authored Nov 24, 2023
2 parents a433bc7 + caab108 commit 6d08f0e
Show file tree
Hide file tree
Showing 22 changed files with 191 additions and 44 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repos:
- id: isort

- repo: https://github.com/psf/black
rev: 23.10.1
rev: 23.11.0
hooks:
- id: black
name: black
Expand All @@ -21,7 +21,7 @@ repos:
- id: flake8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.6.1
rev: v1.7.0
hooks:
- id: mypy
additional_dependencies: [
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
"hypothesis-jsonschema==0.19.0", # JSON Schema fuzzer extension
],
"lint": [
"black>=23.10.1,<24", # Auto-formatter and linter
"mypy>=1.6.1,<2", # Static type analyzer
"black>=23.11.0,<24", # Auto-formatter and linter
"mypy>=1.7.0,<2", # Static type analyzer
"types-PyYAML", # Needed due to mypy typeshed
"types-requests", # Needed due to mypy typeshed
"types-setuptools", # Needed due to mypy typeshed
Expand Down Expand Up @@ -124,7 +124,7 @@
"web3[tester]>=6.7.0,<7",
# ** Dependencies maintained by ApeWorX **
"eip712>=0.2.1,<0.3",
"ethpm-types>=0.5.8,<0.6",
"ethpm-types>=0.5.10,<0.6",
"evm-trace>=0.1.0a23",
],
entry_points={
Expand Down
33 changes: 30 additions & 3 deletions src/ape/api/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import (
TYPE_CHECKING,
Any,
ClassVar,
Collection,
Dict,
Iterator,
Expand Down Expand Up @@ -577,19 +578,45 @@ class ProviderContextManager(ManagerAccessMixin):
provider_stack: List[str] = []
disconnect_map: Dict[str, bool] = {}

def __init__(self, provider: "ProviderAPI", disconnect_after: bool = False):
# We store a provider object at the class level for use when disconnecting
# due to an exception, when interactive mode is set. If we don't hold on
# to a reference to this object, the provider is dropped and reconnecting results
# in losing state when using a spawned local provider
_recycled_provider: ClassVar[Optional["ProviderAPI"]] = None

def __init__(
self,
provider: "ProviderAPI",
disconnect_after: bool = False,
disconnect_on_exit: bool = True,
):
self._provider = provider
self._disconnect_after = disconnect_after
self._disconnect_on_exit = disconnect_on_exit
self._skipped_disconnect = False

@property
def empty(self) -> bool:
return not self.connected_providers or not self.provider_stack

def __enter__(self, *args, **kwargs):
# If we have a recycled provider available, this means our last exit
# was due to an exception during interactive mode. We should resume that
# same connection, but also clear the object so we don't do this again
# in later provider contexts, which we would want to behave normally
if self._recycled_provider is not None:
# set inner var to the recycled provider for use in push_provider()
self._provider = self._recycled_provider
ProviderContextManager._recycled_provider = None
return self.push_provider()

def __exit__(self, *args, **kwargs):
self.pop_provider()
def __exit__(self, exception, *args, **kwargs):
if not self._disconnect_on_exit and exception is not None:
# We want to skip disconnection when exiting due to an exception in interactive mode
if provider := self.network_manager.active_provider:
ProviderContextManager._recycled_provider = provider
else:
self.pop_provider()

def push_provider(self):
must_connect = not self._provider.is_connected
Expand Down
2 changes: 1 addition & 1 deletion src/ape/api/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ def _create_manifest(
compiler_data: Optional[List[Compiler]] = None,
) -> PackageManifest:
manifest = initial_manifest or PackageManifest()
manifest.name = PackageName(name.lower()) if name is not None else manifest.name
manifest.name = PackageName(__root__=name.lower()) if name is not None else manifest.name
manifest.version = version or manifest.version
manifest.sources = cls._create_source_dict(source_paths, contracts_path)
manifest.contract_types = contract_types
Expand Down
7 changes: 4 additions & 3 deletions src/ape/api/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1546,14 +1546,15 @@ def _create_call_tree_node(
# Use raw value since it is not a real address.
contract_id = address.hex()

call_type = evm_call.call_type.value
call_type_str: str = getattr(evm_call.call_type, "value", f"{evm_call.call_type}")
input_data = evm_call.calldata if "CREATE" in call_type_str else evm_call.calldata[4:].hex()
return CallTreeNode(
calls=[self._create_call_tree_node(x, txn_hash=txn_hash) for x in evm_call.calls],
call_type=call_type,
call_type=call_type_str,
contract_id=contract_id,
failed=evm_call.failed,
gas_cost=evm_call.gas_cost,
inputs=evm_call.calldata if "CREATE" in call_type else evm_call.calldata[4:].hex(),
inputs=input_data,
method_id=evm_call.calldata[:4].hex(),
outputs=evm_call.returndata.hex(),
raw=evm_call.dict(),
Expand Down
13 changes: 12 additions & 1 deletion src/ape/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@
from ape import networks


def check_parents_for_interactive(ctx: Context) -> bool:
interactive: bool = ctx.params.get("interactive", False)
if interactive:
return True
# If not found, check the parent context.
if interactive is None and ctx.parent:
return check_parents_for_interactive(ctx.parent)
return False


class NetworkBoundCommand(click.Command):
"""
A command that uses the :meth:`~ape.cli.options.network_option`.
Expand All @@ -14,5 +24,6 @@ class NetworkBoundCommand(click.Command):

def invoke(self, ctx: Context) -> Any:
value = ctx.params.get("network") or networks.default_ecosystem.name
with networks.parse_network_choice(value):
interactive = check_parents_for_interactive(ctx)
with networks.parse_network_choice(value, disconnect_on_exit=not interactive):
super().invoke(ctx)
5 changes: 3 additions & 2 deletions src/ape/managers/chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,10 @@ def poll_blocks(
# Get number of last block with the necessary amount of confirmations.
block = None

if start_block is not None:
head_minus_confirms = self.height - required_confirmations
if start_block is not None and start_block <= head_minus_confirms:
# Front-load historical blocks.
for block in self.range(start_block, self.height - required_confirmations + 1):
for block in self.range(start_block, head_minus_confirms + 1):
yield block

if block:
Expand Down
7 changes: 6 additions & 1 deletion src/ape/managers/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ def parse_network_choice(
network_choice: Optional[str] = None,
provider_settings: Optional[Dict] = None,
disconnect_after: bool = False,
disconnect_on_exit: bool = True,
) -> ProviderContextManager:
"""
Parse a network choice into a context manager for managing a temporary
Expand Down Expand Up @@ -445,7 +446,11 @@ def parse_network_choice(
provider = self.get_provider_from_choice(
network_choice=network_choice, provider_settings=provider_settings
)
return ProviderContextManager(provider=provider, disconnect_after=disconnect_after)
return ProviderContextManager(
provider=provider,
disconnect_after=disconnect_after,
disconnect_on_exit=disconnect_on_exit,
)

@property
def default_ecosystem(self) -> EcosystemAPI:
Expand Down
6 changes: 3 additions & 3 deletions src/ape/managers/project/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,12 +275,12 @@ def extract_manifest(self) -> PackageManifest:
return manifest

def _extract_manifest_dependencies(self) -> Optional[Dict[PackageName, AnyUrl]]:
package_dependencies: Dict[PackageName, AnyUrl] = {}
package_dependencies: Dict[str, AnyUrl] = {}
for dependency_config in self.config_manager.dependencies:
package_name = dependency_config.name.replace("_", "-").lower()
package_dependencies[PackageName(package_name)] = dependency_config.uri
package_dependencies[package_name] = dependency_config.uri

return package_dependencies
return cast(Optional[Dict[PackageName, AnyUrl]], package_dependencies)

@property
def _package_deployments_folder(self) -> Path:
Expand Down
2 changes: 1 addition & 1 deletion src/ape/types/coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
from xml.etree.ElementTree import Element, SubElement, tostring

import requests
from ethpm_types import BaseModel
from ethpm_types.source import ContractSource, SourceLocation

from ape._pydantic_compat import NonNegativeInt, validator
from ape.logging import logger
from ape.utils.basemodel import BaseModel
from ape.utils.misc import get_current_timestamp_ms
from ape.version import version as ape_version

Expand Down
3 changes: 2 additions & 1 deletion src/ape/utils/basemodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def __getattr__(self, name: str) -> Any:
"""

try:
return super().__getattr__(name)
return super().__getattribute__(name)
except AttributeError:
extras_checked = set()
for ape_extra in self.__ape_extra_attributes__():
Expand Down Expand Up @@ -281,6 +281,7 @@ class Config:
arbitrary_types_allowed = True
underscore_attrs_are_private = True
copy_on_model_validation = "none"
use_enum_values = False

def __dir__(self) -> List[str]:
"""
Expand Down
6 changes: 2 additions & 4 deletions src/ape_ethereum/ecosystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,7 @@ class ForkedNetworkConfig(NetworkConfig):
"""


def _create_local_config(
default_provider: Optional[str] = None, use_fork: bool = False, **kwargs
) -> NetworkConfig:
def _create_local_config(default_provider: Optional[str] = None, use_fork: bool = False, **kwargs):
return _create_config(
base_fee_multiplier=1.0,
default_provider=default_provider,
Expand All @@ -155,7 +153,7 @@ def _create_local_config(
def _create_config(
required_confirmations: int = 2,
base_fee_multiplier: float = DEFAULT_LIVE_NETWORK_BASE_FEE_MULTIPLIER,
cls: Type[NetworkConfig] = NetworkConfig,
cls: Type = NetworkConfig,
**kwargs,
) -> NetworkConfig:
return cls(
Expand Down
4 changes: 4 additions & 0 deletions src/ape_geth/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,10 @@ def uri(self) -> str:
network_config = config.get(self.network.name) or DEFAULT_SETTINGS
return network_config.get("uri", DEFAULT_SETTINGS["uri"])

@property
def connection_id(self) -> Optional[str]:
return f"{self.network_choice}:{self.uri}"

@property
def _clean_uri(self) -> str:
return sanitize_url(self.uri)
Expand Down
31 changes: 26 additions & 5 deletions src/ape_plugins/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import Iterator, List, Optional, Sequence, Set, Tuple

from ape.__modules__ import __modules__
from ape._pydantic_compat import root_validator
from ape._pydantic_compat import root_validator, validator
from ape.logging import logger
from ape.plugins import clean_plugin_name
from ape.utils import BaseInterfaceModel, get_package_version, github_client
Expand Down Expand Up @@ -373,14 +373,27 @@ def __bool__(self) -> bool:
return len(self.plugins) > 0

def __repr__(self) -> str:
return f"<{self.name} Plugins Group>"
try:
return f"<{self.name} Plugins Group>"
except Exception:
# Prevent exceptions happening in repr()
logger.log_debug_stack_trace()
return "<PluginGroup>"

def __str__(self) -> str:
return self.to_str()

@validator("plugin_type")
def validate_plugin_type(cls, value):
return PluginType(value) if isinstance(value, str) else value

@property
def plugin_type_str(self) -> str:
return getattr(self.plugin_type, "value", str(self.plugin_type))

@property
def name(self) -> str:
return self.plugin_type.value.capitalize()
return self.plugin_type_str.capitalize()

@property
def plugin_names(self) -> List[str]:
Expand Down Expand Up @@ -426,8 +439,13 @@ def __init__(
self.metadata = metadata

def __repr__(self) -> str:
to_display_str = ", ".join([x.value for x in self.include])
return f"<PluginMap to_display='{to_display_str}'>"
try:
to_display_str = ", ".join([x.value for x in self.include])
return f"<PluginMap to_display='{to_display_str}'>"
except Exception:
# Prevent exceptions happening in repr()
logger.log_debug_stack_trace()
return "<ApePluginsRepr>"

def __str__(self) -> str:
sections = []
Expand All @@ -441,6 +459,9 @@ def __str__(self) -> str:
if PluginType.AVAILABLE in self.include and self.metadata.available:
sections.append(self.metadata.available)

if not sections:
return ""

# Use a single max length for all the sections.
max_length = max(x.max_name_length for x in sections)

Expand Down
18 changes: 10 additions & 8 deletions src/ape_run/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import click
from click import Command, Context, Option

from ape import project
from ape import networks, project
from ape.cli import NetworkBoundCommand, network_option, verbosity_option
from ape.cli.options import _VERBOSITY_VALUES, _create_verbosity_kwargs
from ape.exceptions import ApeException, handle_ape_exception
Expand Down Expand Up @@ -76,13 +76,15 @@ def invoke(self, ctx: Context) -> Any:
if ctx.params["interactive"]:
# Print the exception trace and then launch the console
# Attempt to use source-traceback style printing.
if not isinstance(err, ApeException) or not handle_ape_exception(
err, [ctx.obj.project_manager.path]
):
err_info = traceback.format_exc()
click.echo(err_info)

self._launch_console()
network_value = ctx.params.get("network") or networks.default_ecosystem.name
with networks.parse_network_choice(network_value, disconnect_on_exit=False):
if not isinstance(err, ApeException) or not handle_ape_exception(
err, [ctx.obj.project_manager.path]
):
err_info = traceback.format_exc()
click.echo(err_info)

self._launch_console()
else:
# Don't handle error - raise exception as normal.
raise
Expand Down
19 changes: 19 additions & 0 deletions tests/functional/test_block_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,22 @@ def test_poll_blocks_timeout(
with pytest.raises(ChainError, match=r"Timed out waiting for new block \(time_waited=1.\d+\)."):
with PollDaemon("blocks", poller, lambda x: None, lambda: False):
time.sleep(1.5)


def test_poll_blocks_future(chain_that_mined_5, eth_tester_provider, owner, PollDaemon):
blocks: Queue = Queue(maxsize=3)
poller = chain_that_mined_5.blocks.poll_blocks(
start_block=chain_that_mined_5.blocks.head.number + 1
)

with PollDaemon("blocks", poller, blocks.put, blocks.full):
# Sleep first to ensure listening before mining.
time.sleep(1)
eth_tester_provider.mine(3)

assert blocks.full()
first = blocks.get().number
second = blocks.get().number
third = blocks.get().number
assert first == second - 1
assert second == third - 1
1 change: 1 addition & 0 deletions tests/functional/test_network_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ def test_repr_disconnected(networks_disconnected):
def test_get_provider_from_choice_adhoc_provider(networks_connected_to_tester):
uri = "https://geth:1234567890abcdef@geth.foo.bar/"
provider = networks_connected_to_tester.get_provider_from_choice(f"ethereum:local:{uri}")
assert uri in provider.connection_id
assert provider.name == "geth"
assert provider.uri == uri
assert provider.network.name == "local"
Expand Down
Loading

0 comments on commit 6d08f0e

Please sign in to comment.