Skip to content

Commit

Permalink
feat: a config for excluding files from being compile by default [APE…
Browse files Browse the repository at this point in the history
…-1326] (#1627)
  • Loading branch information
antazoey authored Aug 28, 2023
1 parent 6bdce6a commit b45bf22
Show file tree
Hide file tree
Showing 29 changed files with 184 additions and 363 deletions.
20 changes: 3 additions & 17 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ jobs:

env:
GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GETH_VERSION: 1.12.0

steps:
- uses: actions/checkout@v3
Expand All @@ -84,23 +83,10 @@ jobs:
with:
go-version: '^1.20.1'

- name: Cache Geth
id: cache-geth
uses: actions/cache@v3
with:
path: $HOME/.local/bin
key: ${{ runner.os }}-geth-${{ env.GETH_VERSION }}

- name: Install Geth
if: steps.cache-geth.outputs.cache-hit != 'true'
run: |
mkdir -p $HOME/.local/bin
wget -O geth.tar.gz "https://github.com/ethereum/go-ethereum/archive/v$GETH_VERSION.tar.gz"
tar -zxvf geth.tar.gz
cd go-ethereum-$GETH_VERSION
make geth
cp ./build/bin/geth /usr/local/bin
geth version
uses: gacts/install-geth-tools@v1
with:
version: 1.12.2

- name: Install Dependencies
run: |
Expand Down
6 changes: 3 additions & 3 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.2.0
rev: v4.4.0
hooks:
- id: check-yaml

Expand All @@ -21,7 +21,7 @@ repos:
- id: flake8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.4.1
rev: v1.5.1
hooks:
- id: mypy
additional_dependencies: [
Expand All @@ -34,7 +34,7 @@ repos:
]

- repo: https://github.com/executablebooks/mdformat
rev: 0.7.16
rev: 0.7.17
hooks:
- id: mdformat
additional_dependencies: [mdformat-gfm, mdformat-frontmatter, mdformat-pyproject]
Expand Down
2 changes: 1 addition & 1 deletion docs/userguides/networks.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ geth:
If you would like to connect to a URI using the `geth` provider, you can specify a URI for the provider name in the `--network` option:

```bash
ape run script --network etheruem:mainnet:https://foo.bar
ape run script --network ethereum:mainnet:https://foo.bar
```

Additionally, if you want to connect to an unknown ecosystem or network, you can use the URI by itself.
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
],
"lint": [
"black>=23.7.0,<24", # Auto-formatter and linter
"mypy>=1.4.1,<2", # Static type analyzer
"mypy>=1.5.1,<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 All @@ -29,7 +29,7 @@
"flake8-breakpoint>=1.1.0,<2", # detect breakpoints left in code
"flake8-print>=4.0.1,<5", # detect print statements left in code
"isort>=5.10.1,<6", # Import sorting linter
"mdformat>=0.7.16", # Auto-formatter for markdown
"mdformat>=0.7.17", # Auto-formatter for markdown
"mdformat-gfm>=0.3.5", # Needed for formatting GitHub-flavored markdown
"mdformat-frontmatter>=0.4.1", # Needed for frontmatters-style headers in issue templates
"mdformat-pyproject>=0.0.1", # Allows configuring in pyproject.toml
Expand Down Expand Up @@ -100,7 +100,7 @@
"lazyasd>=0.1.4",
"packaging>=23.0,<24",
"pandas>=1.3.0,<2",
"pluggy>=0.12,<2",
"pluggy>=1.3,<2",
"pydantic>=1.10.8,<2",
"PyGithub>=1.59,<2",
"pytest>=6.0,<8.0",
Expand Down
6 changes: 5 additions & 1 deletion src/ape/api/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,11 @@ def _create_contract_from_call(
if "address" not in data:
return None, calldata

addr = data["address"]
# NOTE: Handling when providers give us odd address values.
# NOTE: `or ""` because sometimes the address key exists and is None.
raw_addr = HexBytes(data.get("address") or "").hex().replace("0x", "")
zeroes = max(40 - len(raw_addr), 0) * "0"
addr = f"0x{zeroes}{raw_addr}"

try:
address = self.provider.network.ecosystem.decode_address(addr)
Expand Down
24 changes: 11 additions & 13 deletions src/ape/api/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
ContractLog,
LogFilter,
SnapshotID,
SourceTraceback,
TraceFrame,
)
from ape.utils import (
Expand Down Expand Up @@ -702,16 +703,9 @@ def _increment_call_func_coverage_hit_count(self, txn: TransactionAPI):
):
return

cov_data = self._test_runner.coverage_tracker.data
if not cov_data:
return

contract_type = self.chain_manager.contracts.get(txn.receiver)
if not contract_type:
return

contract_src = self.project_manager._create_contract_source(contract_type)
if not contract_src:
if not (contract_type := self.chain_manager.contracts.get(txn.receiver)) or not (
contract_src := self.project_manager._create_contract_source(contract_type)
):
return

method_id = txn.data[:4]
Expand Down Expand Up @@ -1571,8 +1565,7 @@ def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMa
if not isinstance(err_data, dict):
return VirtualMachineError(base_err=exception, **kwargs)

err_msg = err_data.get("message")
if not err_msg:
if not (err_msg := err_data.get("message")):
return VirtualMachineError(base_err=exception, **kwargs)

if txn is not None and "nonce too low" in str(err_msg):
Expand All @@ -1593,9 +1586,14 @@ def _handle_execution_reverted(
txn: Optional[TransactionAPI] = None,
trace: Optional[Iterator[TraceFrame]] = None,
contract_address: Optional[AddressType] = None,
source_traceback: Optional[SourceTraceback] = None,
) -> ContractLogicError:
message = str(exception).split(":")[-1].strip()
params: Dict = {"trace": trace, "contract_address": contract_address}
params: Dict = {
"trace": trace,
"contract_address": contract_address,
"source_traceback": source_traceback,
}
no_reason = message == "execution reverted"

if isinstance(exception, Web3ContractLogicError) and no_reason:
Expand Down
2 changes: 0 additions & 2 deletions src/ape/cli/paramtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,4 @@ def convert( # type: ignore[override]
) -> List[PathLibPath]:
path = super().convert(value, param, ctx)
assert isinstance(path, PathLibPath) # For mypy

# NOTE: Return the path if it does not exist so it can be resolved downstream.
return get_all_files_in_directory(path) if path.is_dir() else [path]
128 changes: 11 additions & 117 deletions src/ape/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@
import tempfile
import time
import traceback
from collections import deque
from functools import cached_property
from inspect import getframeinfo, stack
from pathlib import Path
from types import CodeType, TracebackType
from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional, Union

import click
from eth_utils import humanize_hash
from ethpm_types import ContractType
from ethpm_types.abi import ConstructorABI, ErrorABI, MethodABI
from rich import print as rich_print

Expand Down Expand Up @@ -180,12 +177,10 @@ def _set_tb(self):
if not self.source_traceback and self.txn:
self.source_traceback = _get_ape_traceback(self.txn)

src_tb = self.source_traceback
if src_tb is not None and self.txn is not None:
if (src_tb := self.source_traceback) and self.txn is not None:
# Create a custom Pythonic traceback using lines from the sources
# found from analyzing the trace of the transaction.
py_tb = _get_custom_python_traceback(self, self.txn, src_tb)
if py_tb:
if py_tb := _get_custom_python_traceback(self, self.txn, src_tb):
self.__traceback__ = py_tb


Expand Down Expand Up @@ -213,12 +208,6 @@ def __init__(
self.txn = txn
self.trace = trace
self.contract_address = contract_address
if revert_message is None:
try:
# Attempt to use dev message as main exception message.
revert_message = self.dev_message
except Exception:
pass

super().__init__(
base_err=base_err,
Expand All @@ -229,11 +218,18 @@ def __init__(
txn=txn,
)

if revert_message is None and source_traceback is not None and (dev := self.dev_message):
try:
# Attempt to use dev message as main exception message.
self.message = dev
except Exception:
pass

@property
def revert_message(self):
return self.message

@cached_property
@property
def dev_message(self) -> Optional[str]:
"""
The dev-string message of the exception.
Expand All @@ -242,109 +238,7 @@ def dev_message(self) -> Optional[str]:
``ValueError``: When unable to get dev message.
"""

trace = self._get_trace()
if len(trace) == 0:
raise ValueError("Missing trace.")

if address := self.address:
try:
contract_type = trace[-1].chain_manager.contracts[address]
except Exception as err:
raise ValueError(
f"Could not fetch contract at {address} to check dev message."
) from err

else:
raise ValueError("Could not fetch contract information to check dev message.")

if contract_type.pcmap is None:
raise ValueError("Compiler does not support source code mapping.")

pc = None
pcmap = contract_type.pcmap.parse()

# To find a suitable line for inspecting dev messages, we must start at the revert and work
# our way backwards. If the last frame's PC is in the PC map, the offending line is very
# likely a 'raise' statement.
if trace[-1].pc in pcmap:
pc = trace[-1].pc

# Otherwise we must traverse the trace backwards until we find our first suitable candidate.
else:
last_depth = 1
while len(trace) > 0:
frame = trace.pop()
if frame.depth > last_depth:
# Call was made, get the new PCMap.
contract_type = self._find_next_contract(trace)
if not contract_type.pcmap:
raise ValueError("Compiler does not support source code mapping.")

pcmap = contract_type.pcmap.parse()
last_depth += 1

if frame.pc in pcmap:
pc = frame.pc
break

# We were unable to find a suitable PC that matched the compiler's map.
if pc is None:
return None

offending_source = pcmap[pc]
if offending_source is None:
return None

dev_messages = contract_type.dev_messages or {}
if offending_source.line_start is None:
# Check for a `dev` field in PCMap.
return None if offending_source.dev is None else offending_source.dev

elif offending_source.line_start in dev_messages:
return dev_messages[offending_source.line_start]

elif offending_source.dev is not None:
return offending_source.dev

# Dev message is neither found from the compiler or from a dev-comment.
return None

def _get_trace(self) -> deque:
trace = None
if self.trace is None and self.txn is not None:
try:
trace = deque(self.txn.trace)
except APINotImplementedError as err:
raise ValueError(
"Cannot check dev message; provider must support transaction tracing."
) from err

except (ProviderError, SignatureError) as err:
raise ValueError("Cannot fetch transaction trace.") from err

elif self.trace is not None:
trace = deque(self.trace)

if not trace:
raise ValueError("Cannot fetch transaction trace.")

return trace

def _find_next_contract(self, trace: deque) -> ContractType:
msg = "Could not fetch contract at '{address}' to check dev message."
idx = len(trace) - 1
while idx >= 0:
frame = trace[idx]
if frame.contract_address:
ct = frame.chain_manager.contracts.get(frame.contract_address)
if not ct:
raise ValueError(msg.format(address=frame.contract_address))

return ct

idx -= 1

raise ValueError(msg.format(address=frame.contract_address))
return self.source_traceback.revert_type if self.source_traceback else None

@classmethod
def from_error(cls, err: Exception):
Expand Down
12 changes: 10 additions & 2 deletions src/ape/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import sys
import traceback
from enum import IntEnum
from pathlib import Path
from typing import IO, Any, Dict, Optional, Union

import click
Expand All @@ -19,7 +20,7 @@ class LogLevel(IntEnum):
logging.addLevelName(LogLevel.SUCCESS.value, LogLevel.SUCCESS.name)
logging.SUCCESS = LogLevel.SUCCESS.value # type: ignore
DEFAULT_LOG_LEVEL = LogLevel.INFO.name
DEFAULT_LOG_FORMAT = "%(levelname)s: %(message)s"
DEFAULT_LOG_FORMAT = "%(levelname)s%(plugin)s: %(message)s"


def success(self, message, *args, **kws):
Expand Down Expand Up @@ -68,12 +69,19 @@ def __init__(self, fmt: Optional[str] = None):

def format(self, record):
if _isatty(sys.stdout) and _isatty(sys.stderr):
# only color log messages when sys.stdout and sys.stderr are sent to the terminal
# Only color log messages when sys.stdout and sys.stderr are sent to the terminal.
level = LogLevel(record.levelno)
default_dict: Dict[str, Any] = {}
styles: Dict[str, Any] = CLICK_STYLE_KWARGS.get(level, default_dict)
record.levelname = click.style(record.levelname, **styles)

path = Path(record.pathname)
record.plugin = ""
for part in path.parts:
if part.startswith("ape-"):
record.plugin = f" ({part})"
break

return super().format(record)


Expand Down
Loading

0 comments on commit b45bf22

Please sign in to comment.