Skip to content

Commit

Permalink
feat: support pydantic v2 [APE-1374] (#1651)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Juliya Smith <jules@apeworx.io>
  • Loading branch information
banteg and antazoey authored Sep 28, 2023
1 parent 62687ab commit db49185
Show file tree
Hide file tree
Showing 24 changed files with 82 additions and 33 deletions.
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"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 @@ -101,7 +102,7 @@
"packaging>=23.0,<24",
"pandas>=1.3.0,<2",
"pluggy>=1.3,<2",
"pydantic>=1.10.8,<2",
"pydantic>=1.10.8,<3",
"PyGithub>=1.59,<2",
"pytest>=6.0,<8.0",
"python-dateutil>=2.8.2,<3",
Expand Down
47 changes: 47 additions & 0 deletions src/ape/_pydantic_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# support both pydantic v1 and v2

try:
from pydantic.v1 import ( # type: ignore
BaseModel,
BaseSettings,
Extra,
Field,
FileUrl,
HttpUrl,
NonNegativeInt,
PositiveInt,
ValidationError,
root_validator,
validator,
)
from pydantic.v1.dataclasses import dataclass # type: ignore
except ImportError:
from pydantic import ( # type: ignore
BaseModel,
BaseSettings,
Extra,
Field,
FileUrl,
HttpUrl,
NonNegativeInt,
PositiveInt,
ValidationError,
root_validator,
validator,
)
from pydantic.dataclasses import dataclass # type: ignore

__all__ = (
"BaseModel",
"BaseSettings",
"dataclass",
"Extra",
"Field",
"FileUrl",
"HttpUrl",
"NonNegativeInt",
"PositiveInt",
"ValidationError",
"root_validator",
"validator",
)
2 changes: 1 addition & 1 deletion src/ape/api/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from enum import Enum
from typing import Any, Dict, Optional, TypeVar

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

T = TypeVar("T")

Expand Down
2 changes: 1 addition & 1 deletion src/ape/api/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from eth_utils import keccak, to_int
from ethpm_types import ContractType, HexBytes
from ethpm_types.abi import ABIType, ConstructorABI, EventABI, MethodABI
from pydantic import BaseModel

from ape._pydantic_compat import BaseModel
from ape.exceptions import (
NetworkError,
NetworkMismatchError,
Expand Down
7 changes: 4 additions & 3 deletions src/ape/api/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@

from ethpm_types import Checksum, ContractType, PackageManifest, Source
from ethpm_types.manifest import PackageName
from ethpm_types.source import Content
from ethpm_types.utils import Algorithm, AnyUrl, compute_checksum
from packaging.version import InvalidVersion, Version
from pydantic import ValidationError

from ape._pydantic_compat import ValidationError
from ape.logging import logger
from ape.utils import (
BaseInterfaceModel,
Expand Down Expand Up @@ -143,7 +144,7 @@ def contracts(self) -> Dict[str, ContractType]:
continue

contract_name = p.stem
contract_type = ContractType().parse_file(p)
contract_type = ContractType.parse_file(p)
if contract_type.name is None:
contract_type.name = contract_name

Expand Down Expand Up @@ -214,7 +215,7 @@ def _create_source_dict(
hash=compute_checksum(source_path.read_bytes()),
),
urls=[],
content=text,
content=Content(__root__={i + 1: x for i, x in enumerate(text.splitlines())}),
imports=source_imports.get(key, []),
references=source_references.get(key, []),
)
Expand Down
2 changes: 1 addition & 1 deletion src/ape/api/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
from ethpm_types import HexBytes
from evm_trace import CallTreeNode as EvmCallTreeNode
from evm_trace import TraceFrame as EvmTraceFrame
from pydantic import Field, root_validator, validator
from web3 import Web3
from web3.exceptions import ContractLogicError as Web3ContractLogicError
from web3.exceptions import MethodUnavailable, TimeExhausted, TransactionNotFound
from web3.types import RPCEndpoint, TxParams

from ape._pydantic_compat import Field, root_validator, validator
from ape.api.config import PluginConfig
from ape.api.networks import LOCAL_NETWORK_NAME, NetworkAPI
from ape.api.query import BlockTransactionQuery
Expand Down
2 changes: 1 addition & 1 deletion src/ape/api/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from typing import Any, Dict, Iterator, List, Optional, Sequence, Set, Type, Union

from ethpm_types.abi import EventABI, MethodABI
from pydantic import BaseModel, NonNegativeInt, PositiveInt, root_validator

from ape._pydantic_compat import BaseModel, NonNegativeInt, PositiveInt, root_validator
from ape.api.transactions import ReceiptAPI, TransactionAPI
from ape.logging import logger
from ape.types import AddressType
Expand Down
3 changes: 1 addition & 2 deletions src/ape/api/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
from eth_utils import is_0x_prefixed, is_hex, to_int
from ethpm_types import HexBytes
from ethpm_types.abi import EventABI, MethodABI
from pydantic import validator
from pydantic.fields import Field
from tqdm import tqdm # type: ignore

from ape._pydantic_compat import Field, validator
from ape.api.explorers import ExplorerAPI
from ape.exceptions import (
NetworkError,
Expand Down
8 changes: 5 additions & 3 deletions src/ape/managers/chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,16 @@ def query(
)

query = BlockQuery(
columns=columns,
columns=list(columns),
start_block=start_block,
stop_block=stop_block,
step=step,
)

blocks = self.query_manager.query(query, engine_to_use=engine_to_use)
columns = validate_and_expand_columns(columns, self.head.__class__) # type: ignore
columns: List[str] = validate_and_expand_columns( # type: ignore
columns, self.head.__class__
)
blocks = map(partial(extract_fields, columns=columns), blocks)
return pd.DataFrame(columns=columns, data=blocks)

Expand Down Expand Up @@ -486,7 +488,7 @@ def query(
)

query = AccountTransactionQuery(
columns=columns,
columns=list(columns),
account=self.address,
start_nonce=start_nonce,
stop_nonce=stop_nonce,
Expand Down
3 changes: 1 addition & 2 deletions src/ape/managers/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
from pathlib import Path
from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Union

from pydantic import root_validator

from ape._pydantic_compat import root_validator
from ape.api import ConfigDict, DependencyAPI, PluginConfig
from ape.exceptions import ConfigError, NetworkError
from ape.logging import logger
Expand Down
2 changes: 1 addition & 1 deletion src/ape/managers/project/dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

from ethpm_types import PackageManifest
from ethpm_types.utils import AnyUrl
from pydantic import FileUrl, HttpUrl, root_validator
from semantic_version import NpmSpec, Version # type: ignore

from ape._pydantic_compat import FileUrl, HttpUrl, root_validator
from ape.api import DependencyAPI
from ape.exceptions import ProjectError, UnknownVersionError
from ape.logging import logger
Expand Down
1 change: 0 additions & 1 deletion src/ape/managers/project/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,6 @@ def track_deployment(self, contract: ContractInstance):
if network == LOCAL_NETWORK_NAME or network.endswith("-fork"):
raise ProjectError("Can only publish deployments on a live network.")

contract_name = contract.contract_type.name
if not (contract_name := contract.contract_type.name):
raise ProjectError("Contract name required when publishing.")

Expand Down
4 changes: 2 additions & 2 deletions src/ape/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
)
from ethpm_types.abi import EventABI
from ethpm_types.source import Closure
from pydantic import BaseModel, root_validator, validator
from web3.types import FilterParams

from ape._pydantic_compat import BaseModel, root_validator, validator
from ape.types.address import AddressType, RawAddress
from ape.types.coverage import (
ContractCoverage,
Expand Down Expand Up @@ -98,7 +98,7 @@ def validate_multiplier(cls, value):
"""


TopicFilter = Sequence[Union[Optional[HexStr], List[Optional[HexStr]]]]
TopicFilter = Sequence[Union[Optional[HexStr], Sequence[Optional[HexStr]]]]


@dataclass
Expand Down
2 changes: 1 addition & 1 deletion src/ape/types/coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import requests
from ethpm_types import BaseModel
from ethpm_types.source import ContractSource, SourceLocation
from pydantic import NonNegativeInt, validator

from ape._pydantic_compat import NonNegativeInt, validator
from ape.logging import logger
from ape.utils.misc import get_current_timestamp_ms
from ape.version import version as ape_version
Expand Down
2 changes: 1 addition & 1 deletion src/ape/types/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
from ethpm_types.ast import SourceLocation
from ethpm_types.source import Closure, Content, Function, SourceStatement, Statement
from evm_trace.gas import merge_reports
from pydantic import Field
from rich.table import Table
from rich.tree import Tree

from ape._pydantic_compat import Field
from ape.types.address import AddressType
from ape.utils.basemodel import BaseInterfaceModel
from ape.utils.misc import is_evm_precompile, is_zero_hex
Expand Down
6 changes: 5 additions & 1 deletion src/ape/utils/abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,11 @@ def decode_output(self, values: Union[List, Tuple]) -> Any:

return self._decode(self.abi.outputs, values) if isinstance(self.abi, MethodABI) else None

def _decode(self, _types: Sequence[ABIType], values: Union[Sequence, Dict[str, Any]]):
def _decode(
self,
_types: Union[Sequence[ABIType]],
values: Union[Sequence, Dict[str, Any]],
):
if is_struct(_types):
return self._create_struct(_types[0], values)

Expand Down
3 changes: 1 addition & 2 deletions src/ape_compile/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from typing import Any, List, Optional

from pydantic import Field, validator

from ape import plugins
from ape._pydantic_compat import Field, validator
from ape.api import PluginConfig
from ape.logging import logger

Expand Down
2 changes: 1 addition & 1 deletion src/ape_ethereum/ecosystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
)
from ethpm_types import ContractType, HexBytes
from ethpm_types.abi import ABIType, ConstructorABI, EventABI, MethodABI
from pydantic import Field, validator

from ape._pydantic_compat import Field, validator
from ape.api import BlockAPI, EcosystemAPI, PluginConfig, ReceiptAPI, TransactionAPI
from ape.api.networks import LOCAL_NETWORK_NAME
from ape.contracts.base import ContractCall
Expand Down
2 changes: 1 addition & 1 deletion src/ape_ethereum/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from eth_utils import decode_hex, encode_hex, keccak, to_hex, to_int
from ethpm_types import ContractType, HexBytes
from ethpm_types.abi import EventABI, MethodABI
from pydantic import BaseModel, Field, root_validator, validator

from ape._pydantic_compat import BaseModel, Field, root_validator, validator
from ape.api import ReceiptAPI, TransactionAPI
from ape.contracts import ContractEvent
from ape.exceptions import OutOfGasError, SignatureError, TransactionError
Expand Down
2 changes: 1 addition & 1 deletion src/ape_geth/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
from geth.chain import initialize_chain # type: ignore
from geth.process import BaseGethProcess # type: ignore
from geth.wrapper import construct_test_chain_kwargs # type: ignore
from pydantic import Extra
from requests.exceptions import ConnectionError
from web3 import HTTPProvider, Web3
from web3.exceptions import ExtraDataLengthError
Expand All @@ -36,6 +35,7 @@
from web3.providers.auto import load_provider_from_environment
from yarl import URL

from ape._pydantic_compat import Extra
from ape.api import (
PluginConfig,
SubprocessProvider,
Expand Down
3 changes: 1 addition & 2 deletions src/ape_plugins/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
import sys
from typing import List, Optional, Tuple

from pydantic import root_validator

from ape.__modules__ import __modules__
from ape._pydantic_compat import root_validator
from ape.logging import ApeLogger
from ape.plugins import clean_plugin_name
from ape.utils import BaseInterfaceModel, cached_property, get_package_version, github_client
Expand Down
3 changes: 1 addition & 2 deletions src/ape_test/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from typing import Dict, List, NewType, Optional, Union

from pydantic import NonNegativeInt

from ape import plugins
from ape._pydantic_compat import NonNegativeInt
from ape.api import PluginConfig
from ape.api.networks import LOCAL_NETWORK_NAME
from ape.utils import DEFAULT_HD_PATH, DEFAULT_NUMBER_OF_TEST_ACCOUNTS, DEFAULT_TEST_MNEMONIC
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/conversion/test_encode_structs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import pytest
from ethpm_types import HexBytes
from ethpm_types.abi import MethodABI
from pydantic import BaseModel

from ape._pydantic_compat import BaseModel
from ape.types import AddressType

ABI = MethodABI.parse_obj(
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/test_contract_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import pytest
from eth_utils import is_checksum_address, to_hex
from ethpm_types import ContractType, HexBytes
from pydantic import BaseModel

from ape import Contract
from ape._pydantic_compat import BaseModel
from ape.api import TransactionAPI
from ape.contracts import ContractInstance
from ape.exceptions import (
Expand Down

0 comments on commit db49185

Please sign in to comment.