Skip to content

Commit

Permalink
Bump minimum Python version to 3.10
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri committed Mar 5, 2024
1 parent ba262f6 commit d11f17f
Show file tree
Hide file tree
Showing 25 changed files with 412 additions and 727 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
matrix:
# synchronized with `.readthedocs.yml`
python-version: ["3.8"]
python-version: ["3.10"]

steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8"]
python-version: ["3.10"]
steps:
- uses: actions/checkout@v3
- uses: chartboost/ruff-action@v1
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
Expand All @@ -38,7 +38,7 @@ jobs:
run: |
pdm run py.test --cov=pons --cov-report=xml tests
- name: Upload coverage
if: matrix.python-version == '3.11'
if: matrix.python-version == '3.10'
run: |
curl -Os https://uploader.codecov.io/latest/linux/codecov
chmod +x codecov
Expand All @@ -49,7 +49,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8"]
python-version: ["3.10"]

steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
python: "3.10"

# Build documentation in the docs/ directory with Sphinx
sphinx:
Expand Down
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Changed
- ``AccountSigner`` takes ``LocalSigner`` specifically and not just any ``BaseSigner``. (PR_62_)
- ``ClientSession.estimate_transact()`` and ``estimate_deploy()`` now require a ``sender_address`` parameter. (PR_62_)
- Switched to ``alysis`` from ``eth-tester`` for the backend of ``LocalProvider``. (PR_70_)
- Bumped the minimum Python version to 3.10. (PR_72_)


Added
Expand Down Expand Up @@ -63,6 +64,7 @@ Fixed
.. _PR_67: https://github.com/fjarri/pons/pull/67
.. _PR_68: https://github.com/fjarri/pons/pull/68
.. _PR_70: https://github.com/fjarri/pons/pull/70
.. _PR_72: https://github.com/fjarri/pons/pull/72


0.7.0 (09-07-2023)
Expand Down
6 changes: 0 additions & 6 deletions mypy.ini

This file was deleted.

583 changes: 172 additions & 411 deletions pdm.lock

Large diffs are not rendered by default.

84 changes: 31 additions & 53 deletions pons/_abi_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,13 @@

import re
from abc import ABC, abstractmethod
from collections.abc import Iterable, Mapping, Sequence
from functools import cached_property
from typing import (
Any,
Dict,
Iterable,
List,
Mapping,
Optional,
Sequence,
Tuple,
Union,
cast,
)

import eth_abi # type: ignore[import-untyped]
from eth_abi.exceptions import DecodingError # type: ignore[import-untyped]
from types import EllipsisType
from typing import Any

import eth_abi
from eth_abi.exceptions import DecodingError
from eth_utils import keccak

from ._entities import Address
Expand All @@ -34,28 +25,13 @@ class ABIDecodingError(Exception):
"""Raised on an error when decoding a value in an Eth ABI encoded bytestring."""


ABIType = Union[int, str, bytes, bool, List["ABIType"]]
ABIType = int | str | bytes | bool | list["ABIType"]
"""
Represents the argument type that can be received from ``eth_abi.decode()``,
or passed to ``eth_abi.encode()``.
"""


def encode_typed(types: Iterable[str], args: Iterable[ABIType]) -> bytes:
# ``eth_abi.encode()`` does not have type annotations.
# This is a typed wrapper (easier than making custom stubs).
# Remove when typing is added in ``eth_abi``.
return cast(bytes, eth_abi.encode(types, args))


def decode_typed(types: Iterable[str], data: bytes) -> Tuple[ABIType, ...]:
# ``eth_abi.decode()`` does not have type annotations.
# This is a typed wrapper (easier than making custom stubs).
# Remove when typing is added in ``eth_abi``.
decoded = eth_abi.decode(types, data)
return cast(Tuple[ABIType, ...], decoded)


class Type(ABC):
"""The base type for Solidity types."""

Expand All @@ -80,7 +56,7 @@ def _denormalize(self, val: ABIType) -> Any:

def encode(self, val: Any) -> bytes:
"""Encodes the given value in the contract ABI format."""
return encode_typed([self.canonical_form], [val])
return eth_abi.encode([self.canonical_form], [val])

def encode_to_topic(self, val: Any) -> bytes:
"""Encodes the given value as an event topic."""
Expand All @@ -107,7 +83,7 @@ def _encode_to_topic_inner(self, val: Any) -> bytes:
# May be overridden.
return self.encode(val)

def decode_from_topic(self, val: bytes) -> Optional[Any]:
def decode_from_topic(self, val: bytes) -> Any | None:
"""
Decodes an encoded topic.
Returns ``None`` if the decoding is impossible
Expand All @@ -119,12 +95,12 @@ def decode_from_topic(self, val: bytes) -> Optional[Any]:

# By default it's just the decoding of the value type.
# May be overridden.
return self._denormalize(decode_typed([self.canonical_form], val)[0])
return self._denormalize(eth_abi.decode([self.canonical_form], val)[0])

def __str__(self) -> str:
return self.canonical_form

def __getitem__(self, array_size: Union[int, Any]) -> "Array":
def __getitem__(self, array_size: int | EllipsisType) -> "Array":
# In Py3.10 they added EllipsisType which would work better here.
# For now, relying on the documentation.
if isinstance(array_size, int):
Expand Down Expand Up @@ -213,7 +189,7 @@ def __eq__(self, other: object) -> bool:
class Bytes(Type):
"""Corresponds to the Solidity ``bytes<size>`` type."""

def __init__(self, size: Optional[int] = None):
def __init__(self, size: None | int = None):
if size is not None and (size <= 0 or size > MAX_BYTES_SIZE):
raise ValueError(f"Incorrect `bytes` size: {size}")
self._size = size
Expand Down Expand Up @@ -253,7 +229,7 @@ def _encode_to_topic_inner(self, val: bytes) -> bytes:
# Sized `bytes` is a value type, falls back to the base implementation.
return super()._encode_to_topic_inner(val)

def decode_from_topic(self, val: bytes) -> Optional[bytes]:
def decode_from_topic(self, val: bytes) -> None | bytes:
if self._size is None:
# Cannot recover a hashed value.
return None
Expand Down Expand Up @@ -353,7 +329,7 @@ def __eq__(self, other: object) -> bool:
class Array(Type):
"""Corresponds to the Solidity array (``[<size>]``) type."""

def __init__(self, element_type: Type, size: Optional[int] = None):
def __init__(self, element_type: Type, size: None | int = None):
self._element_type = element_type
self._size = size

Expand All @@ -370,10 +346,10 @@ def _check_val(self, val: Any) -> Sequence[Any]:
raise ValueError(f"Expected {self._size} elements, got {len(val)}")
return val

def _normalize(self, val: Any) -> List[ABIType]:
def _normalize(self, val: Any) -> list[ABIType]:
return [self._element_type._normalize(item) for item in self._check_val(val)]

def _denormalize(self, val: ABIType) -> List[ABIType]:
def _denormalize(self, val: ABIType) -> list[ABIType]:
return [self._element_type._denormalize(item) for item in self._check_val(val)]

def _encode_to_topic_outer(self, val: Any) -> bytes:
Expand Down Expand Up @@ -410,29 +386,31 @@ def _check_val(self, val: Any) -> Sequence[Any]:
raise ValueError(f"Expected {len(self._fields)} elements, got {len(val)}")
return val

def _normalize(self, val: Any) -> List[ABIType]:
def _normalize(self, val: Any) -> list[ABIType]:
if isinstance(val, Mapping):
if val.keys() != self._fields.keys():
raise ValueError(
f"Expected fields {list(self._fields.keys())}, got {list(val.keys())}"
)
return [tp._normalize(val[name]) for name, tp in self._fields.items()]
return [
tp._normalize(item) for item, tp in zip(self._check_val(val), self._fields.values())
tp._normalize(item)
for item, tp in zip(self._check_val(val), self._fields.values(), strict=True)
]

def _denormalize(self, val: ABIType) -> Dict[str, ABIType]:
def _denormalize(self, val: ABIType) -> dict[str, ABIType]:
return {
name: tp._denormalize(item)
for item, (name, tp) in zip(self._check_val(val), self._fields.items())
for item, (name, tp) in zip(self._check_val(val), self._fields.items(), strict=True)
}

def _encode_to_topic_outer(self, val: Any) -> bytes:
return keccak(self._encode_to_topic_inner(val))

def _encode_to_topic_inner(self, val: Any) -> bytes:
return b"".join(
tp._encode_to_topic_inner(elem) for elem, tp in zip(val, self._fields.values())
tp._encode_to_topic_inner(elem)
for elem, tp in zip(val, self._fields.values(), strict=True)
)

def decode_from_topic(self, _val: Any) -> None:
Expand Down Expand Up @@ -502,7 +480,7 @@ def dispatch_type(abi_entry: Mapping[str, Any]) -> Type:
return type_from_abi_string(element_type_name)


def dispatch_types(abi_entry: Iterable[Dict[str, Any]]) -> Union[List[Type], Dict[str, Type]]:
def dispatch_types(abi_entry: Iterable[dict[str, Any]]) -> list[Type] | dict[str, Type]:
names = [entry["name"] for entry in abi_entry]

# Unnamed arguments; treat as positional arguments
Expand All @@ -518,21 +496,21 @@ def dispatch_types(abi_entry: Iterable[Dict[str, Any]]) -> Union[List[Type], Dic
return {entry["name"]: dispatch_type(entry) for entry in abi_entry}


def encode_args(*types_and_args: Tuple[Type, Any]) -> bytes:
def encode_args(*types_and_args: tuple[Type, Any]) -> bytes:
if types_and_args:
types, args = zip(*types_and_args)
types, args = zip(*types_and_args, strict=True)
else:
types, args = (), ()
return encode_typed(
return eth_abi.encode(
[tp.canonical_form for tp in types],
tuple(tp._normalize(arg) for tp, arg in zip(types, args)),
tuple(tp._normalize(arg) for tp, arg in zip(types, args, strict=True)),
)


def decode_args(types: Iterable[Type], data: bytes) -> Tuple[ABIType, ...]:
def decode_args(types: Iterable[Type], data: bytes) -> tuple[ABIType, ...]:
canonical_types = [tp.canonical_form for tp in types]
try:
values = decode_typed(canonical_types, data)
values = eth_abi.decode(canonical_types, data)
except DecodingError as exc:
# wrap possible `eth_abi` errors
signature = "(" + ",".join(canonical_types) + ")"
Expand All @@ -541,4 +519,4 @@ def decode_args(types: Iterable[Type], data: bytes) -> Tuple[ABIType, ...]:
)
raise ABIDecodingError(message) from exc

return tuple(tp._denormalize(value) for tp, value in zip(types, values))
return tuple(tp._denormalize(value) for tp, value in zip(types, values, strict=True))
Loading

0 comments on commit d11f17f

Please sign in to comment.