Skip to content

Commit 6b78d6c

Browse files
author
pacrob
committed
asynced Filter and EventFilterBuilder classes
1 parent c282769 commit 6b78d6c

File tree

2 files changed

+108
-13
lines changed

2 files changed

+108
-13
lines changed

web3/_utils/events.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
if TYPE_CHECKING:
8989
from web3 import Web3 # noqa: F401
9090
from web3._utils.filters import ( # noqa: F401
91+
AsyncLogFilter,
9192
LogFilter,
9293
)
9394

@@ -310,7 +311,7 @@ def is_indexed(arg: Any) -> bool:
310311
is_not_indexed = complement(is_indexed)
311312

312313

313-
class EventFilterBuilder:
314+
class BaseEventFilterBuilder:
314315
formatter = None
315316
_fromBlock = None
316317
_toBlock = None
@@ -410,6 +411,8 @@ def filter_params(self) -> FilterParams:
410411
}
411412
return valfilter(lambda x: x is not None, params)
412413

414+
415+
class EventFilterBuilder(BaseEventFilterBuilder):
413416
def deploy(self, w3: "Web3") -> "LogFilter":
414417
if not isinstance(w3, web3.Web3):
415418
raise ValueError(f"Invalid web3 argument: got: {w3!r}")
@@ -427,6 +430,25 @@ def deploy(self, w3: "Web3") -> "LogFilter":
427430
return log_filter
428431

429432

433+
class AsyncEventFilterBuilder(BaseEventFilterBuilder):
434+
async def deploy(self, w3: "Web3") -> "AsyncLogFilter":
435+
if not isinstance(w3, web3.Web3):
436+
raise ValueError(f"Invalid web3 argument: got: {w3!r}")
437+
438+
for arg in AttributeDict.values(self.args):
439+
arg._immutable = True
440+
self._immutable = True
441+
442+
log_filter = await w3.eth.filter(self.filter_params)
443+
log_filter = cast("AsyncLogFilter", log_filter)
444+
log_filter.filter_params = self.filter_params
445+
log_filter.set_data_filters(self.data_argument_values)
446+
log_filter.builder = self
447+
if self.formatter is not None:
448+
log_filter.log_entry_formatter = self.formatter
449+
return log_filter
450+
451+
430452
def initialize_event_topics(event_abi: ABIEvent) -> Union[bytes, List[Any]]:
431453
if event_abi["anonymous"] is False:
432454
# https://github.com/python/mypy/issues/4976

web3/_utils/filters.py

+85-12
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
)
4242

4343
from web3._utils.events import (
44+
AsyncEventFilterBuilder,
4445
EventFilterBuilder,
4546
construct_event_data_set,
4647
construct_event_topic_set,
@@ -62,6 +63,7 @@
6263
if TYPE_CHECKING:
6364
from web3 import Web3 # noqa: F401
6465
from web3.eth import Eth # noqa: F401
66+
from web3.eth import AsyncEth # noqa: F401
6567

6668

6769
def construct_event_filter_params(
@@ -126,14 +128,13 @@ def construct_event_filter_params(
126128
return data_filters_set, filter_params
127129

128130

129-
class Filter:
131+
class BaseFilter:
130132
callbacks: List[Callable[..., Any]] = None
131133
stopped = False
132134
poll_interval = None
133135
filter_id = None
134136

135-
def __init__(self, filter_id: HexStr, eth_module: "Eth") -> None:
136-
self.eth_module = eth_module
137+
def __init__(self, filter_id: HexStr) -> None:
137138
self.filter_id = filter_id
138139
self.callbacks = []
139140
super().__init__()
@@ -159,6 +160,23 @@ def _filter_valid_entries(
159160
) -> Iterator[LogReceipt]:
160161
return filter(self.is_valid_entry, entries)
161162

163+
def _format_log_entries(
164+
self, log_entries: Optional[Iterator[LogReceipt]] = None
165+
) -> List[LogReceipt]:
166+
if log_entries is None:
167+
return []
168+
169+
formatted_log_entries = [
170+
self.format_entry(log_entry) for log_entry in log_entries
171+
]
172+
return formatted_log_entries
173+
174+
175+
class Filter(BaseFilter):
176+
def __init__(self, filter_id: HexStr, eth_module: "Eth") -> None:
177+
self.eth_module = eth_module
178+
super(Filter, self).__init__(filter_id)
179+
162180
def get_new_entries(self) -> List[LogReceipt]:
163181
log_entries = self._filter_valid_entries(
164182
self.eth_module.get_filter_changes(self.filter_id)
@@ -171,26 +189,39 @@ def get_all_entries(self) -> List[LogReceipt]:
171189
)
172190
return self._format_log_entries(log_entries)
173191

174-
def _format_log_entries(
175-
self, log_entries: Optional[Iterator[LogReceipt]] = None
176-
) -> List[LogReceipt]:
177-
if log_entries is None:
178-
return []
179192

180-
formatted_log_entries = [
181-
self.format_entry(log_entry) for log_entry in log_entries
182-
]
183-
return formatted_log_entries
193+
class AsyncFilter(BaseFilter):
194+
def __init__(self, filter_id: HexStr, eth_module: "AsyncEth") -> None:
195+
self.eth_module = eth_module
196+
super(AsyncFilter, self).__init__(filter_id)
197+
198+
async def get_new_entries(self) -> List[LogReceipt]:
199+
filter_changes = await self.eth_module.get_filter_changes(self.filter_id)
200+
log_entries = self._filter_valid_entries(filter_changes)
201+
return self._format_log_entries(log_entries)
202+
203+
async def get_all_entries(self) -> List[LogReceipt]:
204+
filter_logs = await self.eth_module.get_filter_logs(self.filter_id)
205+
log_entries = self._filter_valid_entries(filter_logs)
206+
return self._format_log_entries(log_entries)
184207

185208

186209
class BlockFilter(Filter):
187210
pass
188211

189212

213+
class AsyncBlockFilter(AsyncFilter):
214+
pass
215+
216+
190217
class TransactionFilter(Filter):
191218
pass
192219

193220

221+
class AsyncTransactionFilter(AsyncFilter):
222+
pass
223+
224+
194225
class LogFilter(Filter):
195226
data_filter_set = None
196227
data_filter_set_regex = None
@@ -233,6 +264,48 @@ def is_valid_entry(self, entry: LogReceipt) -> bool:
233264
return bool(self.data_filter_set_function(entry["data"]))
234265

235266

267+
class AsyncLogFilter(AsyncFilter):
268+
data_filter_set = None
269+
data_filter_set_regex = None
270+
data_filter_set_function = None
271+
log_entry_formatter = None
272+
filter_params: FilterParams = None
273+
builder: AsyncEventFilterBuilder = None
274+
275+
def __init__(self, *args: Any, **kwargs: Any) -> None:
276+
self.log_entry_formatter = kwargs.pop(
277+
"log_entry_formatter",
278+
self.log_entry_formatter,
279+
)
280+
if "data_filter_set" in kwargs:
281+
self.set_data_filters(kwargs.pop("data_filter_set"))
282+
super().__init__(*args, **kwargs)
283+
284+
def format_entry(self, entry: LogReceipt) -> LogReceipt:
285+
if self.log_entry_formatter:
286+
return self.log_entry_formatter(entry)
287+
return entry
288+
289+
def set_data_filters(
290+
self, data_filter_set: Collection[Tuple[TypeStr, Any]]
291+
) -> None:
292+
"""Sets the data filters (non indexed argument filters)
293+
294+
Expects a set of tuples with the type and value, e.g.:
295+
(('uint256', [12345, 54321]), ('string', ('a-single-string',)))
296+
"""
297+
self.data_filter_set = data_filter_set
298+
if any(data_filter_set):
299+
self.data_filter_set_function = match_fn(
300+
self.eth_module.codec, data_filter_set
301+
)
302+
303+
def is_valid_entry(self, entry: LogReceipt) -> bool:
304+
if not self.data_filter_set:
305+
return True
306+
return bool(self.data_filter_set_function(entry["data"]))
307+
308+
236309
def decode_utf8_bytes(value: bytes) -> str:
237310
return value.decode("utf-8")
238311

0 commit comments

Comments
 (0)