Skip to content

Commit

Permalink
refactor!: pydantic v2 upgrade [APE-1413] (#1674)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Dec 9, 2023
1 parent 9fec54e commit 82a9f9e
Show file tree
Hide file tree
Showing 90 changed files with 854 additions and 652 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ repos:
- id: flake8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.7.0
rev: v1.7.1
hooks:
- id: mypy
additional_dependencies: [
Expand Down
2 changes: 1 addition & 1 deletion docs/userguides/console.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ If you include a function named `ape_init_extras`, it will be executed with the

```python
def ape_init_extras(chain):
return {"web3": chain.provider._web3}
return {"web3": chain.provider.web3}
```

Then `web3` will be available to use immediately.
Expand Down
2 changes: 1 addition & 1 deletion docs/userguides/contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ In the example above, the bytes value returned contains the method ID selector p
Alternatively, you can decode input:

```python
from ethpm_types import HexBytes
from eth_pydantic_types import HexBytes
from ape import Contract

contract = Contract("0x...")
Expand Down
22 changes: 11 additions & 11 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,28 @@

extras_require = {
"test": [ # `test` GitHub Action jobs uses this
"pytest-xdist", # multi-process runner
"pytest-xdist", # Multi-process runner
"pytest-cov>=4.0.0,<5", # Coverage analyzer plugin
"pytest-mock", # For creating mocks
"hypothesis>=6.2.0,<7.0", # Strategy-based fuzzer
"hypothesis-jsonschema==0.19.0", # JSON Schema fuzzer extension
],
"lint": [
"black>=23.11.0,<24", # Auto-formatter and linter
"mypy>=1.7.0,<2", # Static type analyzer
"mypy>=1.7.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
"pandas-stubs==1.2.0.62", # Needed due to mypy typeshed
"types-SQLAlchemy>=1.4.49", # Needed due to mypy typeshed
"flake8>=6.1.0,<7", # Style linter
"flake8-breakpoint>=1.1.0,<2", # detect breakpoints left in code
"flake8-print>=4.0.1,<5", # detect print statements left in code
"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.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
"pydantic<2.0", # Needed for successful type check. TODO: Remove after full v2 support.
],
"doc": [
"myst-parser>=1.0.0,<2", # Parse markdown docs
Expand Down Expand Up @@ -102,7 +101,8 @@
"packaging>=23.0,<24",
"pandas>=1.3.0,<2",
"pluggy>=1.3,<2",
"pydantic>=1.10.8,<3",
"pydantic>=2.5.0,<3",
"pydantic-settings>=2.0.3,<3",
"PyGithub>=1.59,<2",
"pytest>=6.0,<8.0",
"python-dateutil>=2.8.2,<3",
Expand All @@ -119,13 +119,13 @@
"eth-account>=0.8,<0.9",
"eth-typing>=3.4,<4",
"eth-utils>=2.2.0,<3",
"hexbytes>=0.2.3,<1",
"py-geth>=3.13.0,<4",
"web3[tester]>=6.7.0,<7",
"web3[tester]>=6.11.4,<7",
# ** Dependencies maintained by ApeWorX **
"eip712>=0.2.1,<0.3",
"ethpm-types>=0.5.10,<0.6",
"evm-trace>=0.1.0a23",
"eip712>=0.2.2,<0.3",
"ethpm-types>=0.6.1,<0.7",
"eth_pydantic_types>=0.1.0a4,<0.2",
"evm-trace>=0.1.0",
],
entry_points={
"console_scripts": ["ape=ape._cli:cli"],
Expand Down
2 changes: 1 addition & 1 deletion src/ape/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def display_config(ctx, param, value):
from ape import project

click.echo("# Current configuration")
click.echo(yaml.dump(project.config_manager.dict()))
click.echo(yaml.dump(project.config_manager.model_dump(mode="json")))

ctx.exit() # NOTE: Must exit to bypass running ApeCLI

Expand Down
47 changes: 0 additions & 47 deletions src/ape/_pydantic_compat.py

This file was deleted.

7 changes: 5 additions & 2 deletions src/ape/api/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,10 +462,13 @@ def __contains__(self, address: AddressType) -> bool:

def _verify_account_type(self, account):
if not isinstance(account, self.account_type):
container_type_name = getattr(type(account), "__name__", "<CustomContainerType>")
account_type_name = getattr(self.account_type, "__name__", "<UnknownAccount>")
message = (
f"Container '{type(account).__name__}' is an incorrect "
f"type for container '{type(self.account_type).__name__}'."
f"Container '{container_type_name}' is an incorrect "
f"type for container '{account_type_name}'."
)

raise AccountsError(message)

def _verify_unused_alias(self, account):
Expand Down
5 changes: 3 additions & 2 deletions src/ape/api/address.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import TYPE_CHECKING, Any, List

from ethpm_types import HexBytes
from eth_pydantic_types import HexBytes

from ape.exceptions import ConversionError
from ape.types import AddressType, ContractCode
Expand Down Expand Up @@ -70,7 +70,8 @@ def __dir__(self) -> List[str]:
return self._base_dir_values

def __repr__(self) -> str:
return f"<{self.__class__.__name__} {self.address}>"
cls_name = getattr(type(self), "__name__", BaseAddress.__name__)
return f"<{cls_name} {self.address}>"

def __str__(self) -> str:
"""
Expand Down
10 changes: 6 additions & 4 deletions src/ape/api/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from pathlib import Path
from typing import Dict, Iterator, List, Optional, Set, Tuple

from ethpm_types import ContractType, HexBytes
from eth_pydantic_types import HexBytes
from ethpm_types import ContractType
from ethpm_types.source import Content, ContractSource
from evm_trace.geth import TraceFrame as EvmTraceFrame
from evm_trace.geth import create_call_node_data
Expand Down Expand Up @@ -49,8 +50,8 @@ def settings(self) -> PluginConfig:
The combination of settings from ``ape-config.yaml`` and ``.compiler_settings``.
"""
CustomConfig = self.config.__class__
data = {**self.config.dict(), **self.compiler_settings}
return CustomConfig.parse_obj(data)
data = {**self.config.model_dump(mode="json", by_alias=True), **self.compiler_settings}
return CustomConfig.model_validate(data)

@abstractmethod
def get_versions(self, all_paths: List[Path]) -> Set[str]:
Expand Down Expand Up @@ -157,7 +158,8 @@ def get_version_map( # type: ignore[empty-body]
"""

def __repr__(self) -> str:
return f"<{self.__class__.__name__} {self.name}>"
cls_name = getattr(type(self), "__name__", CompilerAPI.__name__)
return f"<{cls_name} {self.name}>"

def __str__(self) -> str:
return self.name
Expand Down
13 changes: 4 additions & 9 deletions src/ape/api/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from enum import Enum
from typing import Any, Dict, Optional, TypeVar

from ape._pydantic_compat import BaseModel, BaseSettings
from pydantic import ConfigDict
from pydantic_settings import BaseSettings

T = TypeVar("T")

Expand All @@ -14,10 +15,6 @@ class ConfigEnum(str, Enum):
"""


class ConfigDict(BaseModel):
__root__: dict = {}


class PluginConfig(BaseSettings):
"""
A base plugin configuration class. Each plugin that includes
Expand All @@ -26,7 +23,7 @@ class PluginConfig(BaseSettings):

@classmethod
def from_overrides(cls, overrides: Dict) -> "PluginConfig":
default_values = cls().dict()
default_values = cls().model_dump()

def update(root: Dict, value_map: Dict):
for key, val in value_map.items():
Expand Down Expand Up @@ -54,9 +51,7 @@ def get(self, key: str, default: Optional[T] = None) -> T:
return self.__dict__.get(key, default)


class GenericConfig(PluginConfig):
class GenericConfig(ConfigDict):
"""
The default class used when no specialized class is used.
"""

__root__: dict = {}
2 changes: 1 addition & 1 deletion src/ape/api/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
ConvertedType = TypeVar("ConvertedType")


class ConverterAPI(Generic[ConvertedType], BaseInterfaceModel):
class ConverterAPI(BaseInterfaceModel, Generic[ConvertedType]):
@abstractmethod
def is_convertible(self, value: Any) -> bool:
"""
Expand Down
16 changes: 9 additions & 7 deletions src/ape/api/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@
encode_transaction,
serializable_unsigned_transaction_from_dict,
)
from eth_pydantic_types import HexBytes
from eth_utils import keccak, to_int
from ethpm_types import ContractType, HexBytes
from ethpm_types import BaseModel, ContractType
from ethpm_types.abi import ABIType, ConstructorABI, EventABI, MethodABI
from pydantic import computed_field

from ape._pydantic_compat import BaseModel
from ape.exceptions import (
NetworkError,
NetworkMismatchError,
Expand Down Expand Up @@ -88,6 +89,7 @@ class EcosystemAPI(BaseInterfaceModel):
"""The number of the decimals the fee token has."""

_default_network: Optional[str] = None
"""The default network of the ecosystem, such as ``local``."""

def __repr__(self) -> str:
return f"<{self.name}>"
Expand Down Expand Up @@ -152,8 +154,7 @@ def serialize_transaction(self, transaction: "TransactionAPI") -> bytes:
if not self.signature:
raise SignatureError("The transaction is not signed.")

txn_data = self.dict(exclude={"sender"})

txn_data = self.model_dump(exclude={"sender"})
unsigned_txn = serializable_unsigned_transaction_from_dict(txn_data)
signature = (
self.signature.v,
Expand Down Expand Up @@ -267,7 +268,6 @@ def default_network(self) -> str:
Returns:
str
"""

if network := self._default_network:
# Was set programatically.
return network
Expand All @@ -285,7 +285,7 @@ def default_network(self) -> str:
return self.networks[0]

# Very unlikely scenario.
raise ValueError("No networks found.")
raise NetworkError("No networks found.")

def set_default_network(self, network_name: str):
"""
Expand Down Expand Up @@ -506,7 +506,7 @@ def get_method_selector(self, abi: MethodABI) -> HexBytes:
Override example::
from ape.api import EcosystemAPI
from ethpm_types import HexBytes
from eth_pydantic_types import HexBytes
class MyEcosystem(EcosystemAPI):
def get_method_selector(self, abi: MethodABI) -> HexBytes:
Expand Down Expand Up @@ -700,6 +700,7 @@ class NetworkAPI(BaseInterfaceModel):
request_header: Dict
"""A shareable network HTTP header."""

# See ``.default_provider`` which is the proper field.
_default_provider: str = ""

@classmethod
Expand Down Expand Up @@ -982,6 +983,7 @@ def use_provider(
provider = self.get_provider(provider_name=provider_name, provider_settings=settings)
return ProviderContextManager(provider=provider, disconnect_after=disconnect_after)

@computed_field() # type: ignore[misc]
@property
def default_provider(self) -> Optional[str]:
"""
Expand Down
Loading

0 comments on commit 82a9f9e

Please sign in to comment.