Skip to content

Commit e6e4f9f

Browse files
committed
Move filter to use Method class
1 parent 7251ac7 commit e6e4f9f

16 files changed

+246
-74
lines changed

newsfragments/1778.misc.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Move eth.filter method to use ``Method`` class

tests/core/filtering/test_contract_past_event_filtering.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
is_same_address,
55
)
66

7-
# Ignore warning in pyethereum 1.6 - will go away with the upgrade
8-
pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'")
9-
107

118
@pytest.mark.parametrize('call_as_instance', (True, False))
129
@pytest.mark.parametrize('api_style', ('v4', 'build_filter'))

tests/core/filtering/test_existing_filter_instance.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def filter_id(web3):
1717
return block_filter.filter_id
1818

1919

20-
def test_instatiate_existing_filter(web3, sleep_interval, wait_for_block, filter_id):
20+
def test_instantiate_existing_filter(web3, sleep_interval, wait_for_block, filter_id):
2121
with pytest.raises(TypeError):
2222
web3.eth.filter('latest', filter_id)
2323
with pytest.raises(TypeError):

tests/core/filtering/test_filters_against_many_blocks.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,7 @@ def test_block_filter(web3):
7979
assert len(block_filter.get_new_entries()) == web3.eth.blockNumber
8080

8181

82-
def test_transaction_filter_with_mining(
83-
web3):
82+
def test_transaction_filter_with_mining(web3):
8483

8584
transaction_filter = web3.eth.filter("pending")
8685

tests/core/filtering/test_utils_functions.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@
131131
def test_match_fn_with_various_data_types(web3, data, expected, match_data_and_abi):
132132
abi_types, match_data = zip(*match_data_and_abi)
133133
encoded_data = web3.codec.encode_abi(abi_types, data)
134-
assert match_fn(web3, match_data_and_abi, encoded_data) == expected
134+
assert match_fn(web3.codec, match_data_and_abi, encoded_data) == expected
135135

136136

137137
@pytest.mark.parametrize(
@@ -206,7 +206,7 @@ def test_match_fn_with_various_data_types_strict(w3_strict_abi,
206206
match_data_and_abi):
207207
abi_types, match_data = zip(*match_data_and_abi)
208208
encoded_data = w3_strict_abi.codec.encode_abi(abi_types, data)
209-
assert match_fn(w3_strict_abi, match_data_and_abi, encoded_data) == expected
209+
assert match_fn(w3_strict_abi.codec, match_data_and_abi, encoded_data) == expected
210210

211211

212212
@pytest.mark.parametrize(
@@ -233,4 +233,4 @@ def test_wrong_type_match_data(web3):
233233
abi_types, match_data = zip(*match_data_and_abi)
234234
encoded_data = web3.codec.encode_abi(abi_types, data)
235235
with pytest.raises(ValueError):
236-
match_fn(web3, match_data_and_abi, encoded_data)
236+
match_fn(web3.codec, match_data_and_abi, encoded_data)

tests/core/method-class/test_method.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def test_get_formatters_default_formatter_for_falsy_config():
5454
)
5555

5656
default_request_formatters = method.request_formatters(method.method_selector_fn())
57-
default_result_formatters = method.result_formatters(method.method_selector_fn())
57+
default_result_formatters = method.result_formatters(method.method_selector_fn(), 'some module')
5858
assert _apply_request_formatters(['a', 'b', 'c'], default_request_formatters) == ('a', 'b', 'c')
5959
assert apply_result_formatters(
6060
default_result_formatters, ['a', 'b', 'c']) == ['a', 'b', 'c']

tests/core/method-class/test_result_formatters.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
)
2020

2121

22-
def result_formatter(method):
22+
def result_formatter(method, module):
2323
def formatter(self):
2424
return 'OKAY'
2525
return compose(formatter)

tests/core/middleware/test_filter_middleware.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@
2323

2424
class DummyProvider(BaseProvider):
2525
def make_request(self, method, params):
26-
raise NotImplementedError("Cannot make request for {0}:{1}".format(
27-
method,
28-
params,
29-
))
26+
raise NotImplementedError(f"Cannot make request for {method}:{params}")
3027

3128

3229
BLOCK_HASH = '0xfe88c94d860f01a17f961bf4bdfb6e0c6cd10d3fda5cc861e805ca1240c58553'
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import pytest
2+
3+
from web3._utils.filters import (
4+
_UseExistingFilter,
5+
select_filter_method,
6+
)
7+
from web3.exceptions import (
8+
ValidationError,
9+
)
10+
11+
12+
@pytest.mark.parametrize('value, expected', (
13+
('latest', 'new_block_filter'),
14+
('pending', 'new_pending_tx_filter'),
15+
({'to': '0x' + '00' * 20}, 'new_filter'),
16+
))
17+
def test_select_filter_method(value, expected):
18+
filter_method = select_filter_method(
19+
if_new_block_filter='new_block_filter',
20+
if_new_pending_transaction_filter='new_pending_tx_filter',
21+
if_new_filter='new_filter',
22+
)
23+
assert filter_method(value) == expected
24+
25+
26+
@pytest.mark.parametrize('value, error', (
27+
('0x0', _UseExistingFilter),
28+
('disallowed string', ValidationError),
29+
(1, ValidationError), # filter id needs to be a hexstr
30+
))
31+
def test_select_filter_method_raises_error(value, error):
32+
filter_method = select_filter_method(
33+
if_new_block_filter='new_block_filter',
34+
if_new_pending_transaction_filter='new_pending_tx_filter',
35+
if_new_filter='new_filter',
36+
)
37+
with pytest.raises(error):
38+
filter_method(value)

web3/_utils/filters.py

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
Optional,
1010
Sequence,
1111
Tuple,
12+
Union,
1213
)
1314

1415
from eth_abi.codec import (
@@ -23,6 +24,7 @@
2324
TypeStr,
2425
)
2526
from eth_utils import (
27+
is_hex,
2628
is_list_like,
2729
is_string,
2830
is_text,
@@ -46,15 +48,24 @@
4648
from web3._utils.validation import (
4749
validate_address,
4850
)
51+
from web3.exceptions import (
52+
ValidationError,
53+
)
4954
from web3.types import (
5055
ABIEvent,
5156
BlockIdentifier,
5257
FilterParams,
5358
LogReceipt,
59+
RPCEndpoint,
5460
)
5561

5662
if TYPE_CHECKING:
5763
from web3 import Web3 # noqa: F401
64+
from web3.module import ( # noqa: F401
65+
Module,
66+
ModuleV2,
67+
)
68+
from web3.eth import Eth # noqa: F401
5869

5970

6071
def construct_event_filter_params(
@@ -122,14 +133,16 @@ class Filter:
122133
poll_interval = None
123134
filter_id = None
124135

125-
def __init__(self, web3: "Web3", filter_id: HexStr) -> None:
126-
self.web3 = web3
136+
def __init__(self,
137+
filter_id: HexStr,
138+
eth_module: "Eth") -> None:
139+
self.eth_module = eth_module
127140
self.filter_id = filter_id
128141
self.callbacks = []
129142
super().__init__()
130143

131144
def __str__(self) -> str:
132-
return "Filter for {0}".format(self.filter_id)
145+
return f"Filter for {self.filter_id}"
133146

134147
def format_entry(self, entry: LogReceipt) -> LogReceipt:
135148
"""
@@ -148,11 +161,11 @@ def _filter_valid_entries(self, entries: Collection[LogReceipt]) -> Iterator[Log
148161
return filter(self.is_valid_entry, entries)
149162

150163
def get_new_entries(self) -> List[LogReceipt]:
151-
log_entries = self._filter_valid_entries(self.web3.eth.getFilterChanges(self.filter_id))
164+
log_entries = self._filter_valid_entries(self.eth_module.getFilterChanges(self.filter_id))
152165
return self._format_log_entries(log_entries)
153166

154167
def get_all_entries(self) -> List[LogReceipt]:
155-
log_entries = self._filter_valid_entries(self.web3.eth.getFilterLogs(self.filter_id))
168+
log_entries = self._filter_valid_entries(self.eth_module.getFilterLogs(self.filter_id))
156169
return self._format_log_entries(log_entries)
157170

158171
def _format_log_entries(self,
@@ -203,7 +216,7 @@ def set_data_filters(self, data_filter_set: Collection[Tuple[TypeStr, Any]]) ->
203216
"""
204217
self.data_filter_set = data_filter_set
205218
if any(data_filter_set):
206-
self.data_filter_set_function = match_fn(self.web3, data_filter_set)
219+
self.data_filter_set_function = match_fn(self.eth_module.codec, data_filter_set)
207220

208221
def is_valid_entry(self, entry: LogReceipt) -> bool:
209222
if not self.data_filter_set:
@@ -235,21 +248,21 @@ def normalize_data_values(type_string: TypeStr, data_value: Any) -> Any:
235248

236249

237250
@curry
238-
def match_fn(w3: "Web3", match_values_and_abi: Collection[Tuple[str, Any]], data: Any) -> bool:
251+
def match_fn(codec: ABICodec, match_values_and_abi: Collection[Tuple[str, Any]], data: Any) -> bool:
239252
"""Match function used for filtering non-indexed event arguments.
240253
241254
Values provided through the match_values_and_abi parameter are
242255
compared to the abi decoded log data.
243256
"""
244257
abi_types, all_match_values = zip(*match_values_and_abi)
245258

246-
decoded_values = w3.codec.decode_abi(abi_types, HexBytes(data))
259+
decoded_values = codec.decode_abi(abi_types, HexBytes(data))
247260
for data_value, match_values, abi_type in zip(decoded_values, all_match_values, abi_types):
248261
if match_values is None:
249262
continue
250263
normalized_data = normalize_data_values(abi_type, data_value)
251264
for value in match_values:
252-
if not w3.is_encodable(abi_type, value):
265+
if not codec.is_encodable(abi_type, value):
253266
raise ValueError(
254267
f"Value {value} is of the wrong abi type. "
255268
f"Expected {abi_type} typed value."
@@ -260,3 +273,41 @@ def match_fn(w3: "Web3", match_values_and_abi: Collection[Tuple[str, Any]], data
260273
return False
261274

262275
return True
276+
277+
278+
class _UseExistingFilter(Exception):
279+
"""
280+
Internal exception, raised when a filter_id is passed into w3.eth.filter()
281+
"""
282+
def __init__(
283+
self,
284+
filter_id: Union[str, FilterParams, HexStr]
285+
) -> None:
286+
self.filter_id = filter_id
287+
288+
289+
@curry
290+
def select_filter_method(
291+
value: Union[str, FilterParams, HexStr],
292+
if_new_block_filter: RPCEndpoint,
293+
if_new_pending_transaction_filter: RPCEndpoint,
294+
if_new_filter: RPCEndpoint,
295+
) -> RPCEndpoint:
296+
297+
if is_string(value):
298+
if value == "latest":
299+
return if_new_block_filter
300+
elif value == "pending":
301+
return if_new_pending_transaction_filter
302+
elif is_hex(value):
303+
raise _UseExistingFilter(value)
304+
else:
305+
raise ValidationError("Filter argument needs to be either 'latest',"
306+
" 'pending', or a hex-encoded filter_id. Filter argument"
307+
f" is: {value}")
308+
elif isinstance(value, dict):
309+
return if_new_filter
310+
else:
311+
raise ValidationError("Filter argument needs to be either the string "
312+
"'pending' or 'latest', a filter_id, "
313+
f"or a filter params dictionary. Filter argument is: {value}")

0 commit comments

Comments
 (0)