Skip to content

Commit a5caed7

Browse files
committed
refactor: Better exception def
1 parent fedd25e commit a5caed7

File tree

2 files changed

+53
-55
lines changed

2 files changed

+53
-55
lines changed

src/ethereum_test_fixtures/pre_alloc_groups.py

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,19 @@
11
"""Pre-allocation group models for test fixture generation."""
22

3+
import json
34
from pathlib import Path
45
from typing import Any, Dict, List
56

67
from filelock import FileLock
78
from pydantic import Field, computed_field
89

9-
from ethereum_test_base_types import Account, Address, CamelModel, EthereumTestRootModel
10+
from ethereum_test_base_types import CamelModel, EthereumTestRootModel
1011
from ethereum_test_forks import Fork
1112
from ethereum_test_types import Alloc, Environment
12-
from ethereum_test_types.account_types import CollisionError
1313

1414
from .blockchain import FixtureHeader
1515

1616

17-
class Collision(CamelModel):
18-
"""Model to describe two different accounts set to the same addres by two different tests."""
19-
20-
address: Address
21-
account_1: Account
22-
account_2: Account
23-
24-
def exception(self) -> CollisionError:
25-
"""Return the collision exception."""
26-
return CollisionError(
27-
address=self.address, account_1=self.account_1, account_2=self.account_2
28-
)
29-
30-
3117
class PreAllocGroup(CamelModel):
3218
"""
3319
Pre-allocation group for tests with identical Environment and fork values.
@@ -83,14 +69,14 @@ def to_file(self, file: Path) -> None:
8369
# and is not reported to the master thread.
8470
# We signal here that the groups created contain a collision.
8571
collision_file_path = file.with_suffix(".fail")
86-
collision_exception = Collision(
72+
collision_exception = Alloc.CollisionError(
8773
address=account,
8874
account_1=existing_account,
8975
account_2=new_account,
9076
)
9177
with open(collision_file_path, "w") as f:
92-
f.write(collision_exception.model_dump_json(indent=2))
93-
raise collision_exception.exception()
78+
f.write(json.dumps(collision_exception.to_json()))
79+
raise collision_exception
9480
self.test_ids.extend(previous_pre_alloc_group.test_ids)
9581

9682
with open(file, "w") as f:
@@ -112,7 +98,7 @@ def from_folder(cls, folder: Path) -> "PreAllocGroups":
11298
# First check for collision failures
11399
for fail_file in folder.glob("*.fail"):
114100
with open(fail_file) as f:
115-
raise Collision.model_validate_json(f.read()).exception()
101+
raise Alloc.CollisionError.from_json(json.loads(f.read()))
116102
data = {}
117103
for file in folder.glob("*.json"):
118104
with open(file) as f:

src/ethereum_test_types/account_types.py

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
"""Account-related types for Ethereum tests."""
22

3+
import json
34
from dataclasses import dataclass, field
45
from enum import Enum, auto
5-
from typing import Dict, List, Literal, Optional, Tuple
6+
from typing import Any, Dict, List, Literal, Optional, Self, Tuple
67

78
from coincurve.keys import PrivateKey
89
from ethereum_types.bytes import Bytes20
910
from ethereum_types.numeric import U256, Bytes32, Uint
1011
from pydantic import PrivateAttr
11-
from typing_extensions import Self
1212

1313
from ethereum_test_base_types import (
1414
Account,
@@ -133,25 +133,6 @@ def copy(self) -> Self:
133133
return self.__class__(Address(self), key=self.key, nonce=self.nonce)
134134

135135

136-
class CollisionError(Exception):
137-
"""Exception raised when two tests describe different accounts at the same address."""
138-
139-
def __init__(self, address: Address, account_1: Account, account_2: Account):
140-
"""Initialize the exception."""
141-
self.address = address
142-
self.account_1 = account_1
143-
self.account_2 = account_2
144-
145-
def __str__(self) -> str:
146-
"""Exception message."""
147-
return (
148-
"Overlapping key defining different accounts detected:\n"
149-
f"address={self.address}:\n"
150-
f"account_1={self.account_1.model_dump_json(indent=2)}\n"
151-
f"account_2={self.account_2.model_dump_json(indent=2)}"
152-
)
153-
154-
155136
class Alloc(BaseAlloc):
156137
"""Allocation of accounts in the state, pre and post test execution."""
157138

@@ -164,12 +145,6 @@ class UnexpectedAccountError(Exception):
164145
address: Address
165146
account: Account | None
166147

167-
def __init__(self, address: Address, account: Account | None, *args):
168-
"""Initialize the exception."""
169-
super().__init__(args)
170-
self.address = address
171-
self.account = account
172-
173148
def __str__(self):
174149
"""Print exception string."""
175150
return f"unexpected account in allocation {self.address}: {self.account}"
@@ -180,15 +155,50 @@ class MissingAccountError(Exception):
180155

181156
address: Address
182157

183-
def __init__(self, address: Address, *args):
184-
"""Initialize the exception."""
185-
super().__init__(args)
186-
self.address = address
187-
188158
def __str__(self):
189159
"""Print exception string."""
190160
return f"Account missing from allocation {self.address}"
191161

162+
@dataclass(kw_only=True)
163+
class CollisionError(Exception):
164+
"""Different accounts at the same address."""
165+
166+
address: Address
167+
account_1: Account | None
168+
account_2: Account | None
169+
170+
def to_json(self) -> Dict[str, Any]:
171+
"""Dump to json object."""
172+
return {
173+
"address": self.address.hex(),
174+
"account_1": self.account_1.model_dump(mode="json")
175+
if self.account_1 is not None
176+
else None,
177+
"account_2": self.account_2.model_dump(mode="json")
178+
if self.account_2 is not None
179+
else None,
180+
}
181+
182+
@classmethod
183+
def from_json(cls, obj: Dict[str, Any]) -> Self:
184+
"""Parse from a json dict."""
185+
return cls(
186+
address=Address(obj["address"]),
187+
account_1=Account.model_validate(obj["account_1"])
188+
if obj["account_1"] is not None
189+
else None,
190+
account_2=Account.model_validate(obj["account_2"])
191+
if obj["account_2"] is not None
192+
else None,
193+
)
194+
195+
def __str__(self) -> str:
196+
"""Print exception string."""
197+
return (
198+
"Overlapping key defining different accounts detected:\n"
199+
f"{json.dumps(self.to_json(), indent=2)}"
200+
)
201+
192202
class KeyCollisionMode(Enum):
193203
"""Mode for handling key collisions when merging allocations."""
194204

@@ -216,7 +226,7 @@ def merge(
216226
account_1 = alloc_1[key]
217227
account_2 = alloc_2[key]
218228
if account_1 != account_2:
219-
raise CollisionError(
229+
raise Alloc.CollisionError(
220230
address=key,
221231
account_1=account_1,
222232
account_2=account_2,
@@ -309,15 +319,17 @@ def verify_post_alloc(self, got_alloc: "Alloc"):
309319
if account is None:
310320
# Account must not exist
311321
if address in got_alloc.root and got_alloc.root[address] is not None:
312-
raise Alloc.UnexpectedAccountError(address, got_alloc.root[address])
322+
raise Alloc.UnexpectedAccountError(
323+
address=address, account=got_alloc.root[address]
324+
)
313325
else:
314326
if address in got_alloc.root:
315327
got_account = got_alloc.root[address]
316328
assert isinstance(got_account, Account)
317329
assert isinstance(account, Account)
318330
account.check_alloc(address, got_account)
319331
else:
320-
raise Alloc.MissingAccountError(address)
332+
raise Alloc.MissingAccountError(address=address)
321333

322334
def deploy_contract(
323335
self,

0 commit comments

Comments
 (0)