web3 7.0.0b8__py3-none-any.whl → 7.0.0b9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. ens/async_ens.py +16 -7
  2. ens/base_ens.py +3 -1
  3. ens/exceptions.py +2 -7
  4. ens/utils.py +0 -17
  5. web3/_utils/abi.py +100 -257
  6. web3/_utils/compat/__init__.py +1 -0
  7. web3/_utils/contracts.py +116 -205
  8. web3/_utils/encoding.py +4 -5
  9. web3/_utils/events.py +28 -33
  10. web3/_utils/fee_utils.py +2 -2
  11. web3/_utils/filters.py +2 -5
  12. web3/_utils/http_session_manager.py +30 -7
  13. web3/_utils/method_formatters.py +3 -3
  14. web3/_utils/module_testing/eth_module.py +59 -37
  15. web3/_utils/module_testing/module_testing_utils.py +43 -1
  16. web3/_utils/module_testing/persistent_connection_provider.py +2 -0
  17. web3/_utils/module_testing/web3_module.py +8 -8
  18. web3/_utils/normalizers.py +10 -8
  19. web3/_utils/validation.py +5 -7
  20. web3/contract/async_contract.py +18 -17
  21. web3/contract/base_contract.py +116 -80
  22. web3/contract/contract.py +16 -17
  23. web3/contract/utils.py +86 -55
  24. web3/eth/async_eth.py +1 -2
  25. web3/eth/eth.py +1 -2
  26. web3/exceptions.py +22 -9
  27. web3/gas_strategies/time_based.py +4 -0
  28. web3/manager.py +34 -12
  29. web3/middleware/filter.py +3 -3
  30. web3/middleware/signing.py +6 -1
  31. web3/types.py +5 -45
  32. web3/utils/__init__.py +48 -4
  33. web3/utils/abi.py +575 -10
  34. {web3-7.0.0b8.dist-info → web3-7.0.0b9.dist-info}/METADATA +7 -5
  35. {web3-7.0.0b8.dist-info → web3-7.0.0b9.dist-info}/RECORD +39 -44
  36. {web3-7.0.0b8.dist-info → web3-7.0.0b9.dist-info}/WHEEL +1 -1
  37. web3/tools/benchmark/__init__.py +0 -0
  38. web3/tools/benchmark/main.py +0 -190
  39. web3/tools/benchmark/node.py +0 -120
  40. web3/tools/benchmark/reporting.py +0 -39
  41. web3/tools/benchmark/utils.py +0 -69
  42. /web3/_utils/{function_identifiers.py → abi_element_identifiers.py} +0 -0
  43. {web3-7.0.0b8.dist-info → web3-7.0.0b9.dist-info}/LICENSE +0 -0
  44. {web3-7.0.0b8.dist-info → web3-7.0.0b9.dist-info}/top_level.txt +0 -0
web3/_utils/fee_utils.py CHANGED
@@ -45,7 +45,7 @@ def fee_history_priority_fee(eth: "Eth") -> Wei:
45
45
  # This is a tested internal call so no need for type hinting. We can keep
46
46
  # better consistency between the sync and async calls by unpacking
47
47
  # PRIORITY_FEE_HISTORY_PARAMS as constants here.
48
- fee_history = eth.fee_history(*PRIORITY_FEE_HISTORY_PARAMS)
48
+ fee_history = eth.fee_history(*PRIORITY_FEE_HISTORY_PARAMS) # type: ignore
49
49
  return _fee_history_priority_fee_estimate(fee_history)
50
50
 
51
51
 
@@ -53,5 +53,5 @@ async def async_fee_history_priority_fee(async_eth: "AsyncEth") -> Wei:
53
53
  # This is a tested internal call so no need for type hinting. We can keep
54
54
  # better consistency between the sync and async calls by unpacking
55
55
  # PRIORITY_FEE_HISTORY_PARAMS as constants here.
56
- fee_history = await async_eth.fee_history(*PRIORITY_FEE_HISTORY_PARAMS)
56
+ fee_history = await async_eth.fee_history(*PRIORITY_FEE_HISTORY_PARAMS) # type: ignore # noqa: E501
57
57
  return _fee_history_priority_fee_estimate(fee_history)
web3/_utils/filters.py CHANGED
@@ -19,6 +19,7 @@ from eth_abi.grammar import (
19
19
  parse as parse_type_string,
20
20
  )
21
21
  from eth_typing import (
22
+ ABIEvent,
22
23
  ChecksumAddress,
23
24
  HexStr,
24
25
  TypeStr,
@@ -55,7 +56,6 @@ from web3.exceptions import (
55
56
  Web3ValueError,
56
57
  )
57
58
  from web3.types import (
58
- ABIEvent,
59
59
  BlockIdentifier,
60
60
  FilterParams,
61
61
  LogReceipt,
@@ -90,10 +90,7 @@ def construct_event_filter_params(
90
90
  )
91
91
  topic_set = topics
92
92
 
93
- if len(topic_set) == 1 and is_list_like(topic_set[0]):
94
- filter_params["topics"] = topic_set[0]
95
- else:
96
- filter_params["topics"] = topic_set
93
+ filter_params["topics"] = topic_set
97
94
 
98
95
  if address and contract_address:
99
96
  if is_list_like(address):
@@ -5,6 +5,7 @@ from concurrent.futures import (
5
5
  import logging
6
6
  import os
7
7
  import threading
8
+ import time
8
9
  from typing import (
9
10
  Any,
10
11
  Dict,
@@ -32,6 +33,9 @@ from web3._utils.caching import (
32
33
  from web3._utils.http import (
33
34
  DEFAULT_HTTP_TIMEOUT,
34
35
  )
36
+ from web3.exceptions import (
37
+ TimeExhausted,
38
+ )
35
39
  from web3.utils.caching import (
36
40
  SimpleCache,
37
41
  )
@@ -115,21 +119,40 @@ class HTTPSessionManager:
115
119
  def get_response_from_post_request(
116
120
  self, endpoint_uri: URI, *args: Any, **kwargs: Any
117
121
  ) -> requests.Response:
118
- kwargs.setdefault("timeout", DEFAULT_HTTP_TIMEOUT)
119
122
  session = self.cache_and_return_session(
120
123
  endpoint_uri, request_timeout=kwargs["timeout"]
121
124
  )
122
- response = session.post(endpoint_uri, *args, **kwargs)
123
- return response
125
+ return session.post(endpoint_uri, *args, **kwargs)
124
126
 
125
127
  def make_post_request(
126
128
  self, endpoint_uri: URI, data: Union[bytes, Dict[str, Any]], **kwargs: Any
127
129
  ) -> bytes:
128
- response = self.get_response_from_post_request(
130
+ kwargs.setdefault("timeout", DEFAULT_HTTP_TIMEOUT)
131
+ kwargs.setdefault("stream", False)
132
+
133
+ start = time.time()
134
+ timeout = kwargs["timeout"]
135
+
136
+ with self.get_response_from_post_request(
129
137
  endpoint_uri, data=data, **kwargs
130
- )
131
- response.raise_for_status()
132
- return response.content
138
+ ) as response:
139
+ response.raise_for_status()
140
+ if kwargs.get("stream"):
141
+ return self._handle_streaming_response(response, start, timeout)
142
+ else:
143
+ return response.content
144
+
145
+ def _handle_streaming_response(
146
+ self, response: requests.Response, start: float, timeout: float
147
+ ) -> bytes:
148
+ response_body = b""
149
+ for data in response.iter_content():
150
+ response_body += data
151
+ # Manually manage timeout so streaming responses time out
152
+ # rather than resetting the timeout each time a response comes back
153
+ if (time.time() - start) > timeout:
154
+ raise TimeExhausted
155
+ return response_body
133
156
 
134
157
  def _close_evicted_sessions(self, evicted_sessions: List[requests.Session]) -> None:
135
158
  for evicted_session in evicted_sessions:
@@ -199,7 +199,7 @@ def storage_key_to_hexstr(value: Union[bytes, int, str]) -> HexStr:
199
199
  return HexStr(f"0x{value}")
200
200
  elif isinstance(value, bytes):
201
201
  if len(value) == 32:
202
- return HexBytes(value).to_0x_hex()
202
+ return cast(HexStr, HexBytes(value).to_0x_hex())
203
203
  elif isinstance(value, int):
204
204
  return storage_key_to_hexstr(hex(value))
205
205
  raise Web3ValueError(f"Storage key must be a 32-byte value, got {value!r}")
@@ -424,8 +424,8 @@ ACCOUNT_PROOF_FORMATTERS = {
424
424
  proof_formatter = type_aware_apply_formatters_to_dict(ACCOUNT_PROOF_FORMATTERS)
425
425
 
426
426
  FILTER_PARAMS_FORMATTERS = {
427
- "fromBlock": apply_formatter_if(is_integer, integer_to_hex),
428
- "toBlock": apply_formatter_if(is_integer, integer_to_hex),
427
+ "fromBlock": to_hex_if_integer,
428
+ "toBlock": to_hex_if_integer,
429
429
  }
430
430
 
431
431
 
@@ -58,6 +58,7 @@ from web3._utils.module_testing.module_testing_utils import (
58
58
  assert_contains_log,
59
59
  async_mock_offchain_lookup_request_response,
60
60
  flaky_geth_dev_mining,
61
+ flaky_with_xfail_on_exception,
61
62
  mock_offchain_lookup_request_response,
62
63
  )
63
64
  from web3._utils.module_testing.utils import (
@@ -76,6 +77,7 @@ from web3.exceptions import (
76
77
  MultipleFailedRequests,
77
78
  NameNotFound,
78
79
  OffchainLookup,
80
+ RequestTimedOut,
79
81
  TimeExhausted,
80
82
  TooManyRequests,
81
83
  TransactionNotFound,
@@ -498,6 +500,7 @@ class AsyncEthModuleTest:
498
500
  "maxFeePerGas": async_w3.to_wei(3, "gwei"),
499
501
  "maxPriorityFeePerGas": async_w3.to_wei(1, "gwei"),
500
502
  }
503
+
501
504
  txn_hash = await async_w3.eth.send_transaction(txn_params)
502
505
  txn = await async_w3.eth.get_transaction(txn_hash)
503
506
 
@@ -507,7 +510,7 @@ class AsyncEthModuleTest:
507
510
  assert txn["gas"] == 21000
508
511
  assert txn["maxFeePerGas"] == txn_params["maxFeePerGas"]
509
512
  assert txn["maxPriorityFeePerGas"] == txn_params["maxPriorityFeePerGas"]
510
- assert txn["gasPrice"] == txn_params["maxFeePerGas"]
513
+ assert txn["gasPrice"] <= txn["maxFeePerGas"] # effective gas price
511
514
 
512
515
  @pytest.mark.asyncio
513
516
  async def test_eth_send_transaction_default_fees(
@@ -521,6 +524,7 @@ class AsyncEthModuleTest:
521
524
  "value": Wei(1),
522
525
  "gas": 21000,
523
526
  }
527
+
524
528
  txn_hash = await async_w3.eth.send_transaction(txn_params)
525
529
  txn = await async_w3.eth.get_transaction(txn_hash)
526
530
 
@@ -530,7 +534,7 @@ class AsyncEthModuleTest:
530
534
  assert txn["gas"] == 21000
531
535
  assert is_integer(txn["maxPriorityFeePerGas"])
532
536
  assert is_integer(txn["maxFeePerGas"])
533
- assert txn["gasPrice"] == txn["maxFeePerGas"]
537
+ assert txn["gasPrice"] <= txn["maxFeePerGas"] # effective gas price
534
538
 
535
539
  @pytest.mark.asyncio
536
540
  async def test_eth_send_transaction_hex_fees(
@@ -807,7 +811,7 @@ class AsyncEthModuleTest:
807
811
  else 2 * latest_block["baseFeePerGas"] + max_priority_fee
808
812
  )
809
813
  assert txn["maxPriorityFeePerGas"] == max_priority_fee
810
- assert txn["gasPrice"] == txn["maxFeePerGas"]
814
+ assert txn["gasPrice"] <= txn["maxFeePerGas"] # effective gas price
811
815
 
812
816
  async_w3.eth.set_gas_price_strategy(None) # reset strategy
813
817
 
@@ -1145,7 +1149,8 @@ class AsyncEthModuleTest:
1145
1149
  f"{unknown_identifier}"
1146
1150
  ),
1147
1151
  ):
1148
- await async_w3.eth.get_raw_transaction_by_block(unknown_identifier, 0)
1152
+ # type ignored because we are testing an invalid block identifier
1153
+ await async_w3.eth.get_raw_transaction_by_block(unknown_identifier, 0) # type: ignore # noqa: E501
1149
1154
 
1150
1155
  @pytest.mark.asyncio
1151
1156
  async def test_eth_get_balance(self, async_w3: "AsyncWeb3") -> None:
@@ -1240,7 +1245,7 @@ class AsyncEthModuleTest:
1240
1245
  account = accounts[0]
1241
1246
 
1242
1247
  txn_params = async_math_contract._prepare_transaction(
1243
- fn_name="add",
1248
+ abi_element_identifier="add",
1244
1249
  fn_args=(7, 11),
1245
1250
  transaction={"from": account, "to": async_math_contract.address},
1246
1251
  )
@@ -1258,7 +1263,7 @@ class AsyncEthModuleTest:
1258
1263
  accounts = await async_w3.eth.accounts
1259
1264
  account = accounts[0]
1260
1265
  txn_params = async_revert_contract._prepare_transaction(
1261
- fn_name="normalFunction",
1266
+ abi_element_identifier="normalFunction",
1262
1267
  transaction={"from": account, "to": async_revert_contract.address},
1263
1268
  )
1264
1269
  call_result = await async_w3.eth.call(txn_params)
@@ -1327,7 +1332,7 @@ class AsyncEthModuleTest:
1327
1332
  ) -> None:
1328
1333
  accounts = await async_w3.eth.accounts
1329
1334
  txn_params = async_math_contract._prepare_transaction(
1330
- fn_name="add",
1335
+ abi_element_identifier="add",
1331
1336
  fn_args=(0, 0),
1332
1337
  transaction={"from": accounts[0], "to": async_math_contract.address},
1333
1338
  )
@@ -1344,7 +1349,7 @@ class AsyncEthModuleTest:
1344
1349
  async_keyfile_account_address: ChecksumAddress,
1345
1350
  ) -> None:
1346
1351
  txn_params = async_revert_contract._prepare_transaction(
1347
- fn_name="revertWithMessage",
1352
+ abi_element_identifier="revertWithMessage",
1348
1353
  transaction={
1349
1354
  "from": async_keyfile_account_address,
1350
1355
  "to": async_revert_contract.address,
@@ -1364,7 +1369,7 @@ class AsyncEthModuleTest:
1364
1369
  ) -> None:
1365
1370
  with pytest.raises(ContractLogicError, match="execution reverted"):
1366
1371
  txn_params = async_revert_contract._prepare_transaction(
1367
- fn_name="revertWithoutMessage",
1372
+ abi_element_identifier="revertWithoutMessage",
1368
1373
  transaction={
1369
1374
  "from": async_keyfile_account_address,
1370
1375
  "to": async_revert_contract.address,
@@ -1380,10 +1385,11 @@ class AsyncEthModuleTest:
1380
1385
  async_keyfile_account_address: ChecksumAddress,
1381
1386
  ) -> None:
1382
1387
  data = async_revert_contract.encode_abi(
1383
- fn_name="UnauthorizedWithMessage", args=["You are not authorized"]
1388
+ abi_element_identifier="UnauthorizedWithMessage",
1389
+ args=["You are not authorized"],
1384
1390
  )
1385
1391
  txn_params = async_revert_contract._prepare_transaction(
1386
- fn_name="customErrorWithMessage",
1392
+ abi_element_identifier="customErrorWithMessage",
1387
1393
  transaction={
1388
1394
  "from": async_keyfile_account_address,
1389
1395
  "to": async_revert_contract.address,
@@ -1399,9 +1405,9 @@ class AsyncEthModuleTest:
1399
1405
  async_revert_contract: "AsyncContract",
1400
1406
  async_keyfile_account_address: ChecksumAddress,
1401
1407
  ) -> None:
1402
- data = async_revert_contract.encode_abi(fn_name="Unauthorized")
1408
+ data = async_revert_contract.encode_abi(abi_element_identifier="Unauthorized")
1403
1409
  txn_params = async_revert_contract._prepare_transaction(
1404
- fn_name="customErrorWithoutMessage",
1410
+ abi_element_identifier="customErrorWithoutMessage",
1405
1411
  transaction={
1406
1412
  "from": async_keyfile_account_address,
1407
1413
  "to": async_revert_contract.address,
@@ -1692,6 +1698,7 @@ class AsyncEthModuleTest:
1692
1698
  assert isinstance(effective_gas_price, int)
1693
1699
  assert effective_gas_price > 0
1694
1700
 
1701
+ @flaky_geth_dev_mining
1695
1702
  @pytest.mark.asyncio
1696
1703
  async def test_async_eth_get_transaction_receipt_unmined(
1697
1704
  self,
@@ -1757,6 +1764,7 @@ class AsyncEthModuleTest:
1757
1764
  assert isinstance(effective_gas_price, int)
1758
1765
  assert effective_gas_price > 0
1759
1766
 
1767
+ @flaky_geth_dev_mining
1760
1768
  @pytest.mark.asyncio
1761
1769
  async def test_async_eth_wait_for_transaction_receipt_unmined(
1762
1770
  self,
@@ -2356,7 +2364,10 @@ class AsyncEthModuleTest:
2356
2364
  with pytest.raises(Web3ValueError):
2357
2365
  await async_w3.eth.replace_transaction(txn_hash, txn_params)
2358
2366
 
2359
- @flaky_geth_dev_mining
2367
+ @flaky_with_xfail_on_exception(
2368
+ reason="Very flaky on CI runs, hard to reproduce locally.",
2369
+ exception=RequestTimedOut,
2370
+ )
2360
2371
  @pytest.mark.asyncio
2361
2372
  async def test_async_eth_replace_transaction_gas_price_defaulting_minimum(
2362
2373
  self, async_w3: "AsyncWeb3", async_keyfile_account_address: ChecksumAddress
@@ -2380,7 +2391,10 @@ class AsyncEthModuleTest:
2380
2391
  gas_price * 1.125
2381
2392
  ) # minimum gas price
2382
2393
 
2383
- @flaky_geth_dev_mining
2394
+ @flaky_with_xfail_on_exception(
2395
+ reason="Very flaky on CI runs, hard to reproduce locally.",
2396
+ exception=RequestTimedOut,
2397
+ )
2384
2398
  @pytest.mark.asyncio
2385
2399
  async def test_async_eth_replace_transaction_gas_price_defaulting_strategy_higher(
2386
2400
  self, async_w3: "AsyncWeb3", async_keyfile_account_address: ChecksumAddress
@@ -2409,7 +2423,10 @@ class AsyncEthModuleTest:
2409
2423
  ) # Strategy provides higher gas price
2410
2424
  async_w3.eth.set_gas_price_strategy(None) # reset strategy
2411
2425
 
2412
- @flaky_geth_dev_mining
2426
+ @flaky_with_xfail_on_exception(
2427
+ reason="Very flaky on CI runs, hard to reproduce locally.",
2428
+ exception=RequestTimedOut,
2429
+ )
2413
2430
  @pytest.mark.asyncio
2414
2431
  async def test_async_eth_replace_transaction_gas_price_defaulting_strategy_lower(
2415
2432
  self, async_w3: "AsyncWeb3", async_keyfile_account_address: ChecksumAddress
@@ -3101,6 +3118,7 @@ class EthModuleTest:
3101
3118
  "maxFeePerGas": w3.to_wei(3, "gwei"),
3102
3119
  "maxPriorityFeePerGas": w3.to_wei(1, "gwei"),
3103
3120
  }
3121
+
3104
3122
  txn_hash = w3.eth.send_transaction(txn_params)
3105
3123
  txn = w3.eth.get_transaction(txn_hash)
3106
3124
 
@@ -3110,7 +3128,7 @@ class EthModuleTest:
3110
3128
  assert txn["gas"] == 21000
3111
3129
  assert txn["maxFeePerGas"] == txn_params["maxFeePerGas"]
3112
3130
  assert txn["maxPriorityFeePerGas"] == txn_params["maxPriorityFeePerGas"]
3113
- assert txn["gasPrice"] == txn_params["maxFeePerGas"]
3131
+ assert txn["gasPrice"] <= txn["maxFeePerGas"] # effective gas price
3114
3132
 
3115
3133
  def test_eth_send_transaction_with_nonce(
3116
3134
  self, w3: "Web3", keyfile_account_address: ChecksumAddress
@@ -3161,7 +3179,7 @@ class EthModuleTest:
3161
3179
  assert txn["gas"] == 21000
3162
3180
  assert is_integer(txn["maxPriorityFeePerGas"])
3163
3181
  assert is_integer(txn["maxFeePerGas"])
3164
- assert txn["gasPrice"] == txn["maxFeePerGas"]
3182
+ assert txn["gasPrice"] <= txn["maxFeePerGas"] # effective gas price
3165
3183
 
3166
3184
  def test_eth_send_transaction_hex_fees(
3167
3185
  self, w3: "Web3", keyfile_account_address_dual_type: ChecksumAddress
@@ -3327,7 +3345,7 @@ class EthModuleTest:
3327
3345
  else 2 * latest_block["baseFeePerGas"] + max_priority_fee
3328
3346
  )
3329
3347
  assert txn["maxPriorityFeePerGas"] == max_priority_fee
3330
- assert txn["gasPrice"] == txn["maxFeePerGas"]
3348
+ assert txn["gasPrice"] <= txn["maxFeePerGas"] # effective gas price
3331
3349
 
3332
3350
  w3.eth.set_gas_price_strategy(None) # reset strategy
3333
3351
 
@@ -3596,7 +3614,6 @@ class EthModuleTest:
3596
3614
  self, w3: "Web3", keyfile_account_address: ChecksumAddress
3597
3615
  ) -> None:
3598
3616
  gas_price = w3.to_wei(2, "gwei")
3599
-
3600
3617
  txn_params: TxParams = {
3601
3618
  "from": keyfile_account_address,
3602
3619
  "to": keyfile_account_address,
@@ -3701,7 +3718,7 @@ class EthModuleTest:
3701
3718
 
3702
3719
  def test_eth_call(self, w3: "Web3", math_contract: "Contract") -> None:
3703
3720
  txn_params = math_contract._prepare_transaction(
3704
- fn_name="add",
3721
+ abi_element_identifier="add",
3705
3722
  fn_args=(7, 11),
3706
3723
  transaction={"from": w3.eth.accounts[0], "to": math_contract.address},
3707
3724
  )
@@ -3714,7 +3731,7 @@ class EthModuleTest:
3714
3731
  self, w3: "Web3", revert_contract: "Contract"
3715
3732
  ) -> None:
3716
3733
  txn_params = revert_contract._prepare_transaction(
3717
- fn_name="normalFunction",
3734
+ abi_element_identifier="normalFunction",
3718
3735
  transaction={"from": w3.eth.accounts[0], "to": revert_contract.address},
3719
3736
  )
3720
3737
  call_result = w3.eth.call(txn_params)
@@ -3775,7 +3792,7 @@ class EthModuleTest:
3775
3792
  self, w3: "Web3", math_contract: "Contract"
3776
3793
  ) -> None:
3777
3794
  txn_params = math_contract._prepare_transaction(
3778
- fn_name="add",
3795
+ abi_element_identifier="add",
3779
3796
  fn_args=(0, 0),
3780
3797
  transaction={"from": w3.eth.accounts[0], "to": math_contract.address},
3781
3798
  )
@@ -3791,7 +3808,7 @@ class EthModuleTest:
3791
3808
  keyfile_account_address: ChecksumAddress,
3792
3809
  ) -> None:
3793
3810
  txn_params = revert_contract._prepare_transaction(
3794
- fn_name="revertWithMessage",
3811
+ abi_element_identifier="revertWithMessage",
3795
3812
  transaction={
3796
3813
  "from": keyfile_account_address,
3797
3814
  "to": revert_contract.address,
@@ -3812,7 +3829,7 @@ class EthModuleTest:
3812
3829
  ) -> None:
3813
3830
  with pytest.raises(ContractLogicError, match="execution reverted"):
3814
3831
  txn_params = revert_contract._prepare_transaction(
3815
- fn_name="revertWithoutMessage",
3832
+ abi_element_identifier="revertWithoutMessage",
3816
3833
  transaction={
3817
3834
  "from": keyfile_account_address,
3818
3835
  "to": revert_contract.address,
@@ -3827,10 +3844,11 @@ class EthModuleTest:
3827
3844
  keyfile_account_address: ChecksumAddress,
3828
3845
  ) -> None:
3829
3846
  data = revert_contract.encode_abi(
3830
- fn_name="UnauthorizedWithMessage", args=["You are not authorized"]
3847
+ abi_element_identifier="UnauthorizedWithMessage",
3848
+ args=["You are not authorized"],
3831
3849
  )
3832
3850
  txn_params = revert_contract._prepare_transaction(
3833
- fn_name="customErrorWithMessage",
3851
+ abi_element_identifier="customErrorWithMessage",
3834
3852
  transaction={
3835
3853
  "from": keyfile_account_address,
3836
3854
  "to": revert_contract.address,
@@ -3846,9 +3864,9 @@ class EthModuleTest:
3846
3864
  revert_contract: "Contract",
3847
3865
  keyfile_account_address: ChecksumAddress,
3848
3866
  ) -> None:
3849
- data = revert_contract.encode_abi(fn_name="Unauthorized")
3867
+ data = revert_contract.encode_abi(abi_element_identifier="Unauthorized")
3850
3868
  txn_params = revert_contract._prepare_transaction(
3851
- fn_name="customErrorWithoutMessage",
3869
+ abi_element_identifier="customErrorWithoutMessage",
3852
3870
  transaction={
3853
3871
  "from": keyfile_account_address,
3854
3872
  "to": revert_contract.address,
@@ -4102,7 +4120,7 @@ class EthModuleTest:
4102
4120
  ContractLogicError, match="execution reverted: Function has been reverted"
4103
4121
  ):
4104
4122
  txn_params = revert_contract._prepare_transaction(
4105
- fn_name="revertWithMessage",
4123
+ abi_element_identifier="revertWithMessage",
4106
4124
  transaction={
4107
4125
  "from": keyfile_account_address,
4108
4126
  "to": revert_contract.address,
@@ -4118,7 +4136,7 @@ class EthModuleTest:
4118
4136
  ) -> None:
4119
4137
  with pytest.raises(ContractLogicError, match="execution reverted"):
4120
4138
  txn_params = revert_contract._prepare_transaction(
4121
- fn_name="revertWithoutMessage",
4139
+ abi_element_identifier="revertWithoutMessage",
4122
4140
  transaction={
4123
4141
  "from": keyfile_account_address,
4124
4142
  "to": revert_contract.address,
@@ -4133,10 +4151,11 @@ class EthModuleTest:
4133
4151
  keyfile_account_address: ChecksumAddress,
4134
4152
  ) -> None:
4135
4153
  data = revert_contract.encode_abi(
4136
- fn_name="UnauthorizedWithMessage", args=["You are not authorized"]
4154
+ abi_element_identifier="UnauthorizedWithMessage",
4155
+ args=["You are not authorized"],
4137
4156
  )
4138
4157
  txn_params = revert_contract._prepare_transaction(
4139
- fn_name="customErrorWithMessage",
4158
+ abi_element_identifier="customErrorWithMessage",
4140
4159
  transaction={
4141
4160
  "from": keyfile_account_address,
4142
4161
  "to": revert_contract.address,
@@ -4152,9 +4171,9 @@ class EthModuleTest:
4152
4171
  revert_contract: "Contract",
4153
4172
  keyfile_account_address: ChecksumAddress,
4154
4173
  ) -> None:
4155
- data = revert_contract.encode_abi(fn_name="Unauthorized")
4174
+ data = revert_contract.encode_abi(abi_element_identifier="Unauthorized")
4156
4175
  txn_params = revert_contract._prepare_transaction(
4157
- fn_name="customErrorWithoutMessage",
4176
+ abi_element_identifier="customErrorWithoutMessage",
4158
4177
  transaction={
4159
4178
  "from": keyfile_account_address,
4160
4179
  "to": revert_contract.address,
@@ -4286,7 +4305,7 @@ class EthModuleTest:
4286
4305
  self, w3: "Web3", block_with_txn: BlockData
4287
4306
  ) -> None:
4288
4307
  block = w3.eth.get_block(block_with_txn["number"], True)
4289
- transaction = block["transactions"][0]
4308
+ transaction = cast(TxData, block["transactions"][0])
4290
4309
  assert transaction["hash"] == block_with_txn["transactions"][0]
4291
4310
 
4292
4311
  def test_eth_getBlockReceipts_hash(
@@ -4360,6 +4379,7 @@ class EthModuleTest:
4360
4379
  assert isinstance(effective_gas_price, int)
4361
4380
  assert effective_gas_price > 0
4362
4381
 
4382
+ @flaky_geth_dev_mining
4363
4383
  def test_eth_get_transaction_receipt_unmined(
4364
4384
  self, w3: "Web3", keyfile_account_address_dual_type: ChecksumAddress
4365
4385
  ) -> None:
@@ -4417,6 +4437,7 @@ class EthModuleTest:
4417
4437
  assert isinstance(effective_gas_price, int)
4418
4438
  assert effective_gas_price > 0
4419
4439
 
4440
+ @flaky_geth_dev_mining
4420
4441
  def test_eth_wait_for_transaction_receipt_unmined(
4421
4442
  self, w3: "Web3", keyfile_account_address_dual_type: ChecksumAddress
4422
4443
  ) -> None:
@@ -4751,7 +4772,8 @@ class EthModuleTest:
4751
4772
  f"{unknown_identifier}"
4752
4773
  ),
4753
4774
  ):
4754
- w3.eth.get_raw_transaction_by_block(unknown_identifier, 0)
4775
+ # type ignored because we are testing an invalid input
4776
+ w3.eth.get_raw_transaction_by_block(unknown_identifier, 0) # type: ignore
4755
4777
 
4756
4778
  def test_default_account(
4757
4779
  self, w3: "Web3", keyfile_account_address_dual_type: ChecksumAddress
@@ -1,12 +1,17 @@
1
1
  import asyncio
2
+ import functools
3
+ import pytest
2
4
  from typing import (
3
5
  TYPE_CHECKING,
4
6
  Any,
7
+ Callable,
5
8
  Collection,
6
9
  Dict,
7
10
  Generator,
8
11
  Literal,
9
12
  Sequence,
13
+ Tuple,
14
+ Type,
10
15
  Union,
11
16
  )
12
17
 
@@ -55,7 +60,44 @@ flaky_geth_dev_mining decorator for tests requiring a pending block
55
60
  for the duration of the test. This behavior can be flaky
56
61
  due to timing of the test running as a block is mined.
57
62
  """
58
- flaky_geth_dev_mining = flaky(max_runs=3)
63
+ flaky_geth_dev_mining = flaky(max_runs=3, min_passes=1)
64
+
65
+
66
+ def flaky_with_xfail_on_exception(
67
+ reason: str,
68
+ exception: Union[Type[Exception], Tuple[Type[Exception], ...]],
69
+ max_runs: int = 3,
70
+ min_passes: int = 1,
71
+ ) -> Callable[[Any], Any]:
72
+ """
73
+ Some tests inconsistently fail hard with a particular exception and retrying
74
+ these tests often times does not get them "unstuck". If we've exhausted all flaky
75
+ retries and this expected exception is raised, `xfail` the test with the given
76
+ reason.
77
+ """
78
+ runs = max_runs
79
+
80
+ def decorator(func: Any) -> Any:
81
+ @flaky(max_runs=max_runs, min_passes=min_passes)
82
+ @functools.wraps(func)
83
+ async def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
84
+ nonlocal runs
85
+ try:
86
+ return await func(self, *args, **kwargs)
87
+ except exception:
88
+ # xfail the test only if the exception is raised and we have exhausted
89
+ # all flaky retries
90
+ if runs == 1:
91
+ pytest.xfail(reason)
92
+ runs -= 1
93
+ pytest.fail(f"xfailed but {runs} run(s) remaining with flaky...")
94
+ except Exception as e:
95
+ # let flaky handle it
96
+ raise e
97
+
98
+ return wrapper
99
+
100
+ return decorator
59
101
 
60
102
 
61
103
  def assert_contains_log(
@@ -389,7 +389,9 @@ class PersistentConnectionProviderTest:
389
389
  assert isinstance(pending, AttributeDict)
390
390
 
391
391
  # assert block values
392
+ assert latest is not None
392
393
  assert all(k in latest.keys() for k in SOME_BLOCK_KEYS)
394
+ assert pending is not None
393
395
  assert all(k in pending.keys() for k in SOME_BLOCK_KEYS)
394
396
 
395
397
  assert isinstance(block_num, int)
@@ -522,12 +522,12 @@ class AsyncWeb3ModuleTest(Web3ModuleTest):
522
522
  # an asynchronous test should have the exact same name.
523
523
 
524
524
  @pytest.mark.asyncio
525
- async def test_web3_client_version(self, async_w3: AsyncWeb3) -> None:
525
+ async def test_web3_client_version(self, async_w3: AsyncWeb3) -> None: # type: ignore[override] # noqa: E501
526
526
  client_version = await async_w3.client_version
527
527
  self._check_web3_client_version(client_version)
528
528
 
529
529
  @pytest.mark.asyncio
530
- async def test_batch_requests(
530
+ async def test_batch_requests( # type: ignore[override]
531
531
  self, async_w3: AsyncWeb3, async_math_contract: "AsyncContract"
532
532
  ) -> None:
533
533
  async with async_w3.batch_requests() as batch:
@@ -595,8 +595,8 @@ class AsyncWeb3ModuleTest(Web3ModuleTest):
595
595
  assert last_three_responses[2]["number"] == 5
596
596
 
597
597
  @pytest.mark.asyncio
598
- async def test_batch_requests_initialized_as_object(
599
- self, async_w3: AsyncWeb3, async_math_contract: "AsyncContract"
598
+ async def test_batch_requests_initialized_as_object( # type: ignore[override]
599
+ self, async_w3: AsyncWeb3, async_math_contract: "AsyncContract" # type: ignore[override] # noqa: E501
600
600
  ) -> None:
601
601
  batch = async_w3.batch_requests()
602
602
  batch.add(async_w3.eth.get_block(1))
@@ -643,7 +643,7 @@ class AsyncWeb3ModuleTest(Web3ModuleTest):
643
643
  assert cast(BlockData, b4)["number"] == 4
644
644
 
645
645
  @pytest.mark.asyncio
646
- async def test_batch_requests_clear(self, async_w3: AsyncWeb3) -> None:
646
+ async def test_batch_requests_clear(self, async_w3: AsyncWeb3) -> None: # type: ignore[override] # noqa: E501
647
647
  async with async_w3.batch_requests() as batch:
648
648
  batch.add(async_w3.eth.get_block(1))
649
649
  batch.add(async_w3.eth.get_block(2))
@@ -672,7 +672,7 @@ class AsyncWeb3ModuleTest(Web3ModuleTest):
672
672
  assert cast(BlockData, r3)["number"] == 6
673
673
 
674
674
  @pytest.mark.asyncio
675
- async def test_batch_requests_cancel(self, async_w3: AsyncWeb3) -> None:
675
+ async def test_batch_requests_cancel(self, async_w3: AsyncWeb3) -> None: # type: ignore[override] # noqa: E501
676
676
  # as context manager
677
677
  async with async_w3.batch_requests() as batch:
678
678
  batch.add(async_w3.eth.get_block(1))
@@ -712,8 +712,8 @@ class AsyncWeb3ModuleTest(Web3ModuleTest):
712
712
  assert isinstance(block_num, int)
713
713
 
714
714
  @pytest.mark.asyncio
715
- async def test_batch_requests_raises_for_common_unsupported_methods(
716
- self, async_w3: AsyncWeb3, async_math_contract: "AsyncContract"
715
+ async def test_batch_requests_raises_for_common_unsupported_methods( # type: ignore[override] # noqa: E501
716
+ self, async_w3: AsyncWeb3, async_math_contract: "AsyncContract" # type: ignore[override] # noqa: E501
717
717
  ) -> None:
718
718
  async with async_w3.batch_requests() as batch:
719
719
  with pytest.raises(MethodNotSupported, match="eth_sendTransaction"):