Skip to content

Commit 34130ce

Browse files
committed
Bump alysis dependency, relock, and fix ruff errors
1 parent 1871dcc commit 34130ce

17 files changed

Lines changed: 1350 additions & 927 deletions

docs/changelog.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@ Changelog
22
---------
33

44

5+
0.8.2 (in development)
6+
~~~~~~~~~~~~~~~~~~~~~~
7+
8+
Added
9+
^^^^^
10+
11+
- Hash methods for ABI types. (PR_81_)
12+
13+
14+
.. _PR_81: https://github.com/fjarri-eth/pons/pull/81
15+
16+
517
0.8.1 (2024-11-12)
618
~~~~~~~~~~~~~~~~~~
719

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,6 @@
7676
intersphinx_mapping = {
7777
"python": ("https://docs.python.org/3", None),
7878
"eth_account": ("https://eth-account.readthedocs.io/en/v0.13.0/", None),
79+
"eth_typing": ("https://eth-typing.readthedocs.io/en/stable/", None),
7980
"ethereum_rpc": ("https://ethereum-rpc.readthedocs.io/en/v0.1.0", None),
8081
}

pdm.lock

Lines changed: 1230 additions & 870 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pons/__init__.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
)
4848
from ._http_provider_server import HTTPProviderServer
4949
from ._local_provider import LocalProvider, SnapshotID
50-
from ._provider import HTTPProvider, ProtocolError, Provider, Unreachable
50+
from ._provider import HTTPProvider, ProtocolError, Provider, Unreachable, JSON
5151
from ._signer import AccountSigner, Signer
5252
from ._utils import get_create2_address, get_create_address
5353

@@ -71,34 +71,35 @@
7171
"ContractPanic",
7272
"CycleFallback",
7373
"DeployedContract",
74+
"EVMVersion",
7475
"Either",
7576
"Error",
76-
"EVMVersion",
77-
"LocalProvider",
7877
"Event",
7978
"EventFilter",
8079
"Fallback",
8180
"FallbackProvider",
8281
"FallbackStrategy",
8382
"FallbackStrategyFactory",
83+
"JSON",
8484
"HTTPProvider",
85+
"HTTPProviderServer",
86+
"LocalProvider",
8587
"Method",
8688
"MethodCall",
8789
"MultiMethod",
8890
"Mutability",
8991
"PriorityFallback",
9092
"ProtocolError",
91-
"ProviderError",
9293
"Provider",
94+
"ProviderError",
9395
"Receive",
9496
"RemoteError",
95-
"HTTPProviderServer",
9697
"Signer",
9798
"SnapshotID",
9899
"TransactionFailed",
99100
"Unreachable",
100101
"abi",
101102
"compile_contract_file",
102-
"get_create_address",
103103
"get_create2_address",
104+
"get_create_address",
104105
]

pons/_abi_types.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ def _denormalize(self, val: ABIType) -> int:
147147
def __eq__(self, other: object) -> bool:
148148
return isinstance(other, UInt) and self._bits == other._bits
149149

150+
def __hash__(self) -> int:
151+
return hash((type(self), self._bits))
152+
150153

151154
class Int(Type):
152155
"""Corresponds to the Solidity ``int<bits>`` type."""
@@ -183,6 +186,9 @@ def _denormalize(self, val: ABIType) -> int:
183186
def __eq__(self, other: object) -> bool:
184187
return isinstance(other, Int) and self._bits == other._bits
185188

189+
def __hash__(self) -> int:
190+
return hash((type(self), self._bits))
191+
186192

187193
class Bytes(Type):
188194
"""Corresponds to the Solidity ``bytes<size>`` type."""
@@ -199,8 +205,7 @@ def canonical_form(self) -> str:
199205
def _check_val(self, val: Any) -> bytes:
200206
if not isinstance(val, bytes):
201207
raise TypeError(
202-
f"`{self.canonical_form}` must correspond to a bytestring, "
203-
f"got {type(val).__name__}"
208+
f"`{self.canonical_form}` must correspond to a bytestring, got {type(val).__name__}"
204209
)
205210
if self._size is not None and len(val) != self._size:
206211
raise ValueError(f"Expected {self._size} bytes, got {len(val)}")
@@ -236,6 +241,9 @@ def decode_from_topic(self, val: bytes) -> None | bytes:
236241
def __eq__(self, other: object) -> bool:
237242
return isinstance(other, Bytes) and self._size == other._size
238243

244+
def __hash__(self) -> int:
245+
return hash((type(self), self._size))
246+
239247

240248
class AddressType(Type):
241249
"""
@@ -250,8 +258,7 @@ def canonical_form(self) -> str:
250258
def _normalize(self, val: Any) -> str:
251259
if not isinstance(val, Address):
252260
raise TypeError(
253-
f"`address` must correspond to an `Address`-type value, "
254-
f"got {type(val).__name__}"
261+
f"`address` must correspond to an `Address`-type value, got {type(val).__name__}"
255262
)
256263
return val.checksum
257264

@@ -263,6 +270,9 @@ def _denormalize(self, val: ABIType) -> Address:
263270
def __eq__(self, other: object) -> bool:
264271
return isinstance(other, AddressType)
265272

273+
def __hash__(self) -> int:
274+
return hash(type(self))
275+
266276

267277
class String(Type):
268278
"""Corresponds to the Solidity ``string`` type."""
@@ -299,6 +309,9 @@ def decode_from_topic(self, _val: bytes) -> None:
299309
def __eq__(self, other: object) -> bool:
300310
return isinstance(other, String)
301311

312+
def __hash__(self) -> int:
313+
return hash(type(self))
314+
302315

303316
class Bool(Type):
304317
"""Corresponds to the Solidity ``bool`` type."""
@@ -323,6 +336,9 @@ def _denormalize(self, val: ABIType) -> bool:
323336
def __eq__(self, other: object) -> bool:
324337
return isinstance(other, Bool)
325338

339+
def __hash__(self) -> int:
340+
return hash(type(self))
341+
326342

327343
class Array(Type):
328344
"""Corresponds to the Solidity array (``[<size>]``) type."""
@@ -366,6 +382,9 @@ def __eq__(self, other: object) -> bool:
366382
and self._size == other._size
367383
)
368384

385+
def __hash__(self) -> int:
386+
return hash((type(self), self._element_type, self._size))
387+
369388

370389
class Struct(Type):
371390
"""Corresponds to the Solidity struct type."""
@@ -426,6 +445,9 @@ def __eq__(self, other: object) -> bool:
426445
and list(self._fields) == list(other._fields)
427446
)
428447

448+
def __hash__(self) -> int:
449+
return hash((type(self), tuple(self._fields.items())))
450+
429451

430452
_UINT_RE = re.compile(r"uint(\d+)")
431453
_INT_RE = re.compile(r"int(\d+)")

pons/_client.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22
from contextlib import asynccontextmanager, contextmanager
33
from dataclasses import dataclass
44
from enum import Enum
5-
from typing import Any, ParamSpec, TypeVar, cast
5+
from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar, cast
66

77
import anyio
88
from compages import StructuringError
99
from ethereum_rpc import (
10-
JSON,
1110
Address,
1211
Amount,
1312
Block,
@@ -46,6 +45,9 @@
4645
from ._provider import InvalidResponse, Provider, ProviderSession
4746
from ._signer import Signer
4847

48+
if TYPE_CHECKING: # pragma: no cover
49+
from eth_account.types import TransactionDictType
50+
4951

5052
@dataclass
5153
class BlockFilter:
@@ -329,7 +331,7 @@ async def eth_get_transaction_by_hash(self, tx_hash: TxHash) -> None | TxInfo:
329331
# Need an explicit cast, mypy doesn't work with union types correctly.
330332
# See https://github.com/python/mypy/issues/16935
331333
return cast(
332-
None | TxInfo,
334+
"None | TxInfo",
333335
await rpc_call(
334336
self._provider_session,
335337
"eth_getTransactionByHash",
@@ -343,7 +345,7 @@ async def eth_get_transaction_receipt(self, tx_hash: TxHash) -> None | TxReceipt
343345
# Need an explicit cast, mypy doesn't work with union types correctly.
344346
# See https://github.com/python/mypy/issues/16935
345347
return cast(
346-
None | TxReceipt,
348+
"None | TxReceipt",
347349
await rpc_call(
348350
self._provider_session,
349351
"eth_getTransactionReceipt",
@@ -505,7 +507,7 @@ async def eth_get_block_by_hash(
505507
# Need an explicit cast, mypy doesn't work with union types correctly.
506508
# See https://github.com/python/mypy/issues/16935
507509
return cast(
508-
None | BlockInfo,
510+
"None | BlockInfo",
509511
await rpc_call(
510512
self._provider_session,
511513
"eth_getBlockByHash",
@@ -522,7 +524,7 @@ async def eth_get_block_by_number(
522524
# Need an explicit cast, mypy doesn't work with union types correctly.
523525
# See https://github.com/python/mypy/issues/16935
524526
return cast(
525-
None | BlockInfo,
527+
"None | BlockInfo",
526528
await rpc_call(
527529
self._provider_session,
528530
"eth_getBlockByNumber",
@@ -552,7 +554,7 @@ async def broadcast_transfer(
552554
max_tip = min(Amount.gwei(1), max_gas_price)
553555
nonce = await self.eth_get_transaction_count(signer.address, BlockLabel.PENDING)
554556
tx = cast(
555-
dict[str, JSON],
557+
"TransactionDictType",
556558
unstructure(
557559
Type2Transaction(
558560
chain_id=chain_id,
@@ -622,7 +624,7 @@ async def deploy(
622624
max_tip = min(Amount.gwei(1), max_gas_price)
623625
nonce = await self.eth_get_transaction_count(signer.address, BlockLabel.PENDING)
624626
tx = cast(
625-
dict[str, JSON],
627+
"TransactionDictType",
626628
unstructure(
627629
Type2Transaction(
628630
chain_id=chain_id,
@@ -680,7 +682,7 @@ async def broadcast_transact(
680682
max_tip = min(Amount.gwei(1), max_gas_price)
681683
nonce = await self.eth_get_transaction_count(signer.address, BlockLabel.PENDING)
682684
tx = cast(
683-
dict[str, JSON],
685+
"TransactionDictType",
684686
unstructure(
685687
Type2Transaction(
686688
chain_id=chain_id,
@@ -854,7 +856,7 @@ async def iter_blocks(self, poll_interval: int = 1) -> AsyncIterator[BlockHash]:
854856
for block_hash in block_hashes:
855857
# We can't ensure it statically, since `eth_getFilterChanges` return type depends
856858
# on the filter passed to it.
857-
yield cast(BlockHash, block_hash)
859+
yield cast("BlockHash", block_hash)
858860
await anyio.sleep(poll_interval)
859861

860862
async def iter_pending_transactions(self, poll_interval: int = 1) -> AsyncIterator[TxHash]:
@@ -865,7 +867,7 @@ async def iter_pending_transactions(self, poll_interval: int = 1) -> AsyncIterat
865867
for tx_hash in tx_hashes:
866868
# We can't ensure it statically, since `eth_getFilterChanges` return type depends
867869
# on the filter passed to it.
868-
yield cast(TxHash, tx_hash)
870+
yield cast("TxHash", tx_hash)
869871
await anyio.sleep(poll_interval)
870872

871873
async def iter_events(
@@ -891,5 +893,5 @@ async def iter_events(
891893
for log_entry in log_entries:
892894
# We can't ensure it statically, since `eth_getFilterChanges` return type depends
893895
# on the filter passed to it.
894-
yield event_filter.decode_log_entry(cast(LogEntry, log_entry))
896+
yield event_filter.decode_log_entry(cast("LogEntry", log_entry))
895897
await anyio.sleep(poll_interval)

pons/_contract_abi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ def _add_method(self, method: Method) -> None:
437437
def with_method(self, method: Method) -> "MultiMethod":
438438
"""Returns a new ``MultiMethod`` with the given method included."""
439439
new_mm = MultiMethod(*self._methods.values())
440-
new_mm._add_method(method) # noqa: SLF001
440+
new_mm._add_method(method)
441441
return new_mm
442442

443443
def __call__(self, *args: Any, **kwds: Any) -> "MethodCall":

pons/_fallback_provider.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ def __init__(self, weights: list[int]):
3939
def get_provider_order(self) -> list[int]:
4040
if self._counter == self._weights[0]:
4141
self._counter = 0
42-
self._providers = self._providers[1:] + [self._providers[0]]
43-
self._weights = self._weights[1:] + [self._weights[0]]
42+
self._providers = [*self._providers[1:], self._providers[0]]
43+
self._weights = [*self._weights[1:], self._weights[0]]
4444

4545
self._counter += 1
4646
return list(self._providers)

pons/_http_provider_server.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@
1010
from starlette.requests import Request
1111
from starlette.responses import JSONResponse, Response
1212
from starlette.routing import Route
13-
from trio_typing import TaskStatus
1413

1514
from ._provider import JSON, HTTPProvider, Provider
1615

1716

1817
def parse_request(request: JSON) -> tuple[JSON, str, list[JSON]]:
19-
request = cast(dict[str, JSON], request)
18+
request = cast("dict[str, JSON]", request)
2019
request_id = request["id"]
2120
method = request["method"]
2221
if not isinstance(method, str):
@@ -77,7 +76,7 @@ def make_app(provider: Provider) -> ASGIFramework:
7776

7877
# We don't have a typing package shared between Starlette and Hypercorn,
7978
# so this will have to do
80-
return cast(ASGIFramework, app)
79+
return cast("ASGIFramework", app)
8180

8281

8382
class HTTPProviderServer:
@@ -94,7 +93,7 @@ def __init__(self, provider: Provider, host: str = "127.0.0.1", port: int = 8888
9493
self._shutdown_finished = trio.Event()
9594
self.http_provider = HTTPProvider(f"http://{self._host}:{self._port}")
9695

97-
async def __call__(self, *, task_status: TaskStatus[None] = trio.TASK_STATUS_IGNORED) -> None:
96+
async def __call__(self, *, task_status: trio.TaskStatus = trio.TASK_STATUS_IGNORED) -> None:
9897
"""
9998
Starts the server in an external event loop.
10099
Useful for the cases when it needs to run in parallel with other servers or clients.
@@ -106,7 +105,11 @@ async def __call__(self, *, task_status: TaskStatus[None] = trio.TASK_STATUS_IGN
106105
config.worker_class = "trio"
107106
app = make_app(self._provider)
108107
await serve(
109-
app, config, shutdown_trigger=self._shutdown_event.wait, task_status=task_status
108+
app,
109+
config,
110+
shutdown_trigger=self._shutdown_event.wait,
111+
# That's what hypercorn API declares, but it's the same type as `trio.TaskStatus`
112+
task_status=cast("trio._core._run._TaskStatus", task_status), # noqa: SLF001
110113
)
111114
self._shutdown_finished.set()
112115

pons/_provider.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
from abc import ABC, abstractmethod
2-
from collections.abc import AsyncIterator, Mapping
2+
from collections.abc import AsyncIterator, Mapping, Sequence
33
from contextlib import asynccontextmanager
44
from http import HTTPStatus
55
from json import JSONDecodeError
66
from typing import cast
77

88
import httpx
99
from compages import StructuringError
10-
from ethereum_rpc import JSON, RPCError, structure
10+
from ethereum_rpc import RPCError, structure
11+
12+
JSON = None | bool | int | float | str | Sequence["JSON"] | Mapping[str, "JSON"]
13+
"""Values serializable to JSON."""
1114

1215

1316
class InvalidResponse(Exception):
@@ -129,7 +132,7 @@ async def rpc(self, method: str, *args: JSON) -> JSON:
129132

130133
if not isinstance(response_json, Mapping):
131134
raise InvalidResponse(f"RPC response must be a dictionary, got: {response_json}")
132-
response_json = cast(Mapping[str, JSON], response_json)
135+
response_json = cast("Mapping[str, JSON]", response_json)
133136

134137
# Note that the Eth-side errors (e.g. transaction having been reverted)
135138
# will have the HTTP status 200, so we are checking for the "error" field first.

0 commit comments

Comments
 (0)