Skip to content

Commit

Permalink
Merge branch 'main' into feat/account-list-filter-by-account
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Oct 31, 2023
2 parents fdca663 + be57c14 commit 8652776
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 21 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ The CLI tool contains all the Ape commands and the Python SDK contains the class

## **Ape Modular Plugin System:**

Our [list of plugins](https://www.apeworx.io/#plugins) is the best way to have the most interoperable experience with Web3.
Our modular plugin system is the best way to have the most interoperable experience with Web3.

**NOTE**: If a plugin does not originate from the [ApeWorX GitHub Organization](https://github.com/ApeWorX?q=ape&type=all), you will get a warning about installing 3rd-party plugins.

Expand Down
41 changes: 34 additions & 7 deletions docs/userguides/clis.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ This guide is for showcasing utilities that ship with Ape to assist in your CLI

## Ape Context Decorator

The `@ape_cli_context` gives you access to all the root Ape objects (`accounts`, `networks` etc.), the ape logger, and an `abort` method for stopping execution of your CLI gracefully.
The [@ape_cli_context](../methoddocs/cli.html#ape.cli.options.ape_cli_context) gives you access to all the root Ape objects (`accounts`, `networks` etc.), the ape logger, and an [abort](../methoddocs/cli.html#ape.cli.options.ApeCliContextObject.abort) method for stopping execution of your CLI gracefully.
Here is an example using all of those features from the `cli_ctx`:

```python
Expand All @@ -34,10 +34,37 @@ def cmd(cli_ctx):
cli_ctx.abort(f"Bad account: {account.address}")
```

In Ape, it is easy to extend the CLI context object and use the extended version in your CLIs:

```python
from ape.cli import ApeCliContextObject, ape_cli_context
import click

class MyManager:
"""My custom manager."""

class CustomContext(ApeCliContextObject):
"""Add new managers to your custom context"""
my_manager: MyManager = MyManager()

@property
def signer(self):
"""Utilize existing managers in your custom context."""
return self.account_manager.load("my_account")

@click.command()
@ape_cli_context(obj_type=CustomContext)
def cli(cli_ctx):
# Access your manager.
print(cli_ctx.my_manager)
# Access other Ape managers.
print(cli_ctx.account_manager)
```

## Network Tools

The `@network_option()` allows you to select an ecosystem / network / provider combination.
When using with the `NetworkBoundCommand` cls, you can cause your CLI to connect before any of your code executes.
The [@network_option()](../methoddocs/cli.html#ape.cli.options.network_option) allows you to select an ecosystem / network / provider combination.
When using with the [NetworkBoundCommand](../methoddocs/cli.html#ape.cli.commands.NetworkBoundCommand) class, you can cause your CLI to connect before any of your code executes.
This is useful if your script or command requires a provider connection in order for it to run.

```python
Expand All @@ -62,7 +89,7 @@ def cmd(network):

## Account Tools

Use the `@account_option()` for adding an option to your CLIs to select an account.
Use the [@account_option()](../methoddocs/cli.html#ape.cli.options.account_option) for adding an option to your CLIs to select an account.
This option does several things:

1. If you only have a single account in Ape (from both test accounts _and_ other accounts), it will use that account as the default.
Expand Down Expand Up @@ -96,7 +123,7 @@ And when invoking the command from the CLI, it would look like the following:
<prefix> cmd --account TEST::0 # Use the test account at index 0.
```

Alternatively, you can call the `get_user_selected_account()` directly to have more control of when the account gets selected:
Alternatively, you can call the [get_user_selected_account()](../methoddocs/cli.html#ape.cli.choices.get_user_selected_account) directly to have more control of when the account gets selected:

```python
import click
Expand All @@ -110,8 +137,8 @@ def cmd():
```

Similarly, there are a couple custom arguments for aliases alone that are useful when making CLIs for account creation.
If you use `@existing_alias_argument()` and specify an alias does not already exist, it will error.
And visa-versa when using `@non_existing_alias_argument()`
If you use [@existing_alias_argument()](../methoddocs/cli.html#ape.cli.arguments.existing_alias_argument) and specify an alias does not already exist, it will error.
And visa-versa when using [@non_existing_alias_argument()](../methoddocs/cli.html#ape.cli.arguments.non_existing_alias_argument).

```python
import click
Expand Down
91 changes: 91 additions & 0 deletions docs/userguides/networks.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,94 @@ from ape import chain
block = chain.provider.get_block("latest")
```

## Provider Context Manager

Use the [ProviderContextManager](../methoddocs/api.html#ape.api.networks.ProviderContextManager) to change the network-context in Python.
When entering a network for the first time, it will connect to that network.
**You do not need to call `.connect()` or `.disconnect()` manually**.

For example, if you are using a script with a default network connection, you can change connection in the middle of the script by using the provider context manager:

```python
from ape import chain, networks
def main():
start_provider = chain.provider.name
with networks.ethereum.mainnet.use_provider("geth") as provider:
# We are using a different provider than the one we started with.
assert start_provider != provider.name
```

Jump between networks to simulate multi-chain behavior.

```python
import click
from ape import networks
@click.command()
def cli():
with networks.polygon.mainnet.use_provider("geth"):
...
with networks.ethereum.mainnet.use_provider("geth"):
...
```

The argument to [use_provider()](../methoddocs/api.html#ape.api.networks.NetworkAPI.use_provider) is the name of the provider you want to use.
You can also tell Ape to use the default provider by calling method [use_default_provider()](../methoddocs/api.html#ape.api.networks.NetworkAPI.use_default_provider) instead.
This will use whatever provider is set as default for your ecosystem / network combination (via one of your `ape-config.yaml` files).

For example, let's say I have a default provider set like this:

```yaml
arbitrum:
mainnet:
default_provider: alchemy
```

```python
import ape
# Use the provider configured as the default for the arbitrum::mainnet network.
# In this case, it will use the "alchemy" provider.
with ape.networks.arbitrum.mainnet.use_default_provider():
...
```

You can also use the [parse_network_choice()](../methoddocs/managers.html#ape.managers.networks.NetworkManager.parse_network_choice) method when working with network choice strings:

```python
from ape import networks
# Same as doing `networks.ethereum.local.use_provider("test")`.
with networks.parse_network_choice("ethereum:local:test") as provider:
print(provider)
```
**A note about disconnect**: Providers do not disconnect until the very end of your Python session.
This is so you can easily switch network contexts in a bridge or multi-chain environment, which happens in fixtures and other sessions out of Ape's control.
However, sometimes you may definitely want your temporary network session to end before continuing, in which case you can use the `disconnect_after=True` kwarg:

```python
from ape import networks
with networks.parse_network_choice("ethereum:local:foundry", disconnect_after=True) as provider:
print(provider)
```

### Forked Context

Using the `networks.fork()` method, you can achieve similar effects to using a forked network with `disconnect_after=True`.
For example, let's say we are running the following script on the network `ethereum:mainnet`.
We can switch to a forked network by doing this:

```python
from ape import networks
def main():
with networks.fork("foundry"):
...
# Do stuff on a local, forked version of mainnet
# Switch back to mainnet.
```
15 changes: 5 additions & 10 deletions docs/userguides/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,12 @@ To disable isolation add the `--disable-isolation` flag when running `ape test`

## Fixtures

Fixtures are any type of reusable instances of something with configurable scopes. `pytest` handles passing fixtures
into each test method as test-time. To learn more about [fixtures](https://docs.pytest.org/en/7.1.x/explanation/fixtures.html)
You can define and use `pytest` fixtures in your Ape tests.
Learn more about fixtures from [this guide](https://docs.pytest.org/en/7.1.x/explanation/fixtures.html).
The syntax and functionalities of fixtures work exactly the same in Ape as it does with `pytest`.

Define fixtures for static data used by tests. This data can be accessed by all tests in the suite unless specified otherwise. This could be data as well as helpers of modules which will be passed to all tests.

A common place to define fixtures are in the **conftest.py** which should be saved under the test directory:

conftest.py is used to import external plugins or modules. By defining the following global variable, pytest will load the module and make it available for its test.

You can define your own fixtures or use existing ones. The `ape-test` plugin comes
with fixtures you will likely want to use:
The `ape-test` plugin comes with fixtures you will likely want to use.
The following guide explains each fixture that comes with Ape.

### accounts fixture

Expand Down
5 changes: 5 additions & 0 deletions src/ape/api/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,11 @@ class ProviderContextManager(ManagerAccessMixin):
mainnet = networks.ethereum.mainnet # An instance of NetworkAPI
with mainnet.use_provider("infura"):
...
# Or, using choice-strings:
with networks.parse_network_choice("ethereum:local:test"):
...
"""

connected_providers: Dict[str, "ProviderAPI"] = {}
Expand Down
2 changes: 2 additions & 0 deletions src/ape/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
)
from ape.cli.commands import NetworkBoundCommand
from ape.cli.options import (
ApeCliContextObject,
account_option,
ape_cli_context,
contract_option,
Expand All @@ -33,6 +34,7 @@
"Alias",
"AllFilePaths",
"ape_cli_context",
"ApeCliContextObject",
"contract_file_paths_argument",
"contract_option",
"existing_alias_argument",
Expand Down
13 changes: 10 additions & 3 deletions src/ape/cli/options.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Callable, Dict, List, NoReturn, Optional, Union
from typing import Callable, Dict, List, NoReturn, Optional, Type, Union

import click
from ethpm_types import ContractType
Expand Down Expand Up @@ -75,7 +75,9 @@ def set_level(ctx, param, value):
}


def ape_cli_context(default_log_level: str = DEFAULT_LOG_LEVEL):
def ape_cli_context(
default_log_level: str = DEFAULT_LOG_LEVEL, obj_type: Type = ApeCliContextObject
):
"""
A ``click`` context object with helpful utilities.
Use in your commands to get access to common utility features,
Expand All @@ -84,11 +86,16 @@ def ape_cli_context(default_log_level: str = DEFAULT_LOG_LEVEL):
Args:
default_log_level (str): The log-level value to pass to
:meth:`~ape.cli.options.verbosity_option`.
obj_type (Type): The context object type. Defaults to
:class:`~ape.cli.options.ApeCliContextObject`. Sub-class
the context to extend its functionality in your CLIs,
such as if you want to add additional manager classes
to the context.
"""

def decorator(f):
f = verbosity_option(logger, default=default_log_level)(f)
f = click.make_pass_decorator(ApeCliContextObject, ensure=True)(f)
f = click.make_pass_decorator(obj_type, ensure=True)(f)
return f

return decorator
Expand Down
5 changes: 5 additions & 0 deletions src/ape/types/signatures.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

from ape.types import AddressType

# Fix 404 in doc link.
SignableMessage.__doc__ = (SignableMessage.__doc__ or "").replace(
"EIP-191_", "`EIP-191 <https://eips.ethereum.org/EIPS/eip-191>`__"
)


def _left_pad_bytes(val: bytes, num_bytes: int) -> bytes:
return b"\x00" * (num_bytes - len(val)) + val if len(val) < num_bytes else val
Expand Down

0 comments on commit 8652776

Please sign in to comment.