Skip to content

Commit

Permalink
refactor!: remove global config and flatten [APE-1455] (#14)
Browse files Browse the repository at this point in the history
Co-authored-by: Juliya Smith <[email protected]>
  • Loading branch information
fubuloubu and antazoey authored Dec 12, 2023
1 parent ee76978 commit 771814a
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 151 deletions.
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.2.0
rev: v4.5.0
hooks:
- id: check-yaml

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

- repo: https://github.com/psf/black
rev: 23.3.0
rev: 23.12.0
hooks:
- id: black
name: black

- repo: https://github.com/pycqa/flake8
rev: 6.0.0
rev: 6.1.0
hooks:
- id: flake8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991
rev: v1.7.1
hooks:
- id: mypy
additional_dependencies: [types-setuptools, pydantic]

- repo: https://github.com/executablebooks/mdformat
rev: 0.7.14
rev: 0.7.17
hooks:
- id: mdformat
additional_dependencies: [mdformat-gfm, mdformat-frontmatter]
Expand Down
9 changes: 0 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ To use the addressbook in a project, add common addresses to your `ape-config.ya

```yaml
addressbook:
entries:
shared_account: "0x2192f6112a026bce4047CeD2A16553Fd31E798B6"
```
Expand All @@ -43,11 +42,3 @@ from ape_addressbook import addressbook

address = addressbook["shared_account"]
```

You can also add global addresses:

```python
from ape_addressbook import addressbook

addressbook.set_global_entry("global_address", "0x2192f6112a026bce4047CeD2A16553Fd31E798B6")
```
82 changes: 13 additions & 69 deletions ape_addressbook/addressbook.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import json
from pathlib import Path
from typing import Dict, Iterator, Optional, cast
from typing import Dict, Iterator, cast

from ape._pydantic_compat import root_validator
from ape.api import PluginConfig
from ape.exceptions import AccountsError
from ape.logging import logger
from ape.types import AddressType, BaseModel
from ape.types import AddressType
from ape.utils import ManagerAccessMixin
from eth_utils import is_checksum_address, to_checksum_address
from pydantic import validator


def _validate_entries(entries: Dict) -> Dict:
Expand All @@ -29,19 +26,15 @@ def _validate_entries(entries: Dict) -> Dict:


class AddressBookConfig(PluginConfig):
entries: Dict[str, AddressType] = {}

@validator("entries", pre=True)
@root_validator(pre=True)
def validate_entries(cls, entries):
return _validate_entries(entries)

def __len__(self) -> int:
return len(self.dict())

class GlobalAddressBook(BaseModel):
entries: Dict[str, AddressType] = {}

@validator("entries", pre=True)
def validate_entries(cls, entries):
return _validate_entries(entries)
class Config:
extra = "allow"


class AddressBook(ManagerAccessMixin):
Expand All @@ -61,25 +54,6 @@ class AddressBook(ManagerAccessMixin):
"""

@property
def global_config_file(self) -> Path:
"""
The file path to the global addressbook entries JSON file,
located in Ape's data folder.
"""

return self.config_manager.DATA_FOLDER / "addressbook.json"

@property
def global_config(self) -> GlobalAddressBook:
"""
All of the entries stored in the global addressbook JSON file.
"""

file = self.global_config_file
cls = GlobalAddressBook
return cls.parse_file(file) if file.is_file() else cls()

@property
def config(self) -> AddressBookConfig:
"""
Expand All @@ -97,16 +71,18 @@ def registry(self) -> Dict[str, AddressType]:
and project addresses.
"""

registry = self.global_config.dict().get("entries", [])
registry.update(self.config.dict().get("entries", []))
return registry
data = self.config.dict()

# Sorted for consistency's sake.
return {k: data[k] for k in sorted(data)}

@property
def aliases(self) -> Iterator[str]:
"""
An iterator over all aliases in the registry.
"""

# NOTE: self.registry is sorted.
for alias in self.registry:
yield alias

Expand All @@ -119,37 +95,5 @@ def __getitem__(self, alias: str) -> AddressType:

return self.registry[alias]

def set_global_entry(
self, alias: str, address: AddressType, ecosystem_name: Optional[str] = None
):
"""
Add an address in the global registry of the addressbook.
Args:
alias (str): An alias for the address.
address (``AddressType``): The address.
ecosystem_name (Optional[str]): The ecosystem the address belongs to.
This is only used to help decode the address. The parameter defaults
to ``None`` but will use the connected provider's ecosystem if is connected.
Else, will attempt to use Ethereum, which should work for any EVM ecosystem.
"""

if alias in self.aliases:
raise AccountsError(f"Alias '{alias}' already in addressbook.")

if ecosystem_name is None and self.network_manager.active_provider:
ecosystem = self.provider.network.ecosystem
elif ecosystem_name is not None:
ecosystem = (
self.network_manager.ecosystems.get(ecosystem_name) or self.network_manager.ethereum
)
else:
# Default to ethereum
ecosystem = self.network_manager.ethereum

global_config = self.global_config.copy()
global_config.entries[alias] = ecosystem.decode_address(address)
self.global_config_file.write_text(json.dumps(global_config.dict()))


addressbook = AddressBook()
9 changes: 5 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
"hypothesis>=6.2.0,<7.0", # Strategy-based fuzzer
],
"lint": [
"black>=23.3.0,<24", # auto-formatter and linter
"mypy>=0.991,<1", # Static type analyzer
"black>=23.12.0,<24", # auto-formatter and linter
"mypy>=1.7.1,<2", # Static type analyzer
"types-setuptools", # Needed due to mypy typeshed
"flake8>=6.0.0,<7", # Style linter
"flake8>=6.1.0,<7", # Style linter
"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
],
Expand Down Expand Up @@ -58,6 +58,7 @@
include_package_data=True,
install_requires=[
"eth-ape>=0.6.0,<0.7",
"pydantic", # Use same version as eth-ape.
],
python_requires=">=3.8,<4",
extras_require=extras_require,
Expand Down
39 changes: 2 additions & 37 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import shutil
import tempfile
from pathlib import Path

Expand All @@ -7,16 +6,13 @@
from ape.managers.config import CONFIG_FILE_NAME
from eth_utils import to_checksum_address

from ape_addressbook import addressbook

PROJECT_ALIAS_UNCHECKSUMMED = "project_entry_quotes"
PROJECT_ALIAS_NO_QUOTES = "project_entry_no_quotes"
PROJECT_ADDRESS_NON_CHECKSUMMED = "0xbc8563cb0eedbd1b95ccafd0c156e2daf5e18c29"
PROJECT_ADDRESS = to_checksum_address(PROJECT_ADDRESS_NON_CHECKSUMMED)

APE_CONFIG = rf"""
addressbook:
entries:
# This address purposely is not checksummed.
{PROJECT_ALIAS_UNCHECKSUMMED}: '{PROJECT_ADDRESS_NON_CHECKSUMMED}'
Expand All @@ -26,7 +22,7 @@


@pytest.fixture(autouse=True, scope="session")
def madeup_project_and_data_folder():
def madeup_project():
"""
Prevents actually affecting global addressbook
and sets a temporary project.
Expand All @@ -39,32 +35,12 @@ def madeup_project_and_data_folder():
config_file.touch()
config_file.write_text(APE_CONFIG)

# Set the data folder to a temp dir as well so we can
# create global entries.
original = ape.config.DATA_FOLDER
data_folder = path / "data"
data_folder.mkdir()
ape.config.DATA_FOLDER = data_folder

with ape.config.using_project(Path(temp_dir)):
with ape.config.using_project(path):
yield

# Clean up.
ape.config.DATA_FOLDER = original
if config_file.is_file():
config_file.unlink()
if data_folder.is_dir():
shutil.rmtree(data_folder)


@pytest.fixture
def global_alias():
return "global_address"


@pytest.fixture
def global_address():
return "0x2192f6112a026bce4047CeD2A16553Fd31E798B6"


@pytest.fixture
Expand All @@ -80,14 +56,3 @@ def project_alias_no_quotes():
@pytest.fixture
def project_address():
return PROJECT_ADDRESS


@pytest.fixture
def book(global_alias, global_address):
address = global_address.lower() # Use lower to show checksum works.
addressbook.set_global_entry(global_alias, address)

yield addressbook

if addressbook.global_config_file.is_file():
addressbook.global_config_file.unlink()
39 changes: 12 additions & 27 deletions tests/test_addressbook.py
Original file line number Diff line number Diff line change
@@ -1,66 +1,51 @@
from ape_addressbook import addressbook


def test_config(book, project_alias_unchecksummed, project_alias_no_quotes, project_address):
def test_config(project_alias_unchecksummed, project_alias_no_quotes, project_address):
"""
This test shows that we are able to read values from a
project's config as well as handle checksumming them if needbe.
"""

actual = book.config.entries
actual = addressbook.config
assert len(actual) == 2
assert actual[project_alias_unchecksummed] == project_address
assert actual[project_alias_no_quotes] == project_address


def test_registry(
book,
global_alias,
project_alias_unchecksummed,
project_alias_no_quotes,
project_address,
global_address,
):
actual = book.registry
assert len(actual) == 3
assert actual[global_alias] == global_address
actual = addressbook.registry
assert len(actual) == 2
assert actual[project_alias_unchecksummed] == project_address
assert actual[project_alias_no_quotes] == project_address


def test_aliases(book, global_alias, project_alias_unchecksummed, project_alias_no_quotes):
def test_aliases(project_alias_unchecksummed, project_alias_no_quotes):
"""
The aliases includes both project and global addresses.
"""

assert list(addressbook.aliases) == [
global_alias,
project_alias_unchecksummed,
project_alias_no_quotes,
]
actual = list(addressbook.aliases)
expected = [project_alias_no_quotes, project_alias_unchecksummed]
assert actual == expected


def test_contains(
book,
global_alias,
global_address,
project_alias_unchecksummed,
project_address,
project_alias_no_quotes,
):
assert global_alias in book
assert project_alias_unchecksummed in book
assert project_alias_no_quotes in book
assert project_alias_unchecksummed in addressbook
assert project_alias_no_quotes in addressbook


def test_get_item(
book,
global_alias,
global_address,
project_alias_unchecksummed,
project_address,
project_alias_no_quotes,
):
assert book[global_alias] == global_address
assert book[project_alias_unchecksummed] == project_address
assert book[project_alias_no_quotes] == project_address
assert addressbook[project_alias_unchecksummed] == project_address
assert addressbook[project_alias_no_quotes] == project_address

0 comments on commit 771814a

Please sign in to comment.