From 47ae637be2ae50205335a89216fb1b20b7a37be2 Mon Sep 17 00:00:00 2001 From: kclowes Date: Wed, 5 Nov 2025 12:07:31 -0700 Subject: [PATCH 1/8] Add Python 3.14 to CI --- .circleci/config.yml | 6 +++--- .pre-commit-config.yaml | 2 +- tox.ini | 9 +++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f938f46ce1..b06956cafe 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -221,7 +221,7 @@ workflows: - common: matrix: parameters: - python_minor_version: ["10", "11", "12", "13"] + python_minor_version: ["10", "11", "12", "13", "14"] tox_env: [ "lint", "core", @@ -234,7 +234,7 @@ workflows: - geth: matrix: parameters: - python_minor_version: ["10", "11", "12", "13"] + python_minor_version: ["10", "11", "12", "13", "14"] tox_env: [ "integration-goethereum-ipc", "integration-goethereum-ipc_async", @@ -252,7 +252,7 @@ workflows: - windows-wheel: matrix: parameters: - python_minor_version: ["10", "11", "12", "13"] + python_minor_version: ["10", "11", "12", "13", "14"] name: "py3<< matrix.python_minor_version >>-windows-wheel" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 71439d587f..42d97ef717 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.21.2 hooks: - id: pyupgrade args: [--py38-plus] diff --git a/tox.ini b/tox.ini index ff5d2b5e84..8b274a1c83 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist= - py{310,311,312,313}-{ens,core,lint,wheel} - py{310,311,312,313}-integration-{goethereum,ethtester} + py{310,311,312,313,314}-{ens,core,lint,wheel} + py{310,311,312,313,314}-integration-{goethereum,ethtester} docs benchmark windows-wheel @@ -48,8 +48,9 @@ basepython = py311: python3.11 py312: python3.12 py313: python3.13 + py314: python3.14 -[testenv:py{310,311,312,313}-lint] +[testenv:py{310,311,312,313,314}-lint] deps=pre-commit extras=dev commands= @@ -64,7 +65,7 @@ commands= python {toxinidir}/web3/tools/benchmark/main.py --num-calls 100 -[testenv:py{310,311,312,313}-wheel] +[testenv:py{310,311,312,313,314}-wheel] deps= wheel build[virtualenv] From d0406a03f4e370c8e4fbab68781e8c7bd3aaa694 Mon Sep 17 00:00:00 2001 From: kclowes Date: Thu, 30 Oct 2025 16:57:09 -0600 Subject: [PATCH 2/8] Upgrade Websockets dependency and syntax --- setup.py | 5 +- .../contracts/test_contract_call_interface.py | 355 +++++++++++++----- .../module_testing/module_testing_utils.py | 5 +- web3/_utils/module_testing/utils.py | 34 +- web3/providers/async_base.py | 6 +- web3/providers/persistent/websocket.py | 28 +- 6 files changed, 320 insertions(+), 113 deletions(-) diff --git a/setup.py b/setup.py index f57315b8b1..e10f37ef39 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ "towncrier>=24,<25", ], "test": [ - "pytest-asyncio>=0.18.1,<0.23", + "pytest-asyncio>=0.18.1", "pytest-mock>=1.10", "pytest-xdist>=2.4.0", "pytest>=7.0.0", @@ -78,7 +78,7 @@ "requests>=2.23.0", "typing-extensions>=4.0.1", "types-requests>=2.0.0", - "websockets>=10.0.0,<16.0.0", + "websockets>=14.0.0", "pyunormalize>=15.0.0", ], python_requires=">=3.10, <4", @@ -99,5 +99,6 @@ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ], ) diff --git a/tests/core/contracts/test_contract_call_interface.py b/tests/core/contracts/test_contract_call_interface.py index bdc8fe1cc6..82a6c3a891 100644 --- a/tests/core/contracts/test_contract_call_interface.py +++ b/tests/core/contracts/test_contract_call_interface.py @@ -58,7 +58,10 @@ def bytes_contract(w3, request, address_conversion_func): bytes_contract_factory = w3.eth.contract(**BYTES_CONTRACT_DATA) return deploy( - w3, bytes_contract_factory, address_conversion_func, args=[request.param] + w3, + bytes_contract_factory, + address_conversion_func, + args=[request.param], ) @@ -81,7 +84,10 @@ def non_strict_bytes_contract( @pytest.fixture def call_transaction(): - return {"data": "0x61bc221a", "to": "0xc305c901078781C232A2a521C2aF7980f8385ee9"} + return { + "data": "0x61bc221a", + "to": "0xc305c901078781C232A2a521C2aF7980f8385ee9", + } @pytest.fixture @@ -92,12 +98,19 @@ def bytes32_contract_factory(w3): @pytest.fixture( params=[ "0x0406040604060406040604060406040604060406040604060406040604060406", - HexBytes("0406040604060406040604060406040604060406040604060406040604060406"), + HexBytes( + "0406040604060406040604060406040604060406040604060406040604060406" + ), ] ) -def bytes32_contract(w3, bytes32_contract_factory, request, address_conversion_func): +def bytes32_contract( + w3, bytes32_contract_factory, request, address_conversion_func +): return deploy( - w3, bytes32_contract_factory, address_conversion_func, args=[request.param] + w3, + bytes32_contract_factory, + address_conversion_func, + args=[request.param], ) @@ -130,11 +143,15 @@ def test_deploy_raises_due_to_strict_byte_checking_by_default( w3, bytes32_contract_factory, address_conversion_func, - args=["0406040604060406040604060406040604060406040604060406040604060406"], + args=[ + "0406040604060406040604060406040604060406040604060406040604060406" + ], ) -def test_invalid_address_in_deploy_arg(contract_with_constructor_address_factory): +def test_invalid_address_in_deploy_arg( + contract_with_constructor_address_factory, +): with pytest.raises(InvalidAddress): contract_with_constructor_address_factory.constructor( "0xd3cda913deb6f67967b99d67acdfa1712c293601", @@ -151,7 +168,9 @@ def test_call_no_arguments_no_parens(math_contract): def test_call_with_one_argument(math_contract, call): - result = call(contract=math_contract, contract_function="multiply7", func_args=[3]) + result = call( + contract=math_contract, contract_function="multiply7", func_args=[3] + ) assert result == 21 @@ -163,7 +182,9 @@ def test_call_with_one_argument(math_contract, call): (tuple(), {"a": 9, "b": 7}), ), ) -def test_call_with_multiple_arguments(math_contract, call, call_args, call_kwargs): +def test_call_with_multiple_arguments( + math_contract, call, call_args, call_kwargs +): result = call( contract=math_contract, contract_function="add", @@ -190,7 +211,9 @@ def test_saved_method_call_with_multiple_arguments( def test_call_get_w3_value(function_name_tester_contract, call): - result = call(contract=function_name_tester_contract, contract_function="w3") + result = call( + contract=function_name_tester_contract, contract_function="w3" + ) assert result is True @@ -203,7 +226,9 @@ def test_call_get_string_value(string_contract, call): def test_call_get_bytes32_array(arrays_contract, call): - result = call(contract=arrays_contract, contract_function="getBytes32Value") + result = call( + contract=arrays_contract, contract_function="getBytes32Value" + ) # expected_bytes32_array = [keccak('0'), keccak('1')] expected_bytes32_array = [ b"\x04HR\xb2\xa6p\xad\xe5@~x\xfb(c\xc5\x1d\xe9\xfc\xb9eB\xa0q\x86\xfe:\xed\xa6\xbb\x8a\x11m", # noqa: E501 @@ -213,7 +238,9 @@ def test_call_get_bytes32_array(arrays_contract, call): def test_call_get_bytes32_const_array(arrays_contract, call): - result = call(contract=arrays_contract, contract_function="getBytes32ConstValue") + result = call( + contract=arrays_contract, contract_function="getBytes32ConstValue" + ) # expected_bytes32_array = [keccak('A'), keccak('B')] expected_bytes32_array = [ b"\x03x?\xac.\xfe\xd8\xfb\xc9\xadD>Y.\xe3\x0ea\xd6_G\x11@\xc1\x0c\xa1U\xe97\xb45\xb7`", # noqa: E501 @@ -229,7 +256,9 @@ def test_call_get_byte_array(arrays_contract, call): def test_call_get_byte_array_non_strict(non_strict_arrays_contract, call): - result = call(contract=non_strict_arrays_contract, contract_function="getByteValue") + result = call( + contract=non_strict_arrays_contract, contract_function="getByteValue" + ) expected_non_strict_byte_arr = [b"\xff", b"\xff", b"\xff", b"\xff"] assert result == expected_non_strict_byte_arr @@ -269,7 +298,9 @@ def test_set_byte_array_non_strict( contract_function="setByteValue", func_args=[args], ) - result = call(contract=non_strict_arrays_contract, contract_function="getByteValue") + result = call( + contract=non_strict_arrays_contract, contract_function="getByteValue" + ) assert result == expected @@ -287,14 +318,19 @@ def test_set_byte_array_with_invalid_args(arrays_contract, transact, args): def test_call_get_byte_const_array_strict_by_default(arrays_contract, call): - result = call(contract=arrays_contract, contract_function="getByteConstValue") + result = call( + contract=arrays_contract, contract_function="getByteConstValue" + ) expected_byte_arr = [b"\x00", b"\x01"] assert result == expected_byte_arr -def test_call_get_byte_const_array_non_strict(non_strict_arrays_contract, call): +def test_call_get_byte_const_array_non_strict( + non_strict_arrays_contract, call +): result = call( - contract=non_strict_arrays_contract, contract_function="getByteConstValue" + contract=non_strict_arrays_contract, + contract_function="getByteConstValue", ) expected_byte_arr = [b"\x00", b"\x01"] assert result == expected_byte_arr @@ -302,12 +338,15 @@ def test_call_get_byte_const_array_non_strict(non_strict_arrays_contract, call): def test_call_read_address_variable(contract_with_constructor_address, call): result = call( - contract=contract_with_constructor_address, contract_function="testAddr" + contract=contract_with_constructor_address, + contract_function="testAddr", ) assert result == "0xd3CdA913deB6f67967B99D67aCDFa1712C293601" -def test_init_with_ens_name_arg(w3, contract_with_constructor_address_factory, call): +def test_init_with_ens_name_arg( + w3, contract_with_constructor_address_factory, call +): with contract_ens_addresses( contract_with_constructor_address_factory, [("arg-name.eth", "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413")], @@ -335,7 +374,9 @@ def test_call_get_bytes_value_strict_by_default(bytes_contract, call): def test_call_get_bytes_value_non_strict(non_strict_bytes_contract, call): - result = call(contract=non_strict_bytes_contract, contract_function="getValue") + result = call( + contract=non_strict_bytes_contract, contract_function="getValue" + ) assert result == b"\x04\x06" @@ -437,7 +478,12 @@ def test_call_address_list_reflector_with_address( def test_call_address_reflector_single_name(address_reflector_contract, call): with contract_ens_addresses( address_reflector_contract, - [("dennisthepeasant.eth", "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413")], + [ + ( + "dennisthepeasant.eth", + "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413", + ) + ], ): result = call( contract=address_reflector_contract, @@ -457,7 +503,9 @@ def test_call_address_reflector_name_array(address_reflector_contract, call): "0xFeC2079e80465cc8C687fFF9EE6386ca447aFec4", ] - with contract_ens_addresses(address_reflector_contract, zip(names, addresses)): + with contract_ens_addresses( + address_reflector_contract, zip(names, addresses) + ): result = call( contract=address_reflector_contract, contract_function="reflect", @@ -480,7 +528,9 @@ def test_call_rejects_invalid_ens_name(address_reflector_contract, call): def test_call_missing_function(mismatched_math_contract, call): # note: contract being called needs to have a fallback function # (StringContract in this case) - expected_missing_function_error_message = "Could not decode contract function call" + expected_missing_function_error_message = ( + "Could not decode contract function call" + ) with pytest.raises(BadFunctionCallOutput) as exception_info: call(contract=mismatched_math_contract, contract_function="return13") assert expected_missing_function_error_message in str(exception_info.value) @@ -537,7 +587,9 @@ def test_call_receive_fallback_function( def test_call_nonexistent_receive_function(fallback_function_contract): - with pytest.raises(ABIReceiveNotFound, match="No receive function was found"): + with pytest.raises( + ABIReceiveNotFound, match="No receive function was found" + ): fallback_function_contract.receive.call() @@ -563,7 +615,9 @@ def test_accepts_block_hash_as_identifier(w3, math_contract): math_contract.functions.incrementCounter().transact() more_blocks = w3.provider.make_request(method="evm_mine", params=[5]) - old = math_contract.functions.counter().call(block_identifier=blocks["result"][2]) + old = math_contract.functions.counter().call( + block_identifier=blocks["result"][2] + ) new = math_contract.functions.counter().call( block_identifier=more_blocks["result"][2] ) @@ -591,8 +645,12 @@ def test_returns_data_from_specified_block(w3, math_contract): math_contract.functions.incrementCounter().transact() math_contract.functions.incrementCounter().transact() - output1 = math_contract.functions.counter().call(block_identifier=start_num + 6) - output2 = math_contract.functions.counter().call(block_identifier=start_num + 7) + output1 = math_contract.functions.counter().call( + block_identifier=start_num + 6 + ) + output2 = math_contract.functions.counter().call( + block_identifier=start_num + 7 + ) assert output1 == 1 assert output2 == 2 @@ -787,12 +845,18 @@ def test_call_abi_no_functions(w3): contract.functions.thisFunctionDoesNotExist().call() -def test_call_not_sending_ether_to_nonpayable_function(payable_tester_contract, call): - result = call(contract=payable_tester_contract, contract_function="doNoValueCall") +def test_call_not_sending_ether_to_nonpayable_function( + payable_tester_contract, call +): + result = call( + contract=payable_tester_contract, contract_function="doNoValueCall" + ) assert result == [] -def test_call_sending_ether_to_nonpayable_function(payable_tester_contract, call): +def test_call_sending_ether_to_nonpayable_function( + payable_tester_contract, call +): with pytest.raises(Web3ValidationError): call( contract=payable_tester_contract, @@ -817,7 +881,10 @@ def test_call_sending_ether_to_nonpayable_function(payable_tester_contract, call "reflect(ufixed256x80)", Decimal(2**256 - 1) / 10**80, ), # maximum allowed value - ("reflect(ufixed256x80)", Decimal(1) / 10**80), # smallest non-zero value + ( + "reflect(ufixed256x80)", + Decimal(1) / 10**80, + ), # smallest non-zero value # minimum value (for ufixed8x1) ("reflect_short_u", 0), # maximum value (for ufixed8x1) @@ -1066,7 +1133,9 @@ def test_call_tuple_contract_with_decode_tuples_set( type_str, namedtuple_repr, ): - result = tuple_contract_with_decode_tuples.functions.method(method_input).call() + result = tuple_contract_with_decode_tuples.functions.method( + method_input + ).call() # check contract output matches dict_to_namedtuple output namedtuple_from_input = recursive_dict_to_namedtuple(method_input) @@ -1163,7 +1232,9 @@ def test_call_tuple_contract_with_decode_tuples_set( ), ), ) -def test_call_nested_tuple_contract(nested_tuple_contract, method_input, expected): +def test_call_nested_tuple_contract( + nested_tuple_contract, method_input, expected +): result = nested_tuple_contract.functions.method(method_input).call() assert result == expected @@ -1295,11 +1366,15 @@ async def async_bytes_contract( @pytest_asyncio.fixture( params=[ "0x0406040604060406040604060406040604060406040604060406040604060406", - HexBytes("0406040604060406040604060406040604060406040604060406040604060406"), + HexBytes( + "0406040604060406040604060406040604060406040604060406040604060406" + ), ], ) async def async_bytes32_contract(async_w3, request, address_conversion_func): - async_bytes32_contract_factory = async_w3.eth.contract(**BYTES32_CONTRACT_DATA) + async_bytes32_contract_factory = async_w3.eth.contract( + **BYTES32_CONTRACT_DATA + ) return await async_deploy( async_w3, async_bytes32_contract_factory, @@ -1315,7 +1390,9 @@ async def async_undeployed_math_contract( empty_address = address_conversion_func( "0x000000000000000000000000000000000000dEaD" ) - _undeployed_math_contract = async_math_contract_factory(address=empty_address) + _undeployed_math_contract = async_math_contract_factory( + address=empty_address + ) return _undeployed_math_contract @@ -1326,15 +1403,19 @@ async def async_mismatched_math_contract( async_math_contract_factory, address_conversion_func, ): - deploy_txn = await async_string_contract_factory.constructor("Caqalai").transact() - deploy_receipt = await async_w3.eth.wait_for_transaction_receipt(deploy_txn) + deploy_txn = await async_string_contract_factory.constructor( + "Caqalai" + ).transact() + deploy_receipt = await async_w3.eth.wait_for_transaction_receipt( + deploy_txn + ) assert deploy_receipt is not None address = address_conversion_func(deploy_receipt["contractAddress"]) _mismatched_math_contract = async_math_contract_factory(address=address) return _mismatched_math_contract -@pytest_asyncio.fixture +@pytest.mark.asyncio async def test_async_deploy_raises_due_to_strict_byte_checking_by_default( async_w3, async_bytes_contract_factory, address_conversion_func ): @@ -1352,14 +1433,16 @@ async def test_async_deploy_raises_due_to_strict_byte_checking_by_default( @pytest.mark.asyncio -@pytest.mark.parametrize("args", ("0x0406", "0406", HexBytes("0406"), b"\x04\x06")) +@pytest.mark.parametrize( + "args", ("0x0406", "0406", HexBytes("0406"), b"\x04\x06") +) async def test_async_deploy_with_non_strict_abi_check( async_w3_non_strict_abi, address_conversion_func, args, ): - async_non_strict_bytes_contract_factory = async_w3_non_strict_abi.eth.contract( - **BYTES_CONTRACT_DATA + async_non_strict_bytes_contract_factory = ( + async_w3_non_strict_abi.eth.contract(**BYTES_CONTRACT_DATA) ) deployed_contract = await async_deploy( async_w3_non_strict_abi, @@ -1414,13 +1497,17 @@ async def test_async_call_with_multiple_arguments( async def test_async_saved_method_call_with_multiple_arguments( async_math_contract, call_args, call_kwargs ): - math_contract_add = async_math_contract.functions.add(*call_args, **call_kwargs) + math_contract_add = async_math_contract.functions.add( + *call_args, **call_kwargs + ) result = await math_contract_add.call() assert result == 16 @pytest.mark.asyncio -async def test_async_call_get_w3_value(async_function_name_tester_contract, async_call): +async def test_async_call_get_w3_value( + async_function_name_tester_contract, async_call +): result = await async_call( contract=async_function_name_tester_contract, contract_function="w3" ) @@ -1452,9 +1539,12 @@ async def test_async_call_get_bytes32_array(async_arrays_contract, async_call): @pytest.mark.asyncio -async def test_async_call_get_bytes32_const_array(async_arrays_contract, async_call): +async def test_async_call_get_bytes32_const_array( + async_arrays_contract, async_call +): result = await async_call( - contract=async_arrays_contract, contract_function="getBytes32ConstValue" + contract=async_arrays_contract, + contract_function="getBytes32ConstValue", ) # expected_bytes32_array = [keccak('A'), keccak('B')] expected_bytes32_array = [ @@ -1478,16 +1568,23 @@ async def test_async_call_get_byte_array_non_strict( async_non_strict_arrays_contract, async_call ): result = await async_call( - contract=async_non_strict_arrays_contract, contract_function="getByteValue" + contract=async_non_strict_arrays_contract, + contract_function="getByteValue", ) expected_non_strict_byte_arr = [b"\xff", b"\xff", b"\xff", b"\xff"] assert result == expected_non_strict_byte_arr @pytest.mark.asyncio -@pytest.mark.parametrize("args,expected", [([b""], [b"\x00"]), (["0x"], [b"\x00"])]) +@pytest.mark.parametrize( + "args,expected", [([b""], [b"\x00"]), (["0x"], [b"\x00"])] +) async def test_async_set_byte_array_non_strict( - async_non_strict_arrays_contract, async_call, async_transact, args, expected + async_non_strict_arrays_contract, + async_call, + async_transact, + args, + expected, ): await async_transact( contract=async_non_strict_arrays_contract, @@ -1495,14 +1592,17 @@ async def test_async_set_byte_array_non_strict( func_args=[args], ) result = await async_call( - contract=async_non_strict_arrays_contract, contract_function="getByteValue" + contract=async_non_strict_arrays_contract, + contract_function="getByteValue", ) assert result == expected @pytest.mark.asyncio -@pytest.mark.parametrize("args,expected", [([b"1"], [b"1"]), (["0xDe"], [b"\xde"])]) +@pytest.mark.parametrize( + "args,expected", [([b"1"], [b"1"]), (["0xDe"], [b"\xde"])] +) async def test_async_set_byte_array_strict_by_default( async_arrays_contract, async_call, async_transact, args, expected ): @@ -1536,7 +1636,8 @@ async def test_async_call_get_byte_const_array_non_strict( async_non_strict_arrays_contract, async_call ): result = await async_call( - contract=async_non_strict_arrays_contract, contract_function="getByteConstValue" + contract=async_non_strict_arrays_contract, + contract_function="getByteConstValue", ) expected_byte_arr = [b"\x00", b"\x01"] assert result == expected_byte_arr @@ -1570,12 +1671,16 @@ async def test_async_init_with_ens_name_arg( ], ) - result = await async_call(contract=address_contract, contract_function="testAddr") + result = await async_call( + contract=address_contract, contract_function="testAddr" + ) assert result == "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413" @pytest.mark.asyncio -async def test_async_call_read_bytes_variable(async_bytes_contract, async_call): +async def test_async_call_read_bytes_variable( + async_bytes_contract, async_call +): result = await async_call( contract=async_bytes_contract, contract_function="constValue" ) @@ -1591,7 +1696,9 @@ async def test_async_call_get_bytes_value(async_bytes_contract, async_call): @pytest.mark.asyncio -async def test_async_call_read_bytes32_variable(async_bytes32_contract, async_call): +async def test_async_call_read_bytes32_variable( + async_bytes32_contract, async_call +): result = await async_call( contract=async_bytes32_contract, contract_function="constValue" ) @@ -1602,7 +1709,9 @@ async def test_async_call_read_bytes32_variable(async_bytes32_contract, async_ca @pytest.mark.asyncio -async def test_async_call_get_bytes32_value(async_bytes32_contract, async_call): +async def test_async_call_get_bytes32_value( + async_bytes32_contract, async_call +): result = await async_call( contract=async_bytes32_contract, contract_function="getValue" ) @@ -1700,7 +1809,12 @@ async def test_async_call_address_reflector_single_name( ): with contract_ens_addresses( async_address_reflector_contract, - [("dennisthepeasant.eth", "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413")], + [ + ( + "dennisthepeasant.eth", + "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413", + ) + ], ): result = await async_call( contract=async_address_reflector_contract, @@ -1751,13 +1865,18 @@ async def test_async_call_rejects_invalid_ens_name( @pytest.mark.asyncio -async def test_async_call_missing_function(async_mismatched_math_contract, async_call): +async def test_async_call_missing_function( + async_mismatched_math_contract, async_call +): # note: contract being called needs to have a fallback function # (StringContract in this case) - expected_missing_function_error_message = "Could not decode contract function call" + expected_missing_function_error_message = ( + "Could not decode contract function call" + ) with pytest.raises(BadFunctionCallOutput) as exception_info: await async_call( - contract=async_mismatched_math_contract, contract_function="return13" + contract=async_mismatched_math_contract, + contract_function="return13", ) assert expected_missing_function_error_message in str(exception_info.value) @@ -1771,7 +1890,8 @@ async def test_async_call_undeployed_contract( ) with pytest.raises(BadFunctionCallOutput) as exception_info: await async_call( - contract=async_undeployed_math_contract, contract_function="return13" + contract=async_undeployed_math_contract, + contract_function="return13", ) assert expected_undeployed_call_error_message in str(exception_info.value) @@ -1810,12 +1930,16 @@ async def test_async_call_receive_fallback_function( else: raise AssertionError("contract must be either receive or no_receive") - initial_value = await async_call(contract=contract, contract_function="getText") + initial_value = await async_call( + contract=contract, contract_function="getText" + ) assert initial_value == "" to = {"to": contract.address} merged = {**to, **tx_params} await async_w3.eth.send_transaction(merged) - final_value = await async_call(contract=contract, contract_function="getText") + final_value = await async_call( + contract=contract, contract_function="getText" + ) assert final_value == expected @@ -1823,15 +1947,21 @@ async def test_async_call_receive_fallback_function( async def test_async_call_nonexistent_receive_function( async_fallback_function_contract, ): - with pytest.raises(ABIReceiveNotFound, match="No receive function was found"): + with pytest.raises( + ABIReceiveNotFound, match="No receive function was found" + ): await async_fallback_function_contract.receive.call() @pytest.mark.asyncio -async def test_async_throws_error_if_block_out_of_range(async_w3, async_math_contract): +async def test_async_throws_error_if_block_out_of_range( + async_w3, async_math_contract +): await async_w3.provider.make_request(method="evm_mine", params=[20]) with pytest.raises(BlockNumberOutOfRange): - await async_math_contract.functions.counter().call(block_identifier=-50) + await async_math_contract.functions.counter().call( + block_identifier=-50 + ) @pytest.mark.asyncio @@ -1839,7 +1969,9 @@ async def test_async_accepts_latest_block(async_w3, async_math_contract): await async_w3.provider.make_request(method="evm_mine", params=[5]) await async_math_contract.functions.incrementCounter().transact() - late = await async_math_contract.functions.counter().call(block_identifier="latest") + late = await async_math_contract.functions.counter().call( + block_identifier="latest" + ) pend = await async_math_contract.functions.counter().call( block_identifier="pending" ) @@ -1849,10 +1981,16 @@ async def test_async_accepts_latest_block(async_w3, async_math_contract): @pytest.mark.asyncio -async def test_async_accepts_block_hash_as_identifier(async_w3, async_math_contract): - blocks = await async_w3.provider.make_request(method="evm_mine", params=[5]) +async def test_async_accepts_block_hash_as_identifier( + async_w3, async_math_contract +): + blocks = await async_w3.provider.make_request( + method="evm_mine", params=[5] + ) await async_math_contract.functions.incrementCounter().transact() - more_blocks = await async_w3.provider.make_request(method="evm_mine", params=[5]) + more_blocks = await async_w3.provider.make_request( + method="evm_mine", params=[5] + ) old = await async_math_contract.functions.counter().call( block_identifier=blocks["result"][2] @@ -1866,14 +2004,20 @@ async def test_async_accepts_block_hash_as_identifier(async_w3, async_math_contr @pytest.mark.asyncio -async def test_async_neg_block_indexes_from_the_end(async_w3, async_math_contract): +async def test_async_neg_block_indexes_from_the_end( + async_w3, async_math_contract +): await async_w3.provider.make_request(method="evm_mine", params=[5]) await async_math_contract.functions.incrementCounter().transact() await async_math_contract.functions.incrementCounter().transact() await async_w3.provider.make_request(method="evm_mine", params=[5]) - output1 = await async_math_contract.functions.counter().call(block_identifier=-7) - output2 = await async_math_contract.functions.counter().call(block_identifier=-6) + output1 = await async_math_contract.functions.counter().call( + block_identifier=-7 + ) + output2 = await async_math_contract.functions.counter().call( + block_identifier=-6 + ) assert output1 == 1 assert output2 == 2 @@ -1942,7 +2086,9 @@ async def test_async_function_1_match_identifier_wrong_args_encoding( ), ), ) -async def test_async_function_multiple_error_diagnoses(async_w3, arg1, arg2, message): +async def test_async_function_multiple_error_diagnoses( + async_w3, arg1, arg2, message +): Contract = async_w3.eth.contract(abi=MULTIPLE_FUNCTIONS) with pytest.raises(MismatchedABI, match=re.escape(message)): if arg2: @@ -1970,7 +2116,8 @@ async def test_async_call_not_sending_ether_to_nonpayable_function( async_payable_tester_contract, async_call ): result = await async_call( - contract=async_payable_tester_contract, contract_function="doNoValueCall" + contract=async_payable_tester_contract, + contract_function="doNoValueCall", ) assert result == [] @@ -2004,7 +2151,10 @@ async def test_async_call_sending_ether_to_nonpayable_function( "reflect(ufixed256x80)", Decimal(2**256 - 1) / 10**80, ), # maximum allowed value - ("reflect(ufixed256x80)", Decimal(1) / 10**80), # smallest non-zero value + ( + "reflect(ufixed256x80)", + Decimal(1) / 10**80, + ), # smallest non-zero value # minimum value (for ufixed8x1) ("reflect_short_u", 0), # maximum value (for ufixed8x1) @@ -2056,7 +2206,11 @@ async def test_async_reflect_fixed_value( Decimal("25.4" + "9" * DEFAULT_DECIMALS), NO_MATCHING_ARGUMENTS, ), - ("reflect(ufixed256x80)", Decimal(1) / 10**81, MULTIPLE_MATCHING_ELEMENTS), + ( + "reflect(ufixed256x80)", + Decimal(1) / 10**81, + MULTIPLE_MATCHING_ELEMENTS, + ), # floats not accepted, for floating point error concerns ("reflect_short_u", 0.1, NO_MATCHING_ARGUMENTS), ), @@ -2171,7 +2325,9 @@ async def test_async_invalid_fixed_value_reflections( ), ), ) -async def test_async_call_tuple_contract(async_tuple_contract, method_input, expected): +async def test_async_call_tuple_contract( + async_tuple_contract, method_input, expected +): result = await async_tuple_contract.functions.method(method_input).call() assert result == expected @@ -2342,7 +2498,9 @@ async def test_async_call_tuple_contract_with_decode_tuples_set( async def test_async_call_nested_tuple_contract( async_nested_tuple_contract, method_input, expected ): - result = await async_nested_tuple_contract.functions.method(method_input).call() + result = await async_nested_tuple_contract.functions.method( + method_input + ).call() assert result == expected @@ -2399,9 +2557,11 @@ async def test_async_call_nested_tuple_contract_with_decode_tuples_set( type_str, namedtuple_repr, ): - result = await async_nested_tuple_contract_with_decode_tuples.functions.method( - method_input - ).call() + result = ( + await async_nested_tuple_contract_with_decode_tuples.functions.method( + method_input + ).call() + ) # check contract output matches dict_to_namedtuple output namedtuple_from_input = recursive_dict_to_namedtuple(method_input) @@ -2423,7 +2583,9 @@ async def test_async_call_revert_contract(async_revert_contract): # eth-tester will do a gas estimation if we don't submit a gas value, # which does not contain the revert reason. Avoid that by giving a gas # value. - await async_revert_contract.functions.revertWithMessage().call({"gas": 100000}) + await async_revert_contract.functions.revertWithMessage().call( + {"gas": 100000} + ) @pytest.mark.asyncio @@ -2433,7 +2595,9 @@ async def test_async_call_with_no_arguments(async_math_contract, call): @pytest.mark.asyncio -async def test_async_call_with_no_arguments_no_parens(async_math_contract, call): +async def test_async_call_with_no_arguments_no_parens( + async_math_contract, call +): result = await async_math_contract.functions.return13.call() assert result == 13 @@ -2445,7 +2609,9 @@ async def test_async_call_with_one_argument(async_math_contract, call): @pytest.mark.asyncio -async def test_async_returns_data_from_specified_block(async_w3, async_math_contract): +async def test_async_returns_data_from_specified_block( + async_w3, async_math_contract +): start_num = await async_w3.eth.get_block("latest") await async_w3.provider.make_request(method="evm_mine", params=[5]) await async_math_contract.functions.incrementCounter().transact() @@ -2463,21 +2629,32 @@ async def test_async_returns_data_from_specified_block(async_w3, async_math_cont @pytest.mark.asyncio -async def test_async_changing_default_block_identifier(async_w3, async_math_contract): +async def test_async_changing_default_block_identifier( + async_w3, async_math_contract +): assert await async_math_contract.caller.counter() == 0 assert async_w3.eth.default_block == "latest" await async_math_contract.functions.incrementCounter(7).transact() assert await async_math_contract.caller.counter() == 7 - assert await async_math_contract.functions.counter().call(block_identifier=1) == 0 + assert ( + await async_math_contract.functions.counter().call(block_identifier=1) + == 0 + ) async_w3.eth.default_block = 1 assert ( - await async_math_contract.functions.counter().call(block_identifier=None) == 0 + await async_math_contract.functions.counter().call( + block_identifier=None + ) + == 0 ) async_w3.eth.default_block = 0x2 assert ( - await async_math_contract.functions.counter().call(block_identifier=None) == 7 + await async_math_contract.functions.counter().call( + block_identifier=None + ) + == 7 ) diff --git a/web3/_utils/module_testing/module_testing_utils.py b/web3/_utils/module_testing/module_testing_utils.py index f17d84cdfc..c986bd8625 100644 --- a/web3/_utils/module_testing/module_testing_utils.py +++ b/web3/_utils/module_testing/module_testing_utils.py @@ -24,6 +24,9 @@ HexBytes, ) import requests +from websockets.protocol import ( + State, +) from web3._utils.http import ( DEFAULT_HTTP_TIMEOUT, @@ -159,7 +162,7 @@ async def _mock_specific_request( class WebSocketMessageStreamMock: - closed: bool = False + state: State = State.OPEN def __init__( self, messages: Collection[bytes] = None, raise_exception: Exception = None diff --git a/web3/_utils/module_testing/utils.py b/web3/_utils/module_testing/utils.py index 37f298da92..8f9bac7908 100644 --- a/web3/_utils/module_testing/utils.py +++ b/web3/_utils/module_testing/utils.py @@ -1,7 +1,6 @@ from asyncio import ( iscoroutinefunction, ) -import copy from typing import ( TYPE_CHECKING, Any, @@ -113,12 +112,11 @@ def __init__( "AsyncMakeRequestFn", "MakeRequestFn" ] = w3.provider.make_request + self._mock_request_counter = 1 + def _build_request_id(self) -> int: - request_id = ( - next(copy.deepcopy(self.w3.provider.request_counter)) - if hasattr(self.w3.provider, "request_counter") - else 1 - ) + request_id = self._mock_request_counter + self._mock_request_counter += 1 return request_id def __enter__(self) -> "Self": @@ -144,7 +142,11 @@ def _mock_request_handler( if all( method not in mock_dict - for mock_dict in (self.mock_errors, self.mock_results, self.mock_responses) + for mock_dict in ( + self.mock_errors, + self.mock_results, + self.mock_responses, + ) ): return self._make_request(method, params) @@ -265,7 +267,11 @@ async def _async_mock_request_handler( self._make_request = cast("AsyncMakeRequestFn", self._make_request) if all( method not in mock_dict - for mock_dict in (self.mock_errors, self.mock_results, self.mock_responses) + for mock_dict in ( + self.mock_errors, + self.mock_results, + self.mock_responses, + ) ): return await self._make_request(method, params) mocked_result = await self._async_build_mock_result(method, params) @@ -289,7 +295,11 @@ async def _async_mock_send_handler( ) -> "RPCRequest": if all( method not in mock_dict - for mock_dict in (self.mock_errors, self.mock_results, self.mock_responses) + for mock_dict in ( + self.mock_errors, + self.mock_results, + self.mock_responses, + ) ): return await self._send_request(method, params) else: @@ -304,7 +314,11 @@ async def _async_mock_recv_handler( request_id = rpc_request["id"] if all( method not in mock_dict - for mock_dict in (self.mock_errors, self.mock_results, self.mock_responses) + for mock_dict in ( + self.mock_errors, + self.mock_results, + self.mock_responses, + ) ): return await self._recv_for_request(request_id) mocked_result = await self._async_build_mock_result( diff --git a/web3/providers/async_base.py b/web3/providers/async_base.py index 50eaa6b6d1..4b1bfa0d92 100644 --- a/web3/providers/async_base.py +++ b/web3/providers/async_base.py @@ -49,8 +49,8 @@ ) if TYPE_CHECKING: - from websockets.legacy.client import ( - WebSocketClientProtocol, + from websockets.asyncio.client import ( + ClientConnection, ) from web3 import ( # noqa: F401 @@ -169,7 +169,7 @@ async def disconnect(self) -> None: ) # WebSocket typing - _ws: "WebSocketClientProtocol" + _ws: "ClientConnection" # IPC typing _reader: asyncio.StreamReader | None diff --git a/web3/providers/persistent/websocket.py b/web3/providers/persistent/websocket.py index 4c18c8b5f7..6620f46dcb 100644 --- a/web3/providers/persistent/websocket.py +++ b/web3/providers/persistent/websocket.py @@ -12,13 +12,16 @@ from toolz import ( merge, ) +from websockets.asyncio.client import ( + ClientConnection, + connect, +) from websockets.exceptions import ( ConnectionClosedOK, WebSocketException, ) -from websockets.legacy.client import ( - WebSocketClientProtocol, - connect, +from websockets.protocol import ( + State, ) from web3.exceptions import ( @@ -65,11 +68,13 @@ def __init__( ) -> None: # initialize the endpoint_uri before calling the super constructor self.endpoint_uri = ( - URI(endpoint_uri) if endpoint_uri is not None else get_default_endpoint() + URI(endpoint_uri) + if endpoint_uri is not None + else get_default_endpoint() ) super().__init__(**kwargs) self.use_text_frames = use_text_frames - self._ws: WebSocketClientProtocol | None = None + self._ws: ClientConnection | None = None if not any( self.endpoint_uri.startswith(prefix) @@ -90,7 +95,9 @@ def __init__( f"{found_restricted_keys}." ) - self.websocket_kwargs = merge(DEFAULT_WEBSOCKET_KWARGS, websocket_kwargs or {}) + self.websocket_kwargs = merge( + DEFAULT_WEBSOCKET_KWARGS, websocket_kwargs or {} + ) def __str__(self) -> str: return f"WebSocket connection: {self.endpoint_uri}" @@ -120,7 +127,9 @@ async def socket_send(self, request_data: bytes) -> None: if self.use_text_frames: payload = request_data.decode("utf-8") - await asyncio.wait_for(self._ws.send(payload), timeout=self.request_timeout) + await asyncio.wait_for( + self._ws.send(payload), timeout=self.request_timeout + ) async def socket_recv(self) -> RPCResponse: raw_response = await self._ws.recv() @@ -131,9 +140,12 @@ async def socket_recv(self) -> RPCResponse: async def _provider_specific_connect(self) -> None: self._ws = await connect(self.endpoint_uri, **self.websocket_kwargs) + def conn_is_open(self) -> bool: + return self._ws.state == State.OPEN + async def _provider_specific_disconnect(self) -> None: # this should remain idempotent - if self._ws is not None and not self._ws.closed: + if self._ws is not None and self.conn_is_open(): await self._ws.close() self._ws = None From c508f1e54bcf4f449c9f5c84b936f9c8aaacd85f Mon Sep 17 00:00:00 2001 From: kclowes Date: Thu, 6 Nov 2025 14:53:52 -0700 Subject: [PATCH 3/8] Add newsfragments --- newsfragments/3779.breaking.rst | 1 + newsfragments/3779.feature.rst | 1 + 2 files changed, 2 insertions(+) create mode 100644 newsfragments/3779.breaking.rst create mode 100644 newsfragments/3779.feature.rst diff --git a/newsfragments/3779.breaking.rst b/newsfragments/3779.breaking.rst new file mode 100644 index 0000000000..e982f1db8c --- /dev/null +++ b/newsfragments/3779.breaking.rst @@ -0,0 +1 @@ +Upgrade websockets requirement to >=14.0. diff --git a/newsfragments/3779.feature.rst b/newsfragments/3779.feature.rst new file mode 100644 index 0000000000..35b51feb8a --- /dev/null +++ b/newsfragments/3779.feature.rst @@ -0,0 +1 @@ +Add support for Python 3.14 From 3118048013e0cc227d13d28d9aeb60aeedcc2f84 Mon Sep 17 00:00:00 2001 From: kclowes Date: Thu, 6 Nov 2025 16:16:11 -0700 Subject: [PATCH 4/8] Get rid of iscoroutinefunction deprecation warning --- tests/utils.py | 3 ++- web3/_utils/caching/caching_utils.py | 6 ++---- web3/_utils/module_testing/utils.py | 10 ++++------ web3/providers/persistent/websocket.py | 4 ++-- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/tests/utils.py b/tests/utils.py index 0ec8d97717..0937c12d46 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,4 +1,5 @@ import asyncio +import inspect import socket from web3._utils.threads import ( @@ -65,7 +66,7 @@ async def _async_wait_for_transaction_fixture_logic(async_w3, txn_hash, timeout= def async_partial(f, *args, **kwargs): async def f2(*args2, **kwargs2): result = f(*args, *args2, **kwargs, **kwargs2) - if asyncio.iscoroutinefunction(f): + if inspect.iscoroutinefunction(f): result = await result return result diff --git a/web3/_utils/caching/caching_utils.py b/web3/_utils/caching/caching_utils.py index 08783172a2..8692da3aee 100644 --- a/web3/_utils/caching/caching_utils.py +++ b/web3/_utils/caching/caching_utils.py @@ -1,8 +1,6 @@ -from asyncio import ( - iscoroutinefunction, -) import collections import hashlib +import inspect import threading from typing import ( TYPE_CHECKING, @@ -330,7 +328,7 @@ async def _async_should_cache_response( cache_validator = ASYNC_INTERNAL_VALIDATION_MAP[method] return ( await cache_validator(provider, params, result) - if iscoroutinefunction(cache_validator) + if inspect.iscoroutinefunction(cache_validator) else cache_validator(provider, params, result) ) return True diff --git a/web3/_utils/module_testing/utils.py b/web3/_utils/module_testing/utils.py index 8f9bac7908..ad70d402f5 100644 --- a/web3/_utils/module_testing/utils.py +++ b/web3/_utils/module_testing/utils.py @@ -1,6 +1,4 @@ -from asyncio import ( - iscoroutinefunction, -) +import inspect from typing import ( TYPE_CHECKING, Any, @@ -226,7 +224,7 @@ async def _async_build_mock_result( if callable(mock_return): mock_return = mock_return(method, params) - elif iscoroutinefunction(mock_return): + elif inspect.iscoroutinefunction(mock_return): # this is the "correct" way to mock the async make_request mock_return = await mock_return(method, params) @@ -241,7 +239,7 @@ async def _async_build_mock_result( if callable(mock_return): # handle callable to make things easier since we're mocking mock_return = mock_return(method, params) - elif iscoroutinefunction(mock_return): + elif inspect.iscoroutinefunction(mock_return): # this is the "correct" way to mock the async make_request mock_return = await mock_return(method, params) @@ -251,7 +249,7 @@ async def _async_build_mock_result( error = self.mock_errors[method] if callable(error): error = error(method, params) - elif iscoroutinefunction(error): + elif inspect.iscoroutinefunction(error): error = await error(method, params) mocked_result = merge(response_dict, self._create_error_object(error)) diff --git a/web3/providers/persistent/websocket.py b/web3/providers/persistent/websocket.py index 6620f46dcb..355e33f43a 100644 --- a/web3/providers/persistent/websocket.py +++ b/web3/providers/persistent/websocket.py @@ -140,12 +140,12 @@ async def socket_recv(self) -> RPCResponse: async def _provider_specific_connect(self) -> None: self._ws = await connect(self.endpoint_uri, **self.websocket_kwargs) - def conn_is_open(self) -> bool: + def is_open(self) -> bool: return self._ws.state == State.OPEN async def _provider_specific_disconnect(self) -> None: # this should remain idempotent - if self._ws is not None and self.conn_is_open(): + if self._ws is not None and self.is_open(): await self._ws.close() self._ws = None From cd063ca7ac62d55c5789824414c5bff08f923c19 Mon Sep 17 00:00:00 2001 From: kclowes Date: Mon, 15 Dec 2025 12:11:13 -0700 Subject: [PATCH 5/8] Properly close WebSocketProvider in tests --- setup.py | 2 +- .../contracts/test_contract_call_interface.py | 271 +++++------------- .../core/providers/test_websocket_provider.py | 63 ++-- .../module_testing/module_testing_utils.py | 4 +- 4 files changed, 110 insertions(+), 230 deletions(-) diff --git a/setup.py b/setup.py index e10f37ef39..151d9024ec 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ "towncrier>=24,<25", ], "test": [ - "pytest-asyncio>=0.18.1", + "pytest-asyncio>=1.0.0", "pytest-mock>=1.10", "pytest-xdist>=2.4.0", "pytest>=7.0.0", diff --git a/tests/core/contracts/test_contract_call_interface.py b/tests/core/contracts/test_contract_call_interface.py index 82a6c3a891..8d09b0535d 100644 --- a/tests/core/contracts/test_contract_call_interface.py +++ b/tests/core/contracts/test_contract_call_interface.py @@ -98,14 +98,10 @@ def bytes32_contract_factory(w3): @pytest.fixture( params=[ "0x0406040604060406040604060406040604060406040604060406040604060406", - HexBytes( - "0406040604060406040604060406040604060406040604060406040604060406" - ), + HexBytes("0406040604060406040604060406040604060406040604060406040604060406"), ] ) -def bytes32_contract( - w3, bytes32_contract_factory, request, address_conversion_func -): +def bytes32_contract(w3, bytes32_contract_factory, request, address_conversion_func): return deploy( w3, bytes32_contract_factory, @@ -143,9 +139,7 @@ def test_deploy_raises_due_to_strict_byte_checking_by_default( w3, bytes32_contract_factory, address_conversion_func, - args=[ - "0406040604060406040604060406040604060406040604060406040604060406" - ], + args=["0406040604060406040604060406040604060406040604060406040604060406"], ) @@ -168,9 +162,7 @@ def test_call_no_arguments_no_parens(math_contract): def test_call_with_one_argument(math_contract, call): - result = call( - contract=math_contract, contract_function="multiply7", func_args=[3] - ) + result = call(contract=math_contract, contract_function="multiply7", func_args=[3]) assert result == 21 @@ -182,9 +174,7 @@ def test_call_with_one_argument(math_contract, call): (tuple(), {"a": 9, "b": 7}), ), ) -def test_call_with_multiple_arguments( - math_contract, call, call_args, call_kwargs -): +def test_call_with_multiple_arguments(math_contract, call, call_args, call_kwargs): result = call( contract=math_contract, contract_function="add", @@ -211,9 +201,7 @@ def test_saved_method_call_with_multiple_arguments( def test_call_get_w3_value(function_name_tester_contract, call): - result = call( - contract=function_name_tester_contract, contract_function="w3" - ) + result = call(contract=function_name_tester_contract, contract_function="w3") assert result is True @@ -226,9 +214,7 @@ def test_call_get_string_value(string_contract, call): def test_call_get_bytes32_array(arrays_contract, call): - result = call( - contract=arrays_contract, contract_function="getBytes32Value" - ) + result = call(contract=arrays_contract, contract_function="getBytes32Value") # expected_bytes32_array = [keccak('0'), keccak('1')] expected_bytes32_array = [ b"\x04HR\xb2\xa6p\xad\xe5@~x\xfb(c\xc5\x1d\xe9\xfc\xb9eB\xa0q\x86\xfe:\xed\xa6\xbb\x8a\x11m", # noqa: E501 @@ -238,9 +224,7 @@ def test_call_get_bytes32_array(arrays_contract, call): def test_call_get_bytes32_const_array(arrays_contract, call): - result = call( - contract=arrays_contract, contract_function="getBytes32ConstValue" - ) + result = call(contract=arrays_contract, contract_function="getBytes32ConstValue") # expected_bytes32_array = [keccak('A'), keccak('B')] expected_bytes32_array = [ b"\x03x?\xac.\xfe\xd8\xfb\xc9\xadD>Y.\xe3\x0ea\xd6_G\x11@\xc1\x0c\xa1U\xe97\xb45\xb7`", # noqa: E501 @@ -256,9 +240,7 @@ def test_call_get_byte_array(arrays_contract, call): def test_call_get_byte_array_non_strict(non_strict_arrays_contract, call): - result = call( - contract=non_strict_arrays_contract, contract_function="getByteValue" - ) + result = call(contract=non_strict_arrays_contract, contract_function="getByteValue") expected_non_strict_byte_arr = [b"\xff", b"\xff", b"\xff", b"\xff"] assert result == expected_non_strict_byte_arr @@ -298,9 +280,7 @@ def test_set_byte_array_non_strict( contract_function="setByteValue", func_args=[args], ) - result = call( - contract=non_strict_arrays_contract, contract_function="getByteValue" - ) + result = call(contract=non_strict_arrays_contract, contract_function="getByteValue") assert result == expected @@ -318,16 +298,12 @@ def test_set_byte_array_with_invalid_args(arrays_contract, transact, args): def test_call_get_byte_const_array_strict_by_default(arrays_contract, call): - result = call( - contract=arrays_contract, contract_function="getByteConstValue" - ) + result = call(contract=arrays_contract, contract_function="getByteConstValue") expected_byte_arr = [b"\x00", b"\x01"] assert result == expected_byte_arr -def test_call_get_byte_const_array_non_strict( - non_strict_arrays_contract, call -): +def test_call_get_byte_const_array_non_strict(non_strict_arrays_contract, call): result = call( contract=non_strict_arrays_contract, contract_function="getByteConstValue", @@ -344,9 +320,7 @@ def test_call_read_address_variable(contract_with_constructor_address, call): assert result == "0xd3CdA913deB6f67967B99D67aCDFa1712C293601" -def test_init_with_ens_name_arg( - w3, contract_with_constructor_address_factory, call -): +def test_init_with_ens_name_arg(w3, contract_with_constructor_address_factory, call): with contract_ens_addresses( contract_with_constructor_address_factory, [("arg-name.eth", "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413")], @@ -374,9 +348,7 @@ def test_call_get_bytes_value_strict_by_default(bytes_contract, call): def test_call_get_bytes_value_non_strict(non_strict_bytes_contract, call): - result = call( - contract=non_strict_bytes_contract, contract_function="getValue" - ) + result = call(contract=non_strict_bytes_contract, contract_function="getValue") assert result == b"\x04\x06" @@ -503,9 +475,7 @@ def test_call_address_reflector_name_array(address_reflector_contract, call): "0xFeC2079e80465cc8C687fFF9EE6386ca447aFec4", ] - with contract_ens_addresses( - address_reflector_contract, zip(names, addresses) - ): + with contract_ens_addresses(address_reflector_contract, zip(names, addresses)): result = call( contract=address_reflector_contract, contract_function="reflect", @@ -528,9 +498,7 @@ def test_call_rejects_invalid_ens_name(address_reflector_contract, call): def test_call_missing_function(mismatched_math_contract, call): # note: contract being called needs to have a fallback function # (StringContract in this case) - expected_missing_function_error_message = ( - "Could not decode contract function call" - ) + expected_missing_function_error_message = "Could not decode contract function call" with pytest.raises(BadFunctionCallOutput) as exception_info: call(contract=mismatched_math_contract, contract_function="return13") assert expected_missing_function_error_message in str(exception_info.value) @@ -587,9 +555,7 @@ def test_call_receive_fallback_function( def test_call_nonexistent_receive_function(fallback_function_contract): - with pytest.raises( - ABIReceiveNotFound, match="No receive function was found" - ): + with pytest.raises(ABIReceiveNotFound, match="No receive function was found"): fallback_function_contract.receive.call() @@ -615,9 +581,7 @@ def test_accepts_block_hash_as_identifier(w3, math_contract): math_contract.functions.incrementCounter().transact() more_blocks = w3.provider.make_request(method="evm_mine", params=[5]) - old = math_contract.functions.counter().call( - block_identifier=blocks["result"][2] - ) + old = math_contract.functions.counter().call(block_identifier=blocks["result"][2]) new = math_contract.functions.counter().call( block_identifier=more_blocks["result"][2] ) @@ -645,12 +609,8 @@ def test_returns_data_from_specified_block(w3, math_contract): math_contract.functions.incrementCounter().transact() math_contract.functions.incrementCounter().transact() - output1 = math_contract.functions.counter().call( - block_identifier=start_num + 6 - ) - output2 = math_contract.functions.counter().call( - block_identifier=start_num + 7 - ) + output1 = math_contract.functions.counter().call(block_identifier=start_num + 6) + output2 = math_contract.functions.counter().call(block_identifier=start_num + 7) assert output1 == 1 assert output2 == 2 @@ -845,18 +805,12 @@ def test_call_abi_no_functions(w3): contract.functions.thisFunctionDoesNotExist().call() -def test_call_not_sending_ether_to_nonpayable_function( - payable_tester_contract, call -): - result = call( - contract=payable_tester_contract, contract_function="doNoValueCall" - ) +def test_call_not_sending_ether_to_nonpayable_function(payable_tester_contract, call): + result = call(contract=payable_tester_contract, contract_function="doNoValueCall") assert result == [] -def test_call_sending_ether_to_nonpayable_function( - payable_tester_contract, call -): +def test_call_sending_ether_to_nonpayable_function(payable_tester_contract, call): with pytest.raises(Web3ValidationError): call( contract=payable_tester_contract, @@ -1133,9 +1087,7 @@ def test_call_tuple_contract_with_decode_tuples_set( type_str, namedtuple_repr, ): - result = tuple_contract_with_decode_tuples.functions.method( - method_input - ).call() + result = tuple_contract_with_decode_tuples.functions.method(method_input).call() # check contract output matches dict_to_namedtuple output namedtuple_from_input = recursive_dict_to_namedtuple(method_input) @@ -1232,9 +1184,7 @@ def test_call_tuple_contract_with_decode_tuples_set( ), ), ) -def test_call_nested_tuple_contract( - nested_tuple_contract, method_input, expected -): +def test_call_nested_tuple_contract(nested_tuple_contract, method_input, expected): result = nested_tuple_contract.functions.method(method_input).call() assert result == expected @@ -1366,15 +1316,11 @@ async def async_bytes_contract( @pytest_asyncio.fixture( params=[ "0x0406040604060406040604060406040604060406040604060406040604060406", - HexBytes( - "0406040604060406040604060406040604060406040604060406040604060406" - ), + HexBytes("0406040604060406040604060406040604060406040604060406040604060406"), ], ) async def async_bytes32_contract(async_w3, request, address_conversion_func): - async_bytes32_contract_factory = async_w3.eth.contract( - **BYTES32_CONTRACT_DATA - ) + async_bytes32_contract_factory = async_w3.eth.contract(**BYTES32_CONTRACT_DATA) return await async_deploy( async_w3, async_bytes32_contract_factory, @@ -1390,9 +1336,7 @@ async def async_undeployed_math_contract( empty_address = address_conversion_func( "0x000000000000000000000000000000000000dEaD" ) - _undeployed_math_contract = async_math_contract_factory( - address=empty_address - ) + _undeployed_math_contract = async_math_contract_factory(address=empty_address) return _undeployed_math_contract @@ -1403,12 +1347,8 @@ async def async_mismatched_math_contract( async_math_contract_factory, address_conversion_func, ): - deploy_txn = await async_string_contract_factory.constructor( - "Caqalai" - ).transact() - deploy_receipt = await async_w3.eth.wait_for_transaction_receipt( - deploy_txn - ) + deploy_txn = await async_string_contract_factory.constructor("Caqalai").transact() + deploy_receipt = await async_w3.eth.wait_for_transaction_receipt(deploy_txn) assert deploy_receipt is not None address = address_conversion_func(deploy_receipt["contractAddress"]) _mismatched_math_contract = async_math_contract_factory(address=address) @@ -1433,16 +1373,14 @@ async def test_async_deploy_raises_due_to_strict_byte_checking_by_default( @pytest.mark.asyncio -@pytest.mark.parametrize( - "args", ("0x0406", "0406", HexBytes("0406"), b"\x04\x06") -) +@pytest.mark.parametrize("args", ("0x0406", "0406", HexBytes("0406"), b"\x04\x06")) async def test_async_deploy_with_non_strict_abi_check( async_w3_non_strict_abi, address_conversion_func, args, ): - async_non_strict_bytes_contract_factory = ( - async_w3_non_strict_abi.eth.contract(**BYTES_CONTRACT_DATA) + async_non_strict_bytes_contract_factory = async_w3_non_strict_abi.eth.contract( + **BYTES_CONTRACT_DATA ) deployed_contract = await async_deploy( async_w3_non_strict_abi, @@ -1497,17 +1435,13 @@ async def test_async_call_with_multiple_arguments( async def test_async_saved_method_call_with_multiple_arguments( async_math_contract, call_args, call_kwargs ): - math_contract_add = async_math_contract.functions.add( - *call_args, **call_kwargs - ) + math_contract_add = async_math_contract.functions.add(*call_args, **call_kwargs) result = await math_contract_add.call() assert result == 16 @pytest.mark.asyncio -async def test_async_call_get_w3_value( - async_function_name_tester_contract, async_call -): +async def test_async_call_get_w3_value(async_function_name_tester_contract, async_call): result = await async_call( contract=async_function_name_tester_contract, contract_function="w3" ) @@ -1539,9 +1473,7 @@ async def test_async_call_get_bytes32_array(async_arrays_contract, async_call): @pytest.mark.asyncio -async def test_async_call_get_bytes32_const_array( - async_arrays_contract, async_call -): +async def test_async_call_get_bytes32_const_array(async_arrays_contract, async_call): result = await async_call( contract=async_arrays_contract, contract_function="getBytes32ConstValue", @@ -1576,9 +1508,7 @@ async def test_async_call_get_byte_array_non_strict( @pytest.mark.asyncio -@pytest.mark.parametrize( - "args,expected", [([b""], [b"\x00"]), (["0x"], [b"\x00"])] -) +@pytest.mark.parametrize("args,expected", [([b""], [b"\x00"]), (["0x"], [b"\x00"])]) async def test_async_set_byte_array_non_strict( async_non_strict_arrays_contract, async_call, @@ -1600,9 +1530,7 @@ async def test_async_set_byte_array_non_strict( @pytest.mark.asyncio -@pytest.mark.parametrize( - "args,expected", [([b"1"], [b"1"]), (["0xDe"], [b"\xde"])] -) +@pytest.mark.parametrize("args,expected", [([b"1"], [b"1"]), (["0xDe"], [b"\xde"])]) async def test_async_set_byte_array_strict_by_default( async_arrays_contract, async_call, async_transact, args, expected ): @@ -1671,16 +1599,12 @@ async def test_async_init_with_ens_name_arg( ], ) - result = await async_call( - contract=address_contract, contract_function="testAddr" - ) + result = await async_call(contract=address_contract, contract_function="testAddr") assert result == "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413" @pytest.mark.asyncio -async def test_async_call_read_bytes_variable( - async_bytes_contract, async_call -): +async def test_async_call_read_bytes_variable(async_bytes_contract, async_call): result = await async_call( contract=async_bytes_contract, contract_function="constValue" ) @@ -1696,9 +1620,7 @@ async def test_async_call_get_bytes_value(async_bytes_contract, async_call): @pytest.mark.asyncio -async def test_async_call_read_bytes32_variable( - async_bytes32_contract, async_call -): +async def test_async_call_read_bytes32_variable(async_bytes32_contract, async_call): result = await async_call( contract=async_bytes32_contract, contract_function="constValue" ) @@ -1709,9 +1631,7 @@ async def test_async_call_read_bytes32_variable( @pytest.mark.asyncio -async def test_async_call_get_bytes32_value( - async_bytes32_contract, async_call -): +async def test_async_call_get_bytes32_value(async_bytes32_contract, async_call): result = await async_call( contract=async_bytes32_contract, contract_function="getValue" ) @@ -1865,14 +1785,10 @@ async def test_async_call_rejects_invalid_ens_name( @pytest.mark.asyncio -async def test_async_call_missing_function( - async_mismatched_math_contract, async_call -): +async def test_async_call_missing_function(async_mismatched_math_contract, async_call): # note: contract being called needs to have a fallback function # (StringContract in this case) - expected_missing_function_error_message = ( - "Could not decode contract function call" - ) + expected_missing_function_error_message = "Could not decode contract function call" with pytest.raises(BadFunctionCallOutput) as exception_info: await async_call( contract=async_mismatched_math_contract, @@ -1930,16 +1846,12 @@ async def test_async_call_receive_fallback_function( else: raise AssertionError("contract must be either receive or no_receive") - initial_value = await async_call( - contract=contract, contract_function="getText" - ) + initial_value = await async_call(contract=contract, contract_function="getText") assert initial_value == "" to = {"to": contract.address} merged = {**to, **tx_params} await async_w3.eth.send_transaction(merged) - final_value = await async_call( - contract=contract, contract_function="getText" - ) + final_value = await async_call(contract=contract, contract_function="getText") assert final_value == expected @@ -1947,21 +1859,15 @@ async def test_async_call_receive_fallback_function( async def test_async_call_nonexistent_receive_function( async_fallback_function_contract, ): - with pytest.raises( - ABIReceiveNotFound, match="No receive function was found" - ): + with pytest.raises(ABIReceiveNotFound, match="No receive function was found"): await async_fallback_function_contract.receive.call() @pytest.mark.asyncio -async def test_async_throws_error_if_block_out_of_range( - async_w3, async_math_contract -): +async def test_async_throws_error_if_block_out_of_range(async_w3, async_math_contract): await async_w3.provider.make_request(method="evm_mine", params=[20]) with pytest.raises(BlockNumberOutOfRange): - await async_math_contract.functions.counter().call( - block_identifier=-50 - ) + await async_math_contract.functions.counter().call(block_identifier=-50) @pytest.mark.asyncio @@ -1969,9 +1875,7 @@ async def test_async_accepts_latest_block(async_w3, async_math_contract): await async_w3.provider.make_request(method="evm_mine", params=[5]) await async_math_contract.functions.incrementCounter().transact() - late = await async_math_contract.functions.counter().call( - block_identifier="latest" - ) + late = await async_math_contract.functions.counter().call(block_identifier="latest") pend = await async_math_contract.functions.counter().call( block_identifier="pending" ) @@ -1981,16 +1885,10 @@ async def test_async_accepts_latest_block(async_w3, async_math_contract): @pytest.mark.asyncio -async def test_async_accepts_block_hash_as_identifier( - async_w3, async_math_contract -): - blocks = await async_w3.provider.make_request( - method="evm_mine", params=[5] - ) +async def test_async_accepts_block_hash_as_identifier(async_w3, async_math_contract): + blocks = await async_w3.provider.make_request(method="evm_mine", params=[5]) await async_math_contract.functions.incrementCounter().transact() - more_blocks = await async_w3.provider.make_request( - method="evm_mine", params=[5] - ) + more_blocks = await async_w3.provider.make_request(method="evm_mine", params=[5]) old = await async_math_contract.functions.counter().call( block_identifier=blocks["result"][2] @@ -2004,20 +1902,14 @@ async def test_async_accepts_block_hash_as_identifier( @pytest.mark.asyncio -async def test_async_neg_block_indexes_from_the_end( - async_w3, async_math_contract -): +async def test_async_neg_block_indexes_from_the_end(async_w3, async_math_contract): await async_w3.provider.make_request(method="evm_mine", params=[5]) await async_math_contract.functions.incrementCounter().transact() await async_math_contract.functions.incrementCounter().transact() await async_w3.provider.make_request(method="evm_mine", params=[5]) - output1 = await async_math_contract.functions.counter().call( - block_identifier=-7 - ) - output2 = await async_math_contract.functions.counter().call( - block_identifier=-6 - ) + output1 = await async_math_contract.functions.counter().call(block_identifier=-7) + output2 = await async_math_contract.functions.counter().call(block_identifier=-6) assert output1 == 1 assert output2 == 2 @@ -2086,9 +1978,7 @@ async def test_async_function_1_match_identifier_wrong_args_encoding( ), ), ) -async def test_async_function_multiple_error_diagnoses( - async_w3, arg1, arg2, message -): +async def test_async_function_multiple_error_diagnoses(async_w3, arg1, arg2, message): Contract = async_w3.eth.contract(abi=MULTIPLE_FUNCTIONS) with pytest.raises(MismatchedABI, match=re.escape(message)): if arg2: @@ -2325,9 +2215,7 @@ async def test_async_invalid_fixed_value_reflections( ), ), ) -async def test_async_call_tuple_contract( - async_tuple_contract, method_input, expected -): +async def test_async_call_tuple_contract(async_tuple_contract, method_input, expected): result = await async_tuple_contract.functions.method(method_input).call() assert result == expected @@ -2498,9 +2386,7 @@ async def test_async_call_tuple_contract_with_decode_tuples_set( async def test_async_call_nested_tuple_contract( async_nested_tuple_contract, method_input, expected ): - result = await async_nested_tuple_contract.functions.method( - method_input - ).call() + result = await async_nested_tuple_contract.functions.method(method_input).call() assert result == expected @@ -2557,11 +2443,9 @@ async def test_async_call_nested_tuple_contract_with_decode_tuples_set( type_str, namedtuple_repr, ): - result = ( - await async_nested_tuple_contract_with_decode_tuples.functions.method( - method_input - ).call() - ) + result = await async_nested_tuple_contract_with_decode_tuples.functions.method( + method_input + ).call() # check contract output matches dict_to_namedtuple output namedtuple_from_input = recursive_dict_to_namedtuple(method_input) @@ -2583,9 +2467,7 @@ async def test_async_call_revert_contract(async_revert_contract): # eth-tester will do a gas estimation if we don't submit a gas value, # which does not contain the revert reason. Avoid that by giving a gas # value. - await async_revert_contract.functions.revertWithMessage().call( - {"gas": 100000} - ) + await async_revert_contract.functions.revertWithMessage().call({"gas": 100000}) @pytest.mark.asyncio @@ -2595,9 +2477,7 @@ async def test_async_call_with_no_arguments(async_math_contract, call): @pytest.mark.asyncio -async def test_async_call_with_no_arguments_no_parens( - async_math_contract, call -): +async def test_async_call_with_no_arguments_no_parens(async_math_contract, call): result = await async_math_contract.functions.return13.call() assert result == 13 @@ -2609,9 +2489,7 @@ async def test_async_call_with_one_argument(async_math_contract, call): @pytest.mark.asyncio -async def test_async_returns_data_from_specified_block( - async_w3, async_math_contract -): +async def test_async_returns_data_from_specified_block(async_w3, async_math_contract): start_num = await async_w3.eth.get_block("latest") await async_w3.provider.make_request(method="evm_mine", params=[5]) await async_math_contract.functions.incrementCounter().transact() @@ -2629,32 +2507,21 @@ async def test_async_returns_data_from_specified_block( @pytest.mark.asyncio -async def test_async_changing_default_block_identifier( - async_w3, async_math_contract -): +async def test_async_changing_default_block_identifier(async_w3, async_math_contract): assert await async_math_contract.caller.counter() == 0 assert async_w3.eth.default_block == "latest" await async_math_contract.functions.incrementCounter(7).transact() assert await async_math_contract.caller.counter() == 7 - assert ( - await async_math_contract.functions.counter().call(block_identifier=1) - == 0 - ) + assert await async_math_contract.functions.counter().call(block_identifier=1) == 0 async_w3.eth.default_block = 1 assert ( - await async_math_contract.functions.counter().call( - block_identifier=None - ) - == 0 + await async_math_contract.functions.counter().call(block_identifier=None) == 0 ) async_w3.eth.default_block = 0x2 assert ( - await async_math_contract.functions.counter().call( - block_identifier=None - ) - == 7 + await async_math_contract.functions.counter().call(block_identifier=None) == 7 ) diff --git a/tests/core/providers/test_websocket_provider.py b/tests/core/providers/test_websocket_provider.py index 5b08bde61b..c5cf86c9b6 100644 --- a/tests/core/providers/test_websocket_provider.py +++ b/tests/core/providers/test_websocket_provider.py @@ -14,6 +14,9 @@ ConnectionClosed, ConnectionClosedOK, ) +from websockets.protocol import ( + State, +) from web3 import ( AsyncWeb3, @@ -42,12 +45,12 @@ def _mock_ws(provider): provider._ws = AsyncMock() - provider._ws.closed = False + provider._ws.state = State.OPEN async def _mocked_ws_conn(): _conn = AsyncMock() - _conn.closed = False + _conn.state = State.OPEN return _conn @@ -354,17 +357,21 @@ async def test_async_iterator_pattern_exception_handling_for_requests(): raise_exception=ConnectionClosed(None, None) ), ): - async for w3 in AsyncWeb3(WebSocketProvider("ws://mocked")): - try: - await w3.eth.block_number - except ConnectionClosed: - if iterations == 3: - break - else: - iterations += 1 - continue - - pytest.fail("Expected `ConnectionClosed` exception.") + agen = AsyncWeb3(WebSocketProvider("ws://mocked")).__aiter__() + try: + async for w3 in agen: + try: + await w3.eth.block_number + except ConnectionClosed: + if iterations == 3: + break + else: + iterations += 1 + continue + + pytest.fail("Expected `ConnectionClosed` exception.") + finally: + await agen.aclose() assert iterations == 3 @@ -379,19 +386,23 @@ async def test_async_iterator_pattern_exception_handling_for_subscriptions(): raise_exception=ConnectionClosed(None, None) ), ): - async for w3 in AsyncWeb3(WebSocketProvider("ws://mocked")): - try: - async for _ in w3.socket.process_subscriptions(): - # raises exception - pass - except ConnectionClosed: - if iterations == 3: - break - else: - iterations += 1 - continue - - pytest.fail("Expected `ConnectionClosed` exception.") + agen = AsyncWeb3(WebSocketProvider("ws://mocked")).__aiter__() + try: + async for w3 in agen: + try: + async for _ in w3.socket.process_subscriptions(): + # raises exception + pass + except ConnectionClosed: + if iterations == 3: + break + else: + iterations += 1 + continue + + pytest.fail("Expected `ConnectionClosed` exception.") + finally: + await agen.aclose() assert iterations == 3 diff --git a/web3/_utils/module_testing/module_testing_utils.py b/web3/_utils/module_testing/module_testing_utils.py index c986bd8625..7bbf278438 100644 --- a/web3/_utils/module_testing/module_testing_utils.py +++ b/web3/_utils/module_testing/module_testing_utils.py @@ -165,7 +165,9 @@ class WebSocketMessageStreamMock: state: State = State.OPEN def __init__( - self, messages: Collection[bytes] = None, raise_exception: Exception = None + self, + messages: Collection[bytes] = None, + raise_exception: Exception = None, ) -> None: self.queue = asyncio.Queue() # type: ignore # py38 issue for msg in messages or []: From e2f0df7e5b01419f3e49f1030cf328f92c4bd293 Mon Sep 17 00:00:00 2001 From: kclowes Date: Mon, 15 Dec 2025 13:12:46 -0700 Subject: [PATCH 6/8] Install pre-releases via tox --- .circleci/config.yml | 1 - tox.ini | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b06956cafe..6b8f4aecad 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -33,7 +33,6 @@ common: &common command: | python -m pip install --upgrade pip python -m pip install tox - python web3/scripts/install_pre_releases.py - run: name: run tox command: python -m tox run -r diff --git a/tox.ini b/tox.ini index 8b274a1c83..1b1e86bd7f 100644 --- a/tox.ini +++ b/tox.ini @@ -19,6 +19,9 @@ max_issue_threshold=1 allowlist_externals=make,pre-commit install_command=python -m pip install {opts} {packages} usedevelop=True +commands_pre= + python {toxinidir}/web3/scripts/install_pre_releases.py + python -m pip freeze --all commands= core: pytest {posargs:tests/core -m "not asyncio" -n auto --maxprocesses=15} core_async: pytest {posargs:tests/core -m asyncio -n auto --maxprocesses=15} From 82dd38ef6fa74dccd98e0ece465da6cdccf68d1a Mon Sep 17 00:00:00 2001 From: kclowes Date: Mon, 15 Dec 2025 13:55:55 -0700 Subject: [PATCH 7/8] Lint --- web3/providers/persistent/websocket.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/web3/providers/persistent/websocket.py b/web3/providers/persistent/websocket.py index 355e33f43a..6d2efe4706 100644 --- a/web3/providers/persistent/websocket.py +++ b/web3/providers/persistent/websocket.py @@ -68,9 +68,7 @@ def __init__( ) -> None: # initialize the endpoint_uri before calling the super constructor self.endpoint_uri = ( - URI(endpoint_uri) - if endpoint_uri is not None - else get_default_endpoint() + URI(endpoint_uri) if endpoint_uri is not None else get_default_endpoint() ) super().__init__(**kwargs) self.use_text_frames = use_text_frames @@ -95,9 +93,7 @@ def __init__( f"{found_restricted_keys}." ) - self.websocket_kwargs = merge( - DEFAULT_WEBSOCKET_KWARGS, websocket_kwargs or {} - ) + self.websocket_kwargs = merge(DEFAULT_WEBSOCKET_KWARGS, websocket_kwargs or {}) def __str__(self) -> str: return f"WebSocket connection: {self.endpoint_uri}" @@ -127,9 +123,7 @@ async def socket_send(self, request_data: bytes) -> None: if self.use_text_frames: payload = request_data.decode("utf-8") - await asyncio.wait_for( - self._ws.send(payload), timeout=self.request_timeout - ) + await asyncio.wait_for(self._ws.send(payload), timeout=self.request_timeout) async def socket_recv(self) -> RPCResponse: raw_response = await self._ws.recv() From b642e38bdfacb50b8db4c75a252f457ef042251e Mon Sep 17 00:00:00 2001 From: kclowes Date: Mon, 15 Dec 2025 21:06:47 -0700 Subject: [PATCH 8/8] Monkey patch cached_property to remove warnings --- conftest.py | 9 +++++++++ setup.py | 1 + 2 files changed, 10 insertions(+) diff --git a/conftest.py b/conftest.py index 06cbbdcacc..c02cf9eeb5 100644 --- a/conftest.py +++ b/conftest.py @@ -1,7 +1,14 @@ import pytest +import inspect import time import warnings +# Monkey-patch cached_property to use inspect.iscoroutinefunction +# instead of the deprecated asyncio.iscoroutinefunction (deprecated in Python 3.14+) +# cached_property is a dependency of py-evm, so once we remove py-evm +# or once cached_property gets a release, we can remove this. +# https://github.com/pydanny/cached-property/issues/276 +import cached_property as _cached_property_module import pytest_asyncio from tests.utils import ( @@ -20,6 +27,8 @@ EthereumTesterProvider, ) +_cached_property_module.asyncio.iscoroutinefunction = inspect.iscoroutinefunction + @pytest.fixture() def sleep_interval(): diff --git a/setup.py b/setup.py index 151d9024ec..b888d23ab7 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,7 @@ "tox>=4.0.0", "mypy==1.10.0", "pre-commit>=3.4.0", + "cached-property>=2.0.1", ], }