Skip to content

Commit 8b4b4ce

Browse files
committed
Remove AttributeDict method formatters and opt for middleware
- ``AttributeDict`` leads to typing complications when properties are accessed via attribute, rather than key / value. This doesn't complicate too much since a user can opt for key / value every time but it isn't ideal to provide an option that breaks typing. Those who wish to turn off all recursive conversion to ``AttributeDict`` can simply remove the ``attrdict_middleware`` (or ``async_attrdict_middleware``) and not worry about possible typing complications. - In order for this to work for async as well, support for ``async_attrdict_middleware` was introduced. This is also now one of the default middlewares for the ``EthereumTesterProvider`` as this was the default behavior achieved via the result formatters.
1 parent 58a8b2c commit 8b4b4ce

File tree

11 files changed

+166
-107
lines changed

11 files changed

+166
-107
lines changed

tests/core/contracts/test_extracting_event_data.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -579,9 +579,8 @@ def test_event_rich_log(
579579
raise Exception("Unreachable!")
580580

581581
assert rich_log["args"] == expected_args
582-
assert rich_log.args == expected_args
583582
for arg in expected_args:
584-
assert getattr(rich_log.args, arg) == expected_args[arg]
583+
assert rich_log["args"][arg] == expected_args[arg]
585584
assert rich_log["blockHash"] == txn_receipt["blockHash"]
586585
assert rich_log["blockNumber"] == txn_receipt["blockNumber"]
587586
assert rich_log["transactionIndex"] == txn_receipt["transactionIndex"]
@@ -842,9 +841,8 @@ def test_event_rich_log_non_strict(
842841
raise Exception("Unreachable!")
843842

844843
assert rich_log["args"] == expected_args
845-
assert rich_log.args == expected_args
846844
for arg in expected_args:
847-
assert getattr(rich_log.args, arg) == expected_args[arg]
845+
assert rich_log["args"][arg] == expected_args[arg]
848846
assert rich_log["blockHash"] == txn_receipt["blockHash"]
849847
assert rich_log["blockNumber"] == txn_receipt["blockNumber"]
850848
assert rich_log["transactionIndex"] == txn_receipt["transactionIndex"]
@@ -880,9 +878,8 @@ def test_event_rich_log_with_byte_args(
880878
"arg1": [b"54"],
881879
}
882880
assert rich_log["args"] == expected_args
883-
assert rich_log.args == expected_args
884881
for arg in expected_args:
885-
assert getattr(rich_log.args, arg) == expected_args[arg]
882+
assert rich_log["args"][arg] == expected_args[arg]
886883
assert rich_log["blockHash"] == txn_receipt["blockHash"]
887884
assert rich_log["blockNumber"] == txn_receipt["blockNumber"]
888885
assert rich_log["transactionIndex"] == txn_receipt["transactionIndex"]
@@ -976,12 +973,12 @@ def test_get_all_entries_with_nested_tuple_event(w3, emitter):
976973

977974
log_entry = entries[0]
978975

979-
assert log_entry.args == {"arg0": 1, "arg1": (2, 3, (4,))}
980-
assert log_entry.event == "LogStructArgs"
981-
assert log_entry.blockHash == txn_receipt["blockHash"]
982-
assert log_entry.blockNumber == txn_receipt["blockNumber"]
983-
assert log_entry.transactionIndex == txn_receipt["transactionIndex"]
984-
assert is_same_address(log_entry.address, emitter.address)
976+
assert log_entry["args"] == {"arg0": 1, "arg1": (2, 3, (4,))}
977+
assert log_entry["event"] == "LogStructArgs"
978+
assert log_entry["blockHash"] == txn_receipt["blockHash"]
979+
assert log_entry["blockNumber"] == txn_receipt["blockNumber"]
980+
assert log_entry["transactionIndex"] == txn_receipt["transactionIndex"]
981+
assert is_same_address(log_entry["address"], emitter.address)
985982

986983

987984
def test_get_all_entries_with_nested_tuple_event_non_strict(
@@ -1002,9 +999,9 @@ def test_get_all_entries_with_nested_tuple_event_non_strict(
1002999

10031000
log_entry = entries[0]
10041001

1005-
assert log_entry.args == {"arg0": 1, "arg1": (2, 3, (4,))}
1006-
assert log_entry.event == "LogStructArgs"
1007-
assert log_entry.blockHash == txn_receipt["blockHash"]
1008-
assert log_entry.blockNumber == txn_receipt["blockNumber"]
1009-
assert log_entry.transactionIndex == txn_receipt["transactionIndex"]
1010-
assert is_same_address(log_entry.address, non_strict_emitter.address)
1002+
assert log_entry["args"] == {"arg0": 1, "arg1": (2, 3, (4,))}
1003+
assert log_entry["event"] == "LogStructArgs"
1004+
assert log_entry["blockHash"] == txn_receipt["blockHash"]
1005+
assert log_entry["blockNumber"] == txn_receipt["blockNumber"]
1006+
assert log_entry["transactionIndex"] == txn_receipt["transactionIndex"]
1007+
assert is_same_address(log_entry["address"], non_strict_emitter.address)

web3/_utils/events.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,10 @@ def get_event_abi_types_for_decoding(
208208

209209
@curry
210210
def get_event_data(
211-
abi_codec: ABICodec, event_abi: ABIEvent, log_entry: LogReceipt
211+
abi_codec: ABICodec,
212+
event_abi: ABIEvent,
213+
log_entry: LogReceipt,
214+
use_attribute_dicts: bool = False,
212215
) -> EventData:
213216
"""
214217
Given an event ABI and a log entry for that event, return the decoded
@@ -269,18 +272,21 @@ def get_event_data(
269272
)
270273
)
271274

272-
event_data = {
273-
"args": event_args,
274-
"event": event_abi["name"],
275-
"logIndex": log_entry["logIndex"],
276-
"transactionIndex": log_entry["transactionIndex"],
277-
"transactionHash": log_entry["transactionHash"],
278-
"address": log_entry["address"],
279-
"blockHash": log_entry["blockHash"],
280-
"blockNumber": log_entry["blockNumber"],
281-
}
275+
event_data = EventData(
276+
args=event_args,
277+
event=event_abi["name"],
278+
logIndex=log_entry["logIndex"],
279+
transactionIndex=log_entry["transactionIndex"],
280+
transactionHash=log_entry["transactionHash"],
281+
address=log_entry["address"],
282+
blockHash=log_entry["blockHash"],
283+
blockNumber=log_entry["blockNumber"],
284+
)
285+
286+
if use_attribute_dicts:
287+
return cast(EventData, AttributeDict.recursive(event_data))
282288

283-
return cast(EventData, AttributeDict.recursive(event_data))
289+
return event_data
284290

285291

286292
@to_tuple

web3/_utils/method_formatters.py

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
is_0x_prefixed,
3333
is_address,
3434
is_bytes,
35-
is_dict,
3635
is_integer,
3736
is_null,
3837
is_string,
@@ -88,6 +87,7 @@
8887
)
8988
from web3.datastructures import (
9089
AttributeDict,
90+
ReadableAttributeDict,
9191
)
9292
from web3.exceptions import (
9393
BlockNotFound,
@@ -155,6 +155,19 @@ def is_attrdict(val: Any) -> bool:
155155
not_attrdict = complement(is_attrdict)
156156

157157

158+
@curry
159+
def type_aware_apply_formatters_to_dict(
160+
formatters: Formatters,
161+
value: Union[AttributeDict[str, Any], Dict[str, Any]],
162+
) -> Union[ReadableAttributeDict[str, Any], Dict[str, Any]]:
163+
formatted_dict: Dict[str, Any] = apply_formatters_to_dict(formatters, dict(value))
164+
return (
165+
AttributeDict.recursive(formatted_dict)
166+
if is_attrdict(value)
167+
else formatted_dict
168+
)
169+
170+
158171
TRANSACTION_RESULT_FORMATTERS = {
159172
"blockHash": apply_formatter_if(is_not_null, to_hexbytes(32)),
160173
"blockNumber": apply_formatter_if(is_not_null, to_integer_if_hex),
@@ -179,7 +192,9 @@ def is_attrdict(val: Any) -> bool:
179192
}
180193

181194

182-
transaction_result_formatter = apply_formatters_to_dict(TRANSACTION_RESULT_FORMATTERS)
195+
transaction_result_formatter = type_aware_apply_formatters_to_dict(
196+
TRANSACTION_RESULT_FORMATTERS
197+
)
183198

184199

185200
def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
@@ -198,7 +213,7 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
198213
}
199214

200215

201-
log_entry_formatter = apply_formatters_to_dict(LOG_ENTRY_FORMATTERS)
216+
log_entry_formatter = type_aware_apply_formatters_to_dict(LOG_ENTRY_FORMATTERS)
202217

203218

204219
RECEIPT_FORMATTERS = {
@@ -219,7 +234,7 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
219234
}
220235

221236

222-
receipt_formatter = apply_formatters_to_dict(RECEIPT_FORMATTERS)
237+
receipt_formatter = type_aware_apply_formatters_to_dict(RECEIPT_FORMATTERS)
223238

224239
BLOCK_FORMATTERS = {
225240
"baseFeePerGas": to_integer_if_hex,
@@ -256,7 +271,7 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
256271
}
257272

258273

259-
block_formatter = apply_formatters_to_dict(BLOCK_FORMATTERS)
274+
block_formatter = type_aware_apply_formatters_to_dict(BLOCK_FORMATTERS)
260275

261276

262277
SYNCING_FORMATTERS = {
@@ -268,7 +283,7 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
268283
}
269284

270285

271-
syncing_formatter = apply_formatters_to_dict(SYNCING_FORMATTERS)
286+
syncing_formatter = type_aware_apply_formatters_to_dict(SYNCING_FORMATTERS)
272287

273288

274289
TRANSACTION_POOL_CONTENT_FORMATTERS = {
@@ -283,7 +298,7 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
283298
}
284299

285300

286-
transaction_pool_content_formatter = apply_formatters_to_dict(
301+
transaction_pool_content_formatter = type_aware_apply_formatters_to_dict(
287302
TRANSACTION_POOL_CONTENT_FORMATTERS
288303
)
289304

@@ -294,7 +309,7 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
294309
}
295310

296311

297-
transaction_pool_inspect_formatter = apply_formatters_to_dict(
312+
transaction_pool_inspect_formatter = type_aware_apply_formatters_to_dict(
298313
TRANSACTION_POOL_INSPECT_FORMATTERS
299314
)
300315

@@ -308,7 +323,7 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
308323
),
309324
}
310325

311-
fee_history_formatter = apply_formatters_to_dict(FEE_HISTORY_FORMATTERS)
326+
fee_history_formatter = type_aware_apply_formatters_to_dict(FEE_HISTORY_FORMATTERS)
312327

313328
STORAGE_PROOF_FORMATTERS = {
314329
"key": HexBytes,
@@ -324,19 +339,19 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
324339
"nonce": to_integer_if_hex,
325340
"storageHash": to_hexbytes(32),
326341
"storageProof": apply_list_to_array_formatter(
327-
apply_formatters_to_dict(STORAGE_PROOF_FORMATTERS)
342+
type_aware_apply_formatters_to_dict(STORAGE_PROOF_FORMATTERS)
328343
),
329344
}
330345

331-
proof_formatter = apply_formatters_to_dict(ACCOUNT_PROOF_FORMATTERS)
346+
proof_formatter = type_aware_apply_formatters_to_dict(ACCOUNT_PROOF_FORMATTERS)
332347

333348
FILTER_PARAMS_FORMATTERS = {
334349
"fromBlock": apply_formatter_if(is_integer, integer_to_hex),
335350
"toBlock": apply_formatter_if(is_integer, integer_to_hex),
336351
}
337352

338353

339-
filter_params_formatter = apply_formatters_to_dict(FILTER_PARAMS_FORMATTERS)
354+
filter_params_formatter = type_aware_apply_formatters_to_dict(FILTER_PARAMS_FORMATTERS)
340355

341356

342357
filter_result_formatter = apply_one_of_formatters(
@@ -351,7 +366,9 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
351366
"maxPriorityFeePerGas": to_hex_if_integer,
352367
}
353368

354-
transaction_request_formatter = apply_formatters_to_dict(TRANSACTION_REQUEST_FORMATTERS)
369+
transaction_request_formatter = type_aware_apply_formatters_to_dict(
370+
TRANSACTION_REQUEST_FORMATTERS
371+
)
355372
transaction_param_formatter = compose(
356373
remove_key_if("to", lambda txn: txn["to"] in {"", b"", None}),
357374
remove_key_if("gasPrice", lambda txn: txn["gasPrice"] in {"", b"", None}),
@@ -398,22 +415,22 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
398415
"tx": transaction_result_formatter,
399416
}
400417

401-
signed_tx_formatter = apply_formatters_to_dict(SIGNED_TX_FORMATTER)
418+
signed_tx_formatter = type_aware_apply_formatters_to_dict(SIGNED_TX_FORMATTER)
402419

403-
FILTER_PARAM_NORMALIZERS = apply_formatters_to_dict(
420+
FILTER_PARAM_NORMALIZERS = type_aware_apply_formatters_to_dict(
404421
{"address": apply_formatter_if(is_string, lambda x: [x])}
405422
)
406423

407424

408425
GETH_WALLET_FORMATTER = {"address": to_checksum_address}
409426

410-
geth_wallet_formatter = apply_formatters_to_dict(GETH_WALLET_FORMATTER)
427+
geth_wallet_formatter = type_aware_apply_formatters_to_dict(GETH_WALLET_FORMATTER)
411428

412429
GETH_WALLETS_FORMATTER = {
413430
"accounts": apply_list_to_array_formatter(geth_wallet_formatter),
414431
}
415432

416-
geth_wallets_formatter = apply_formatters_to_dict(GETH_WALLETS_FORMATTER)
433+
geth_wallets_formatter = type_aware_apply_formatters_to_dict(GETH_WALLETS_FORMATTER)
417434

418435

419436
PYTHONIC_REQUEST_FORMATTERS: Dict[RPCEndpoint, Callable[..., Any]] = {
@@ -556,10 +573,6 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
556573
RPC.net_peerCount: to_integer_if_hex,
557574
}
558575

559-
ATTRDICT_FORMATTER = {
560-
"*": apply_formatter_if(is_dict and not_attrdict, AttributeDict.recursive)
561-
}
562-
563576
METHOD_NORMALIZERS: Dict[RPCEndpoint, Callable[..., Any]] = {
564577
RPC.eth_getLogs: apply_formatter_at_index(FILTER_PARAM_NORMALIZERS, 0),
565578
RPC.eth_newFilter: apply_formatter_at_index(FILTER_PARAM_NORMALIZERS, 0),
@@ -820,10 +833,7 @@ def get_result_formatters(
820833
partial_formatters = apply_module_to_formatters(
821834
formatters_requiring_module, module, method_name
822835
)
823-
attrdict_formatter = apply_formatter_if(
824-
is_dict and not_attrdict, AttributeDict.recursive
825-
)
826-
return compose(*partial_formatters, attrdict_formatter, *formatters)
836+
return compose(*partial_formatters, *formatters)
827837

828838

829839
def get_error_formatters(

web3/contract/async_contract.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
from web3._utils.filters import (
3939
AsyncLogFilter,
4040
)
41+
from web3._utils.method_formatters import (
42+
is_attrdict,
43+
)
4144
from web3._utils.normalizers import (
4245
normalize_abi,
4346
normalize_address_no_ens,
@@ -414,8 +417,14 @@ async def get_logs(
414417
)
415418

416419
# Convert raw binary data to Python proxy objects as described by ABI
417-
return tuple(
418-
get_event_data(self.w3.codec, abi, entry) for entry in logs # type: ignore
420+
return tuple( # type: ignore
421+
get_event_data(
422+
self.w3.codec,
423+
abi,
424+
entry,
425+
use_attribute_dicts=is_attrdict(entry),
426+
)
427+
for entry in logs
419428
)
420429

421430
@combomethod

web3/contract/contract.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
from web3._utils.filters import (
3535
LogFilter,
3636
)
37+
from web3._utils.method_formatters import (
38+
is_attrdict,
39+
)
3740
from web3._utils.normalizers import (
3841
normalize_abi,
3942
normalize_address,
@@ -162,7 +165,15 @@ def get_logs(
162165
)
163166

164167
# Convert raw binary data to Python proxy objects as described by ABI
165-
return tuple(get_event_data(self.w3.codec, abi, entry) for entry in logs)
168+
return tuple(
169+
get_event_data(
170+
self.w3.codec,
171+
abi,
172+
entry,
173+
use_attribute_dicts=is_attrdict(entry),
174+
)
175+
for entry in logs
176+
)
166177

167178
@combomethod
168179
def create_filter(

web3/datastructures.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ def _apply_if_mapping(cls: Type[T], value: TValue) -> Union[T, TValue]:
7979
if isinstance(value, Mapping):
8080
# error: Too many arguments for "object"
8181
return cls(value) # type: ignore
82-
else:
83-
return value
82+
83+
return value
8484

8585
@classmethod
8686
def recursive(cls, value: TValue) -> "ReadableAttributeDict[TKey, TValue]":

0 commit comments

Comments
 (0)