From eead3d6cb6f9cfde8432a16c75bd9760c4c481c2 Mon Sep 17 00:00:00 2001 From: Paul Robinson Date: Fri, 2 Dec 2022 14:11:57 -0700 Subject: [PATCH] async more filter tests (#2732) * async existing filter test * async test_filter_against_transaction_logs * async test_filter_against_pending_transactions * async test_contract_get_logs * async test_contract_topic_filters * async test_contract_past_event_filtering * async test_contract_on_event_filtering * async test_contract_data_filters --- newsfragments/2744.feature.rst | 1 + tests/core/filtering/conftest.py | 16 +- .../core/filtering/test_basic_filter_tests.py | 10 +- .../filtering/test_contract_data_filters.py | 292 +++++++++++++++--- .../core/filtering/test_contract_get_logs.py | 110 +++++++ .../test_contract_on_event_filtering.py | 261 ++++++++++++++++ .../test_contract_past_event_filtering.py | 100 ++++++ .../filtering/test_contract_topic_filters.py | 292 +++++++++++++++--- .../test_existing_filter_instance.py | 51 ++- .../test_filter_against_latest_blocks.py | 5 +- ...est_filter_against_pending_transactions.py | 55 +--- .../test_filter_against_transaction_logs.py | 47 +-- .../test_filters_against_many_blocks.py | 8 +- tests/core/filtering/utils.py | 8 - web3/module.py | 15 +- 15 files changed, 1062 insertions(+), 209 deletions(-) create mode 100644 newsfragments/2744.feature.rst diff --git a/newsfragments/2744.feature.rst b/newsfragments/2744.feature.rst new file mode 100644 index 0000000000..0221ab2143 --- /dev/null +++ b/newsfragments/2744.feature.rst @@ -0,0 +1 @@ +Added async functionality to filter diff --git a/tests/core/filtering/conftest.py b/tests/core/filtering/conftest.py index 56463280ca..58457e856c 100644 --- a/tests/core/filtering/conftest.py +++ b/tests/core/filtering/conftest.py @@ -149,7 +149,7 @@ def emitter_log_topics(): return LogTopics -def return_filter(contract=None, args=[]): +def return_filter(contract, args): event_name = args[0] kwargs = apply_key_map({"filter": "argument_filters"}, args[1]) if "fromBlock" not in kwargs: @@ -196,6 +196,14 @@ async def async_emitter( ) -@pytest.fixture(scope="module") -def async_create_filter(request): - return async_partial(return_filter) +async def async_return_filter(contract, args): + event_name = args[0] + kwargs = apply_key_map({"filter": "argument_filters"}, args[1]) + if "fromBlock" not in kwargs: + kwargs["fromBlock"] = "latest" + return await contract.events[event_name].create_filter(**kwargs) + + +@pytest_asyncio.fixture(scope="module") +async def async_create_filter(request): + return async_partial(async_return_filter) diff --git a/tests/core/filtering/test_basic_filter_tests.py b/tests/core/filtering/test_basic_filter_tests.py index 957e0f08e3..cb47271de3 100644 --- a/tests/core/filtering/test_basic_filter_tests.py +++ b/tests/core/filtering/test_basic_filter_tests.py @@ -1,9 +1,5 @@ import pytest -from tests.core.filtering.utils import ( - async_range, -) - def test_filtering_sequential_blocks_with_bounded_range( w3, emitter, Emitter, wait_for_transaction @@ -51,7 +47,7 @@ async def test_async_filtering_sequential_blocks_with_bounded_range( initial_block_number = await async_w3.eth.block_number builder.toBlock = initial_block_number + 100 filter_ = await builder.deploy(async_w3) - async for i in async_range(100): + for i in range(100): await async_emitter.functions.logNoArgs(which=1).transact() eth_block_number = await async_w3.eth.block_number assert eth_block_number == initial_block_number + 100 @@ -61,12 +57,12 @@ async def test_async_filtering_sequential_blocks_with_bounded_range( @pytest.mark.asyncio async def test_async_filtering_starting_block_range(async_w3, async_emitter): - async for i in async_range(10): + for i in range(10): await async_emitter.functions.logNoArgs(which=1).transact() builder = async_emitter.events.LogNoArguments.build_filter() filter_ = await builder.deploy(async_w3) initial_block_number = await async_w3.eth.block_number - async for i in async_range(10): + for i in range(10): await async_emitter.functions.logNoArgs(which=1).transact() eth_block_number = await async_w3.eth.block_number assert eth_block_number == initial_block_number + 10 diff --git a/tests/core/filtering/test_contract_data_filters.py b/tests/core/filtering/test_contract_data_filters.py index eeeeb3c51e..186c0e2544 100644 --- a/tests/core/filtering/test_contract_data_filters.py +++ b/tests/core/filtering/test_contract_data_filters.py @@ -1,3 +1,4 @@ +import asyncio import pytest from hypothesis import ( @@ -5,33 +6,23 @@ settings, strategies as st, ) +import pytest_asyncio -from web3 import Web3 +from tests.core.filtering.utils import ( + _async_emitter_fixture_logic, + _async_w3_fixture_logic, + _emitter_fixture_logic, + _w3_fixture_logic, +) +from tests.utils import ( + _async_wait_for_block_fixture_logic, + _async_wait_for_transaction_fixture_logic, +) from web3._utils.module_testing.emitter_contract import ( CONTRACT_EMITTER_ABI, CONTRACT_EMITTER_CODE, CONTRACT_EMITTER_RUNTIME, ) -from web3.middleware import ( - local_filter_middleware, -) -from web3.providers.eth_tester import ( - EthereumTesterProvider, -) - - -@pytest.fixture( - scope="module", - params=[True, False], - ids=["local_filter_middleware", "node_based_filter"], -) -def w3(request): - use_filter_middleware = request.param - provider = EthereumTesterProvider() - w3 = Web3(provider) - if use_filter_middleware: - w3.middleware_onion.add(local_filter_middleware) - return w3 @pytest.fixture(scope="module") @@ -58,25 +49,6 @@ def EMITTER(EMITTER_CODE, EMITTER_RUNTIME, EMITTER_ABI): } -@pytest.fixture(scope="module") -def Emitter(w3, EMITTER): - return w3.eth.contract(**EMITTER) - - -@pytest.fixture(scope="module") -def emitter(w3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func): - wait_for_block(w3) - deploy_txn_hash = Emitter.constructor().transact({"gas": 10000000}) - deploy_receipt = wait_for_transaction(w3, deploy_txn_hash) - contract_address = address_conversion_func(deploy_receipt["contractAddress"]) - - bytecode = w3.eth.get_code(contract_address) - assert bytecode == Emitter.bytecode_runtime - _emitter = Emitter(address=contract_address) - assert _emitter.address == contract_address - return _emitter - - def not_empty_string(x): return x != "" @@ -130,6 +102,30 @@ def array_values(draw): return (matching, non_matching) +# --- sync --- # + + +@pytest.fixture( + scope="module", + params=[True, False], + ids=["local_filter_middleware", "node_based_filter"], +) +def w3(request): + return _w3_fixture_logic(request) + + +@pytest.fixture(scope="module") +def Emitter(w3, EMITTER): + return w3.eth.contract(**EMITTER) + + +@pytest.fixture(scope="module") +def emitter(w3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func): + return _emitter_fixture_logic( + w3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func + ) + + @pytest.mark.parametrize("api_style", ("v4", "build_filter")) @given(vals=dynamic_values()) @settings(max_examples=5, deadline=None) @@ -284,3 +280,219 @@ def test_data_filters_with_list_arguments( else: with pytest.raises(TypeError): create_filter(emitter, ["LogListArgs", {"filter": {"arg1": matching}}]) + + +# --- async --- # + + +@pytest_asyncio.fixture(scope="module") +async def async_wait_for_block(): + return _async_wait_for_block_fixture_logic + + +@pytest_asyncio.fixture(scope="module") +async def async_wait_for_transaction(): + return _async_wait_for_transaction_fixture_logic + + +@pytest.fixture(scope="module") +def event_loop(): + policy = asyncio.get_event_loop_policy() + loop = policy.new_event_loop() + yield loop + loop.close() + + +@pytest.fixture( + scope="module", + params=[True, False], + ids=["local_filter_middleware", "node_based_filter"], +) +def async_w3(request): + return _async_w3_fixture_logic(request) + + +@pytest.fixture(scope="module") +def AsyncEmitter(async_w3, EMITTER): + return async_w3.eth.contract(**EMITTER) + + +@pytest_asyncio.fixture(scope="module") +async def async_emitter( + async_w3, + AsyncEmitter, + async_wait_for_transaction, + async_wait_for_block, + address_conversion_func, +): + return await _async_emitter_fixture_logic( + async_w3, + AsyncEmitter, + async_wait_for_transaction, + async_wait_for_block, + address_conversion_func, + ) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("api_style", ("v4", "build_filter")) +@given(vals=dynamic_values()) +@settings(max_examples=5, deadline=None) +async def test_async_data_filters_with_dynamic_arguments( + async_w3, + async_wait_for_transaction, + async_create_filter, + async_emitter, + api_style, + vals, +): + if api_style == "build_filter": + filter_builder = async_emitter.events.LogDynamicArgs.build_filter() + filter_builder.args["arg1"].match_single(vals["matching"]) + event_filter = await filter_builder.deploy(async_w3) + else: + event_filter = await async_create_filter( + async_emitter, ["LogDynamicArgs", {"filter": {"arg1": vals["matching"]}}] + ) + + txn_hashes = [ + await async_emitter.functions.logDynamicArgs( + arg0=vals["matching"], arg1=vals["matching"] + ).transact( + {"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, "gas": 400000} + ), + await async_emitter.functions.logDynamicArgs( + arg0=vals["non_matching"][0], arg1=vals["non_matching"][0] + ).transact( + {"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, "gas": 400000} + ), + ] + + for txn_hash in txn_hashes: + await async_wait_for_transaction(async_w3, txn_hash) + + log_entries = await event_filter.get_new_entries() + assert len(log_entries) == 1 + assert log_entries[0]["transactionHash"] == txn_hashes[0] + + +@pytest.mark.asyncio +@pytest.mark.parametrize("api_style", ("v4", "build_filter")) +@given(vals=fixed_values()) +@settings(max_examples=5, deadline=None) +async def test_async_data_filters_with_fixed_arguments( + async_w3, + async_emitter, + async_wait_for_transaction, + async_create_filter, + api_style, + vals, +): + if api_style == "build_filter": + filter_builder = async_emitter.events.LogQuadrupleArg.build_filter() + filter_builder.args["arg0"].match_single(vals["matching"][0]) + filter_builder.args["arg1"].match_single(vals["matching"][1]) + filter_builder.args["arg2"].match_single(vals["matching"][2]) + filter_builder.args["arg3"].match_single(vals["matching"][3]) + event_filter = await filter_builder.deploy(async_w3) + else: + event_filter = await async_create_filter( + async_emitter, + [ + "LogQuadrupleArg", + { + "filter": { + "arg0": vals["matching"][0], + "arg1": vals["matching"][1], + "arg2": vals["matching"][2], + "arg3": vals["matching"][3], + } + }, + ], + ) + + txn_hashes = [] + txn_hashes.append( + await async_emitter.functions.logQuadruple( + which=5, + arg0=vals["matching"][0], + arg1=vals["matching"][1], + arg2=vals["matching"][2], + arg3=vals["matching"][3], + ).transact( + {"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, "gas": 100000} + ) + ) + txn_hashes.append( + await async_emitter.functions.logQuadruple( + which=5, + arg0=vals["non_matching"][0], + arg1=vals["non_matching"][1], + arg2=vals["non_matching"][2], + arg3=vals["non_matching"][3], + ).transact( + {"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, "gas": 100000} + ) + ) + + for txn_hash in txn_hashes: + await async_wait_for_transaction(async_w3, txn_hash) + + log_entries = await event_filter.get_new_entries() + assert len(log_entries) == 1 + assert log_entries[0]["transactionHash"] == txn_hashes[0] + + +@pytest.mark.asyncio +@pytest.mark.parametrize("api_style", ("v4", "build_filter")) +@given(vals=array_values()) +@settings(max_examples=5, deadline=None) +async def test_async_data_filters_with_list_arguments( + async_w3, + async_emitter, + async_wait_for_transaction, + async_create_filter, + api_style, + vals, +): + matching, non_matching = vals + + if api_style == "build_filter": + filter_builder = async_emitter.events.LogListArgs.build_filter() + filter_builder.args["arg1"].match_single(matching) + event_filter = await filter_builder.deploy(async_w3) + + txn_hashes = [] + txn_hashes.append( + await async_emitter.functions.logListArgs( + arg0=matching, arg1=matching + ).transact({"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9}) + ) + txn_hashes.append( + await async_emitter.functions.logListArgs( + arg0=non_matching, arg1=non_matching + ).transact({"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9}) + ) + txn_hashes.append( + await async_emitter.functions.logListArgs( + arg0=non_matching, arg1=matching + ).transact({"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9}) + ) + txn_hashes.append( + await async_emitter.functions.logListArgs( + arg0=matching, arg1=non_matching + ).transact({"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9}) + ) + + for txn_hash in txn_hashes: + await async_wait_for_transaction(async_w3, txn_hash) + + log_entries = await event_filter.get_new_entries() + assert len(log_entries) == 2 + assert log_entries[0]["transactionHash"] == txn_hashes[0] + assert log_entries[1]["transactionHash"] == txn_hashes[2] + else: + with pytest.raises(TypeError): + await async_create_filter( + async_emitter, ["LogListArgs", {"filter": {"arg1": matching}}] + ) diff --git a/tests/core/filtering/test_contract_get_logs.py b/tests/core/filtering/test_contract_get_logs.py index 27efa2348f..eedfe8a970 100644 --- a/tests/core/filtering/test_contract_get_logs.py +++ b/tests/core/filtering/test_contract_get_logs.py @@ -1,3 +1,6 @@ +import pytest + + def test_contract_get_available_events( emitter, ): @@ -83,3 +86,110 @@ def test_contract_getLogs_argument_filter( argument_filters={"arg0": 1}, ) assert len(partial_logs) == 4 + + +# --- async --- # + + +def test_async_contract_get_available_events( + async_emitter, +): + """We can iterate over available contract events""" + contract = async_emitter + events = list(contract.events) + assert len(events) == 19 + + +@pytest.mark.asyncio +async def test_async_contract_getLogs_all( + async_w3, + async_emitter, + async_wait_for_transaction, + emitter_event_ids, +): + contract = async_emitter + event_id = emitter_event_ids.LogNoArguments + + txn_hash = await contract.functions.logNoArgs(event_id).transact() + await async_wait_for_transaction(async_w3, txn_hash) + + contract_logs = await contract.events.LogNoArguments.getLogs() + log_entries = list(contract_logs) + assert len(log_entries) == 1 + assert log_entries[0]["transactionHash"] == txn_hash + + +@pytest.mark.asyncio +async def test_async_contract_getLogs_range( + async_w3, + async_emitter, + async_wait_for_transaction, + emitter_event_ids, +): + contract = async_emitter + event_id = emitter_event_ids.LogNoArguments + + eth_block_number = await async_w3.eth.block_number + assert eth_block_number == 2 + txn_hash = await contract.functions.logNoArgs(event_id).transact() + # Mined as block 3 + await async_wait_for_transaction(async_w3, txn_hash) + eth_block_number = await async_w3.eth.block_number + assert eth_block_number == 3 + + contract_logs = await contract.events.LogNoArguments.getLogs() + log_entries = list(contract_logs) + assert len(log_entries) == 1 + + contract_logs = await contract.events.LogNoArguments.getLogs(fromBlock=2, toBlock=3) + log_entries = list(contract_logs) + assert len(log_entries) == 1 + + contract_logs = await contract.events.LogNoArguments.getLogs(fromBlock=1, toBlock=2) + log_entries = list(contract_logs) + assert len(log_entries) == 0 + + +@pytest.mark.asyncio +async def test_async_contract_getLogs_argument_filter( + async_w3, async_emitter, async_wait_for_transaction, emitter_event_ids +): + + contract = async_emitter + + txn_hashes = [] + event_id = emitter_event_ids.LogTripleWithIndex + # 1 = arg0 + # 4 = arg1 + # 1 = arg2 + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 1, 4, 1).transact() + ) + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 1, 1, 2).transact() + ) + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 1, 2, 2).transact() + ) + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 1, 3, 1).transact() + ) + for txn_hash in txn_hashes: + await async_wait_for_transaction(async_w3, txn_hash) + + all_logs = await contract.events.LogTripleWithIndex.getLogs(fromBlock=1) + assert len(all_logs) == 4 + + # Filter all entries where arg1 in (1, 2) + partial_logs = await contract.events.LogTripleWithIndex.getLogs( + fromBlock=1, + argument_filters={"arg1": [1, 2]}, + ) + assert len(partial_logs) == 2 + + # Filter all entries where arg0 == 1 + partial_logs = await contract.events.LogTripleWithIndex.getLogs( + fromBlock=1, + argument_filters={"arg0": 1}, + ) + assert len(partial_logs) == 4 diff --git a/tests/core/filtering/test_contract_on_event_filtering.py b/tests/core/filtering/test_contract_on_event_filtering.py index 8eb69a70aa..6778ffc8d1 100644 --- a/tests/core/filtering/test_contract_on_event_filtering.py +++ b/tests/core/filtering/test_contract_on_event_filtering.py @@ -1,3 +1,4 @@ +import asyncio import pytest from eth_utils import ( @@ -212,3 +213,263 @@ def test_on_sync_filter_with_topic_filter_options_on_old_apis( old_logs = post_event_filter.get_all_entries() assert len(old_logs) == 4 + + +# --- async --- # + + +@pytest.fixture(scope="module") +def event_loop(): + policy = asyncio.get_event_loop_policy() + loop = policy.new_event_loop() + yield loop + loop.close() + + +@pytest.mark.asyncio +@pytest.mark.parametrize("call_as_instance", (True, False)) +async def test_async_create_filter_address_parameter( + async_emitter, AsyncEmitter, call_as_instance +): + if call_as_instance: + event_filter = await async_emitter.events.LogNoArguments.create_filter( + fromBlock="latest" + ) + else: + event_filter = await AsyncEmitter.events.LogNoArguments.create_filter( + fromBlock="latest" + ) + + if call_as_instance: + # Assert this is a single string value, and not a list of addresses + assert is_address(event_filter.filter_params["address"]) + else: + # Undeployed contract shouldnt have address... + assert "address" not in event_filter.filter_params + + +@pytest.mark.asyncio +@pytest.mark.parametrize("call_as_instance", (True, False)) +@pytest.mark.parametrize("api_style", ("v4", "build_filter")) +async def test_on_async_filter_using_get_entries_interface( + async_w3, + async_emitter, + AsyncEmitter, + async_wait_for_transaction, + emitter_event_ids, + call_as_instance, + api_style, + async_create_filter, +): + + if call_as_instance: + contract = async_emitter + else: + contract = AsyncEmitter + + if api_style == "build_filter": + event_filter = await contract.events.LogNoArguments.build_filter().deploy( + async_w3 + ) + else: + event_filter = await async_create_filter(async_emitter, ["LogNoArguments", {}]) + + txn_hash = await async_emitter.functions.logNoArgs( + emitter_event_ids.LogNoArguments + ).transact() + await async_wait_for_transaction(async_w3, txn_hash) + + log_entries = await event_filter.get_new_entries() + assert len(log_entries) == 1 + assert log_entries[0]["transactionHash"] == txn_hash + + # a second call is empty because all events have been retrieved + new_entries = await event_filter.get_new_entries() + assert len(new_entries) == 0 + + +@pytest.mark.asyncio +@pytest.mark.parametrize("call_as_instance", (True, False)) +@pytest.mark.parametrize("api_style", ("v4", "build_filter")) +async def test_on_async_filter_with_event_name_and_single_argument( + async_w3, + async_emitter, + AsyncEmitter, + async_wait_for_transaction, + emitter_event_ids, + call_as_instance, + api_style, + async_create_filter, +): + + if call_as_instance: + contract = async_emitter + else: + contract = AsyncEmitter + + if api_style == "build_filter": + builder = contract.events.LogTripleWithIndex.build_filter() + builder.args["arg1"].match_single(2) + event_filter = await builder.deploy(async_w3) + else: + event_filter = await async_create_filter( + contract, + [ + "LogTripleWithIndex", + { + "filter": { + "arg1": 2, + } + }, + ], + ) + + txn_hashes = [] + event_id = emitter_event_ids.LogTripleWithIndex + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 2, 1, 3).transact() + ) + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 1, 2, 3).transact() + ) + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 12345, 2, 54321).transact() + ) + for txn_hash in txn_hashes: + await async_wait_for_transaction(async_w3, txn_hash) + + seen_logs = await event_filter.get_new_entries() + assert len(seen_logs) == 2 + assert {log["transactionHash"] for log in seen_logs} == set(txn_hashes[1:]) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("call_as_instance", (True, False)) +@pytest.mark.parametrize("api_style", ("v4", "build_filter")) +async def test_on_async_filter_with_event_name_and_non_indexed_argument( + async_w3, + async_emitter, + AsyncEmitter, + async_wait_for_transaction, + emitter_event_ids, + call_as_instance, + api_style, + async_create_filter, +): + + if call_as_instance: + contract = async_emitter + else: + contract = AsyncEmitter + + if api_style == "build_filter": + builder = contract.events.LogTripleWithIndex.build_filter() + builder.args["arg0"].match_single(1) + builder.args["arg1"].match_single(2) + event_filter = await builder.deploy(async_w3) + else: + event_filter = await async_create_filter( + contract, + [ + "LogTripleWithIndex", + { + "filter": { + "arg0": 1, + "arg1": 2, + } + }, + ], + ) + + txn_hashes = [] + event_id = emitter_event_ids.LogTripleWithIndex + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 2, 1, 3).transact() + ) + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 1, 2, 3).transact() + ) + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 12345, 2, 54321).transact() + ) + for txn_hash in txn_hashes: + await async_wait_for_transaction(async_w3, txn_hash) + + seen_logs = await event_filter.get_new_entries() + assert len(seen_logs) == 1 + assert seen_logs[0]["transactionHash"] == txn_hashes[1] + + post_event_filter = await contract.events.LogTripleWithIndex.create_filter( + argument_filters={"arg0": 1, "arg1": 2}, + fromBlock=0, + ) + + old_logs = await post_event_filter.get_all_entries() + assert len(old_logs) == 1 + assert old_logs[0]["transactionHash"] == txn_hashes[1] + + +@pytest.mark.asyncio +async def test_async_filter_with_contract_address( + async_w3, async_emitter, emitter_event_ids, async_wait_for_transaction +): + event_filter = await async_w3.eth.filter( + filter_params={"address": async_emitter.address} + ) + txn_hash = await async_emitter.functions.logNoArgs( + emitter_event_ids.LogNoArguments + ).transact() + await async_wait_for_transaction(async_w3, txn_hash) + seen_logs = await event_filter.get_new_entries() + assert len(seen_logs) == 1 + assert seen_logs[0]["transactionHash"] == txn_hash + + +@pytest.mark.asyncio +@pytest.mark.parametrize("call_as_instance", (True, False)) +async def test_on_async_filter_with_topic_filter_options_on_old_apis( + async_w3, + async_emitter, + AsyncEmitter, + async_wait_for_transaction, + emitter_event_ids, + call_as_instance, + async_create_filter, +): + + if call_as_instance: + contract = async_emitter + else: + contract = AsyncEmitter + + event_filter = await async_create_filter( + contract, ["LogTripleWithIndex", {"filter": {"arg1": [1, 2], "arg2": [1, 2]}}] + ) + + txn_hashes = [] + event_id = emitter_event_ids.LogTripleWithIndex + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 1, 1, 1).transact() + ) + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 1, 1, 2).transact() + ) + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 1, 2, 2).transact() + ) + txn_hashes.append( + await async_emitter.functions.logTriple(event_id, 1, 2, 1).transact() + ) + for txn_hash in txn_hashes: + await async_wait_for_transaction(async_w3, txn_hash) + + seen_logs = await event_filter.get_new_entries() + assert len(seen_logs) == 4 + + post_event_filter = await contract.events.LogTripleWithIndex.create_filter( + argument_filters={"arg1": [1, 2], "arg2": [1, 2]}, + fromBlock=0, + ) + + old_logs = await post_event_filter.get_all_entries() + assert len(old_logs) == 4 diff --git a/tests/core/filtering/test_contract_past_event_filtering.py b/tests/core/filtering/test_contract_past_event_filtering.py index 3fc66e2aa7..14bb2ef54f 100644 --- a/tests/core/filtering/test_contract_past_event_filtering.py +++ b/tests/core/filtering/test_contract_past_event_filtering.py @@ -1,3 +1,4 @@ +import asyncio import pytest from eth_utils import ( @@ -85,3 +86,102 @@ def test_get_all_entries_returned_block_data( assert event_data["transactionIndex"] == txn_receipt["transactionIndex"] assert is_same_address(event_data["address"], emitter.address) assert event_data["event"] == "LogNoArguments" + + +# --- async --- # + + +@pytest.fixture(scope="module") +def event_loop(): + policy = asyncio.get_event_loop_policy() + loop = policy.new_event_loop() + yield loop + loop.close() + + +@pytest.mark.asyncio +@pytest.mark.parametrize("call_as_instance", (True, False)) +@pytest.mark.parametrize("api_style", ("v4", "build_filter")) +async def test_on_async_filter_using_get_all_entries_interface( + async_w3, + async_emitter, + AsyncEmitter, + async_wait_for_transaction, + emitter_event_ids, + call_as_instance, + api_style, + async_create_filter, +): + if call_as_instance: + contract = async_emitter + else: + contract = AsyncEmitter + + if api_style == "build_filter": + builder = contract.events.LogNoArguments.build_filter() + builder.fromBlock = "latest" + event_filter = await builder.deploy(async_w3) + else: + event_filter = await async_create_filter( + contract, ["LogNoArguments", {"fromBlock": "latest"}] + ) + + txn_hash = await async_emitter.functions.logNoArgs( + emitter_event_ids.LogNoArguments + ).transact() + await async_wait_for_transaction(async_w3, txn_hash) + + log_entries = await event_filter.get_all_entries() + + assert len(log_entries) == 1 + assert log_entries[0]["transactionHash"] == txn_hash + + # a second call still retrieves all results + log_entries_2 = await event_filter.get_all_entries() + + assert len(log_entries_2) == 1 + assert log_entries_2[0]["transactionHash"] == txn_hash + + +@pytest.mark.asyncio +@pytest.mark.parametrize("call_as_instance", (True, False)) +@pytest.mark.parametrize("api_style", ("v4", "build_filter")) +async def test_async_get_all_entries_returned_block_data( + async_w3, + async_emitter, + AsyncEmitter, + async_wait_for_transaction, + emitter_event_ids, + call_as_instance, + api_style, + async_create_filter, +): + txn_hash = await async_emitter.functions.logNoArgs( + emitter_event_ids.LogNoArguments + ).transact() + txn_receipt = await async_wait_for_transaction(async_w3, txn_hash) + + if call_as_instance: + contract = async_emitter + else: + contract = AsyncEmitter + + if api_style == "build_filter": + builder = contract.events.LogNoArguments.build_filter() + builder.fromBlock = txn_receipt["blockNumber"] + event_filter = await builder.deploy(async_w3) + else: + event_filter = await async_create_filter( + contract, ["LogNoArguments", {"fromBlock": txn_receipt["blockNumber"]}] + ) + + log_entries = await event_filter.get_all_entries() + + assert len(log_entries) == 1 + event_data = log_entries[0] + assert event_data["args"] == {} + assert event_data["blockHash"] == txn_receipt["blockHash"] + assert event_data["blockNumber"] == txn_receipt["blockNumber"] + assert event_data["transactionIndex"] == txn_receipt["transactionIndex"] + assert is_same_address(event_data["address"], async_emitter.address) + assert event_data["event"] == "LogNoArguments" diff --git a/tests/core/filtering/test_contract_topic_filters.py b/tests/core/filtering/test_contract_topic_filters.py index 0ea0d74a96..4e94ea80bb 100644 --- a/tests/core/filtering/test_contract_topic_filters.py +++ b/tests/core/filtering/test_contract_topic_filters.py @@ -1,3 +1,4 @@ +import asyncio import pytest from hypothesis import ( @@ -5,38 +6,23 @@ settings, strategies as st, ) +import pytest_asyncio -from web3 import Web3 +from tests.core.filtering.utils import ( + _async_emitter_fixture_logic, + _async_w3_fixture_logic, + _emitter_fixture_logic, + _w3_fixture_logic, +) +from tests.utils import ( + _async_wait_for_block_fixture_logic, + _async_wait_for_transaction_fixture_logic, +) from web3._utils.module_testing.emitter_contract import ( CONTRACT_EMITTER_ABI, CONTRACT_EMITTER_CODE, CONTRACT_EMITTER_RUNTIME, ) -from web3.middleware import ( - local_filter_middleware, -) -from web3.providers.eth_tester import ( - EthereumTesterProvider, -) - - -@pytest.fixture( - scope="module", - params=[True, False], - ids=["local_filter_middleware", "node_based_filter"], -) -def w3(request): - use_filter_middleware = request.param - provider = EthereumTesterProvider() - w3 = Web3(provider) - if use_filter_middleware: - w3.middleware_onion.add(local_filter_middleware) - return w3 - - -@pytest.fixture(autouse=True) -def wait_for_mining_start(w3, wait_for_block): - wait_for_block(w3) @pytest.fixture(scope="module") @@ -63,25 +49,6 @@ def EMITTER(EMITTER_CODE, EMITTER_RUNTIME, EMITTER_ABI): } -@pytest.fixture(scope="module") -def Emitter(w3, EMITTER): - return w3.eth.contract(**EMITTER) - - -@pytest.fixture(scope="module") -def emitter(w3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func): - wait_for_block(w3) - deploy_txn_hash = Emitter.constructor().transact({"gas": 10000000}) - deploy_receipt = wait_for_transaction(w3, deploy_txn_hash) - contract_address = address_conversion_func(deploy_receipt["contractAddress"]) - - bytecode = w3.eth.get_code(contract_address) - assert bytecode == Emitter.bytecode_runtime - _emitter = Emitter(address=contract_address) - assert _emitter.address == contract_address - return _emitter - - def not_empty_string(x): return x != "" @@ -135,6 +102,30 @@ def array_values(draw): return (matching, non_matching) +# --- sync --- # + + +@pytest.fixture( + scope="module", + params=[True, False], + ids=["local_filter_middleware", "node_based_filter"], +) +def w3(request): + return _w3_fixture_logic(request) + + +@pytest.fixture(scope="module") +def Emitter(w3, EMITTER): + return w3.eth.contract(**EMITTER) + + +@pytest.fixture(scope="module") +def emitter(w3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func): + return _emitter_fixture_logic( + w3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func + ) + + @pytest.mark.parametrize("api_style", ("v4", "build_filter")) @given(vals=dynamic_values()) @settings(max_examples=5, deadline=None) @@ -171,16 +162,13 @@ def test_topic_filters_with_dynamic_arguments( assert log_entries[0]["transactionHash"] == txn_hashes[0] -@pytest.mark.parametrize("call_as_instance", (True, False)) @pytest.mark.parametrize("api_style", ("v4", "build_filter")) @given(vals=fixed_values()) @settings(max_examples=5, deadline=None) def test_topic_filters_with_fixed_arguments( w3, emitter, - Emitter, wait_for_transaction, - call_as_instance, create_filter, api_style, vals, @@ -240,12 +228,11 @@ def test_topic_filters_with_fixed_arguments( assert log_entries[0]["transactionHash"] == txn_hashes[0] -@pytest.mark.parametrize("call_as_instance", (True, False)) @pytest.mark.parametrize("api_style", ("v4", "build_filter")) @given(vals=array_values()) @settings(max_examples=5, deadline=None) def test_topic_filters_with_list_arguments( - w3, emitter, wait_for_transaction, call_as_instance, create_filter, api_style, vals + w3, emitter, wait_for_transaction, create_filter, api_style, vals ): matching, non_matching = vals @@ -274,3 +261,208 @@ def test_topic_filters_with_list_arguments( else: with pytest.raises(TypeError): create_filter(emitter, ["LogListArgs", {"filter": {"arg0": matching}}]) + + +# --- async --- # + + +@pytest_asyncio.fixture(scope="module") +async def async_wait_for_block(): + return _async_wait_for_block_fixture_logic + + +@pytest_asyncio.fixture(scope="module") +async def async_wait_for_transaction(): + return _async_wait_for_transaction_fixture_logic + + +@pytest.fixture(scope="module") +def event_loop(): + policy = asyncio.get_event_loop_policy() + loop = policy.new_event_loop() + yield loop + loop.close() + + +@pytest.fixture( + scope="module", + params=[True, False], + ids=["local_filter_middleware", "node_based_filter"], +) +def async_w3(request): + return _async_w3_fixture_logic(request) + + +@pytest_asyncio.fixture(scope="module") +def AsyncEmitter(async_w3, EMITTER): + return async_w3.eth.contract(**EMITTER) + + +@pytest_asyncio.fixture(scope="module") +async def async_emitter( + async_w3, + AsyncEmitter, + async_wait_for_transaction, + async_wait_for_block, + address_conversion_func, +): + return await _async_emitter_fixture_logic( + async_w3, + AsyncEmitter, + async_wait_for_transaction, + async_wait_for_block, + address_conversion_func, + ) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("api_style", ("v4", "build_filter")) +@given(vals=dynamic_values()) +@settings(max_examples=5, deadline=None) +async def test_async_topic_filters_with_dynamic_arguments( + async_w3, + async_emitter, + async_wait_for_transaction, + async_create_filter, + api_style, + vals, +): + if api_style == "build_filter": + + filter_builder = async_emitter.events.LogDynamicArgs.build_filter() + filter_builder.args["arg0"].match_single(vals["matching"]) + event_filter = await filter_builder.deploy(async_w3) + else: + event_filter = await async_create_filter( + async_emitter, ["LogDynamicArgs", {"filter": {"arg0": vals["matching"]}}] + ) + + txn_hashes = [ + await async_emitter.functions.logDynamicArgs( + arg0=vals["matching"], arg1=vals["matching"] + ).transact( + {"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, "gas": 60000} + ), + await async_emitter.functions.logDynamicArgs( + arg0=vals["non_matching"][0], arg1=vals["non_matching"][0] + ).transact( + {"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, "gas": 60000} + ), + ] + + for txn_hash in txn_hashes: + await async_wait_for_transaction(async_w3, txn_hash) + + log_entries = await event_filter.get_new_entries() + assert len(log_entries) == 1 + assert log_entries[0]["transactionHash"] == txn_hashes[0] + + +@pytest.mark.asyncio +@pytest.mark.parametrize("api_style", ("v4", "build_filter")) +@given(vals=fixed_values()) +@settings(max_examples=5, deadline=None) +async def test_async_topic_filters_with_fixed_arguments( + async_w3, + async_emitter, + async_wait_for_transaction, + async_create_filter, + api_style, + vals, +): + if api_style == "build_filter": + filter_builder = async_emitter.events.LogQuadrupleWithIndex.build_filter() + filter_builder.args["arg0"].match_single(vals["matching"][0]) + filter_builder.args["arg1"].match_single(vals["matching"][1]) + filter_builder.args["arg2"].match_single(vals["matching"][2]) + filter_builder.args["arg3"].match_single(vals["matching"][3]) + event_filter = await filter_builder.deploy(async_w3) + else: + event_filter = await async_create_filter( + async_emitter, + [ + "LogQuadrupleWithIndex", + { + "filter": { + "arg0": vals["matching"][0], + "arg1": vals["matching"][1], + "arg2": vals["matching"][2], + "arg3": vals["matching"][3], + } + }, + ], + ) + + txn_hashes = [] + txn_hashes.append( + await async_emitter.functions.logQuadruple( + which=11, + arg0=vals["matching"][0], + arg1=vals["matching"][1], + arg2=vals["matching"][2], + arg3=vals["matching"][3], + ).transact( + {"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, "gas": 60000} + ) + ) + txn_hashes.append( + await async_emitter.functions.logQuadruple( + which=11, + arg0=vals["non_matching"][0], + arg1=vals["non_matching"][1], + arg2=vals["non_matching"][2], + arg3=vals["non_matching"][3], + ).transact( + {"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, "gas": 60000} + ) + ) + + for txn_hash in txn_hashes: + await async_wait_for_transaction(async_w3, txn_hash) + + log_entries = await event_filter.get_new_entries() + assert len(log_entries) == 1 + assert log_entries[0]["transactionHash"] == txn_hashes[0] + + +@pytest.mark.asyncio +@pytest.mark.parametrize("api_style", ("v4", "build_filter")) +@given(vals=array_values()) +@settings(max_examples=5, deadline=None) +async def test_async_topic_filters_with_list_arguments( + async_w3, + async_emitter, + async_wait_for_transaction, + async_create_filter, + api_style, + vals, +): + matching, non_matching = vals + + if api_style == "build_filter": + filter_builder = async_emitter.events.LogListArgs.build_filter() + filter_builder.args["arg0"].match_single(matching) + event_filter = await filter_builder.deploy(async_w3) + txn_hashes = [] + txn_hashes.append( + await async_emitter.functions.logListArgs( + arg0=matching, arg1=matching + ).transact({"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9}) + ) + txn_hashes.append( + await async_emitter.functions.logListArgs( + arg0=non_matching, arg1=non_matching + ).transact({"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9}) + ) + + for txn_hash in txn_hashes: + await async_wait_for_transaction(async_w3, txn_hash) + + log_entries = await event_filter.get_new_entries() + assert len(log_entries) == 1 + assert log_entries[0]["transactionHash"] == txn_hashes[0] + else: + with pytest.raises(TypeError): + await async_create_filter( + async_emitter, ["LogListArgs", {"filter": {"arg0": matching}}] + ) diff --git a/tests/core/filtering/test_existing_filter_instance.py b/tests/core/filtering/test_existing_filter_instance.py index d97523e463..8ae2cdd2ee 100644 --- a/tests/core/filtering/test_existing_filter_instance.py +++ b/tests/core/filtering/test_existing_filter_instance.py @@ -1,18 +1,14 @@ import pytest +import pytest_asyncio + from web3._utils.threads import ( Timeout, ) -from web3.providers.eth_tester import ( - EthereumTesterProvider, -) @pytest.fixture() def filter_id(w3): - if not isinstance(w3.provider, EthereumTesterProvider): - w3.provider = EthereumTesterProvider() - block_filter = w3.eth.filter("latest") return block_filter.filter_id @@ -43,3 +39,46 @@ def test_instantiate_existing_filter(w3, sleep_interval, wait_for_block, filter_ w3.eth.get_block(n + 1).hash for n in range(current_block, current_block + 3) ] assert found_block_hashes == expected_block_hashes + + +# --- async --- # + + +@pytest_asyncio.fixture() +async def async_filter_id(async_w3): + block_filter = await async_w3.eth.filter("latest") + return block_filter.filter_id + + +@pytest.mark.asyncio +async def test_async_instantiate_existing_filter( + async_w3, sleep_interval, async_wait_for_block, async_filter_id +): + with pytest.raises(TypeError): + await async_w3.eth.filter("latest", async_filter_id) + with pytest.raises(TypeError): + await async_w3.eth.filter("latest", filter_id=async_filter_id) + with pytest.raises(TypeError): + await async_w3.eth.filter(filter_params="latest", filter_id=async_filter_id) + + block_filter = await async_w3.eth.filter(filter_id=async_filter_id) + + current_block = await async_w3.eth.block_number + + await async_wait_for_block(async_w3, current_block + 3) + + found_block_hashes = [] + with Timeout(5) as timeout: + while len(found_block_hashes) < 3: + new_entries = await block_filter.get_new_entries() + found_block_hashes.extend(new_entries) + await timeout.async_sleep(sleep_interval()) + + assert len(found_block_hashes) == 3 + + expected_block_hashes = [] + for n in range(current_block, current_block + 3): + next_block = await async_w3.eth.get_block(n + 1) + expected_block_hashes.append(next_block.hash) + + assert found_block_hashes == expected_block_hashes diff --git a/tests/core/filtering/test_filter_against_latest_blocks.py b/tests/core/filtering/test_filter_against_latest_blocks.py index 246f80804a..856154b528 100644 --- a/tests/core/filtering/test_filter_against_latest_blocks.py +++ b/tests/core/filtering/test_filter_against_latest_blocks.py @@ -1,8 +1,5 @@ import pytest -from tests.core.filtering.utils import ( - async_range, -) from web3._utils.threads import ( Timeout, ) @@ -48,7 +45,7 @@ async def test_async_filter_against_latest_blocks( assert len(found_block_hashes) == 3 expected_block_hashes = [] - async for n in async_range(current_block, current_block + 3): + for n in range(current_block, current_block + 3): block = await async_w3.eth.get_block(n + 1) expected_block_hashes.append(block.hash) diff --git a/tests/core/filtering/test_filter_against_pending_transactions.py b/tests/core/filtering/test_filter_against_pending_transactions.py index c23da77412..9a0b8e904e 100644 --- a/tests/core/filtering/test_filter_against_pending_transactions.py +++ b/tests/core/filtering/test_filter_against_pending_transactions.py @@ -1,25 +1,10 @@ import pytest -import random -from flaky import ( - flaky, -) -from web3._utils.threads import ( - Timeout, -) - - -@pytest.mark.skip(reason="fixture 'w3_empty' not found") -@flaky(max_runs=3) def test_sync_filter_against_pending_transactions( - w3_empty, wait_for_transaction, skip_if_testrpc + w3, wait_for_transaction, skip_if_testrpc ): - w3 = w3_empty - skip_if_testrpc(w3) - txn_filter = w3.eth.filter("pending") - txn_1_hash = w3.eth.send_transaction( { "from": w3.eth.coinbase, @@ -38,51 +23,37 @@ def test_sync_filter_against_pending_transactions( wait_for_transaction(w3, txn_1_hash) wait_for_transaction(w3, txn_2_hash) - with Timeout(5) as timeout: - while not txn_filter.get_new_entries(): - timeout.sleep(random.random()) - seen_txns = txn_filter.get_new_entries() assert txn_1_hash in seen_txns assert txn_2_hash in seen_txns -@pytest.mark.skip(reason="fixture 'w3_empty' not found") -@flaky(max_runs=3) -def test_async_filter_against_pending_transactions( - w3_empty, wait_for_transaction, skip_if_testrpc +@pytest.mark.asyncio +async def test_async_filter_against_pending_transactions( + async_w3, async_wait_for_transaction ): - w3 = w3_empty - skip_if_testrpc(w3) - - seen_txns = [] - txn_filter = w3.eth.filter("pending") - txn_filter.watch(seen_txns.append) - - txn_1_hash = w3.eth.send_transaction( + txn_filter = await async_w3.eth.filter("pending") + async_w3_eth_coinbase = await async_w3.eth.coinbase + txn_1_hash = await async_w3.eth.send_transaction( { - "from": w3.eth.coinbase, + "from": async_w3_eth_coinbase, "to": "0xd3CdA913deB6f67967B99D67aCDFa1712C293601", "value": 12345, } ) - txn_2_hash = w3.eth.send_transaction( + txn_2_hash = await async_w3.eth.send_transaction( { - "from": w3.eth.coinbase, + "from": async_w3_eth_coinbase, "to": "0xd3CdA913deB6f67967B99D67aCDFa1712C293601", "value": 54321, } ) - wait_for_transaction(w3, txn_1_hash) - wait_for_transaction(w3, txn_2_hash) - - with Timeout(5) as timeout: - while not seen_txns: - timeout.sleep(random.random()) + await async_wait_for_transaction(async_w3, txn_1_hash) + await async_wait_for_transaction(async_w3, txn_2_hash) - txn_filter.stop_watching(30) + seen_txns = await txn_filter.get_new_entries() assert txn_1_hash in seen_txns assert txn_2_hash in seen_txns diff --git a/tests/core/filtering/test_filter_against_transaction_logs.py b/tests/core/filtering/test_filter_against_transaction_logs.py index 1a769c12ba..49172f0891 100644 --- a/tests/core/filtering/test_filter_against_transaction_logs.py +++ b/tests/core/filtering/test_filter_against_transaction_logs.py @@ -1,24 +1,10 @@ import pytest -import random -from flaky import ( - flaky, -) -from web3._utils.threads import ( - Timeout, -) - - -@pytest.mark.skip(reason="fixture 'w3_empty' not found") -@flaky(max_runs=3) def test_sync_filter_against_log_events( - w3_empty, emitter, wait_for_transaction, emitter_log_topics, emitter_event_ids + w3, emitter, wait_for_transaction, emitter_event_ids ): - w3 = w3_empty - txn_filter = w3.eth.filter({}) - txn_hashes = set() txn_hashes.add( emitter.functions.logNoArgs(emitter_event_ids.LogNoArguments).transact() @@ -27,39 +13,26 @@ def test_sync_filter_against_log_events( for txn_hash in txn_hashes: wait_for_transaction(w3, txn_hash) - with Timeout(5) as timeout: - while not txn_filter.get_new_entries(): - timeout.sleep(random.random()) - seen_logs = txn_filter.get_new_entries() assert txn_hashes == {log["transactionHash"] for log in seen_logs} -@pytest.mark.skip(reason="fixture 'w3_empty' not found") -@flaky(max_runs=3) -def test_async_filter_against_log_events( - w3_empty, emitter, wait_for_transaction, emitter_log_topics, emitter_event_ids +@pytest.mark.asyncio +async def test_async_filter_against_log_events( + async_w3, async_emitter, async_wait_for_transaction, emitter_event_ids ): - w3 = w3_empty - - seen_logs = [] - txn_filter = w3.eth.filter({}) - txn_filter.watch(seen_logs.append) - + txn_filter = await async_w3.eth.filter({}) txn_hashes = set() - txn_hashes.add( - emitter.functions.logNoArgs(emitter_event_ids.LogNoArguments).transact() + await async_emitter.functions.logNoArgs( + emitter_event_ids.LogNoArguments + ).transact() ) for txn_hash in txn_hashes: - wait_for_transaction(w3, txn_hash) - - with Timeout(5) as timeout: - while not seen_logs: - timeout.sleep(random.random()) + await async_wait_for_transaction(async_w3, txn_hash) - txn_filter.stop_watching(30) + seen_logs = await txn_filter.get_new_entries() assert txn_hashes == {log["transactionHash"] for log in seen_logs} diff --git a/tests/core/filtering/test_filters_against_many_blocks.py b/tests/core/filtering/test_filters_against_many_blocks.py index 968cd1666b..9a8633d230 100644 --- a/tests/core/filtering/test_filters_against_many_blocks.py +++ b/tests/core/filtering/test_filters_against_many_blocks.py @@ -5,10 +5,6 @@ to_tuple, ) -from tests.core.filtering.utils import ( - async_range, -) - @to_tuple def deploy_contracts(w3, contract, wait_for_transaction): @@ -156,7 +152,7 @@ def gen_non_matching_transact(): async def async_deploy_contracts(async_w3, contract, async_wait_for_transaction): txs = [] - async for i in async_range(25): + for i in range(25): tx_hash = await contract.constructor().transact() await async_wait_for_transaction(async_w3, tx_hash) tx = await async_w3.eth.get_transaction_receipt(tx_hash) @@ -168,7 +164,7 @@ async def async_deploy_contracts(async_w3, contract, async_wait_for_transaction) async def async_pad_with_transactions(async_w3): accounts = await async_w3.eth.accounts - async for tx_count in async_range(random.randint(0, 10)): + for tx_count in range(random.randint(0, 10)): _from = accounts[random.randint(0, len(accounts) - 1)] _to = accounts[random.randint(0, len(accounts) - 1)] value = 50 + tx_count diff --git a/tests/core/filtering/utils.py b/tests/core/filtering/utils.py index 7382cc2d2b..5c38af984d 100644 --- a/tests/core/filtering/utils.py +++ b/tests/core/filtering/utils.py @@ -1,5 +1,3 @@ -import asyncio - from web3 import Web3 from web3.eth import ( AsyncEth, @@ -68,9 +66,3 @@ async def _async_emitter_fixture_logic( _emitter = AsyncEmitter(address=contract_address) assert _emitter.address == contract_address return _emitter - - -async def async_range(*args): - for i in range(*args): - yield (i) - await asyncio.sleep(0.0) diff --git a/web3/module.py b/web3/module.py index cc5ac7cb8d..b41168d9eb 100644 --- a/web3/module.py +++ b/web3/module.py @@ -17,6 +17,7 @@ ) from web3._utils.filters import ( + AsyncLogFilter, LogFilter, _UseExistingFilter, ) @@ -72,11 +73,15 @@ def caller(*args: Any, **kwargs: Any) -> Union[TReturn, LogFilter]: @curry def retrieve_async_method_call_fn( w3: "Web3", module: "Module", method: Method[Callable[..., Any]] -) -> Callable[..., Coroutine[Any, Any, RPCResponse]]: - async def caller(*args: Any, **kwargs: Any) -> RPCResponse: - (method_str, params), response_formatters = method.process_params( - module, *args, **kwargs - ) +) -> Callable[..., Coroutine[Any, Any, Union[RPCResponse, AsyncLogFilter]]]: + async def caller(*args: Any, **kwargs: Any) -> Union[RPCResponse, AsyncLogFilter]: + try: + (method_str, params), response_formatters = method.process_params( + module, *args, **kwargs + ) + + except _UseExistingFilter as err: + return AsyncLogFilter(eth_module=module, filter_id=err.filter_id) ( result_formatters, error_formatters,