diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2de45d1..e15dfcf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 @@ -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] diff --git a/README.md b/README.md index 0102dd0..972f21e 100644 --- a/README.md +++ b/README.md @@ -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" ``` @@ -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") -``` diff --git a/ape_addressbook/addressbook.py b/ape_addressbook/addressbook.py index f555d01..c30adbe 100644 --- a/ape_addressbook/addressbook.py +++ b/ape_addressbook/addressbook.py @@ -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: @@ -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): @@ -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: """ @@ -97,9 +71,10 @@ 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]: @@ -107,6 +82,7 @@ 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 @@ -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() diff --git a/setup.py b/setup.py index baff82c..0839db1 100644 --- a/setup.py +++ b/setup.py @@ -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 ], @@ -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, diff --git a/tests/conftest.py b/tests/conftest.py index a66fdc1..e58c38f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,3 @@ -import shutil import tempfile from pathlib import Path @@ -7,8 +6,6 @@ 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" @@ -16,7 +13,6 @@ APE_CONFIG = rf""" addressbook: - entries: # This address purposely is not checksummed. {PROJECT_ALIAS_UNCHECKSUMMED}: '{PROJECT_ADDRESS_NON_CHECKSUMMED}' @@ -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. @@ -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 @@ -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() diff --git a/tests/test_addressbook.py b/tests/test_addressbook.py index d4c17b7..d112bf0 100644 --- a/tests/test_addressbook.py +++ b/tests/test_addressbook.py @@ -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