Skip to content

Commit c5472ef

Browse files
authored
refactor: allow consuming without blocking the listening loop (#147)
* refactor: kotlin-like flows to allow consuming without blocking the listening loop * update changelogs * Revert "refactor: kotlin-like flows to allow consuming without blocking the listening loop" This reverts commit b1520ba. * Refactor to a asynchronous handlers to avoid blocking the listening loop * typo
1 parent f4facfc commit c5472ef

File tree

3 files changed

+60
-49
lines changed

3 files changed

+60
-49
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
- Refactor to asynchronous handlers to avoid blocking the listening loop @nathanfallet
13+
1214
### Added
1315

1416
### Changed

zendriver/core/browser.py

Lines changed: 46 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class Browser:
5959
_process_pid: int | None
6060
_http: HTTPApi | None = None
6161
_cookies: CookieJar | None = None
62+
_update_target_info_mutex: asyncio.Lock = asyncio.Lock()
6263

6364
config: Config
6465
connection: Connection | None
@@ -182,7 +183,7 @@ async def wait(self, time: Union[float, int] = 1) -> Browser:
182183
sleep = wait
183184
"""alias for wait"""
184185

185-
def _handle_target_update(
186+
async def _handle_target_update(
186187
self,
187188
event: Union[
188189
cdp.target.TargetInfoChanged,
@@ -193,56 +194,58 @@ def _handle_target_update(
193194
):
194195
"""this is an internal handler which updates the targets when chrome emits the corresponding event"""
195196

196-
if isinstance(event, cdp.target.TargetInfoChanged):
197-
target_info = event.target_info
197+
async with self._update_target_info_mutex:
198+
if isinstance(event, cdp.target.TargetInfoChanged):
199+
target_info = event.target_info
198200

199-
current_tab = next(
200-
filter(
201-
lambda item: item.target_id == target_info.target_id, self.targets
202-
)
203-
)
204-
current_target = current_tab.target
205-
206-
if logger.getEffectiveLevel() <= 10:
207-
changes = util.compare_target_info(current_target, target_info)
208-
changes_string = ""
209-
for change in changes:
210-
key, old, new = change
211-
changes_string += f"\n{key}: {old} => {new}\n"
212-
logger.debug(
213-
"target #%d has changed: %s"
214-
% (self.targets.index(current_tab), changes_string)
201+
current_tab = next(
202+
filter(
203+
lambda item: item.target_id == target_info.target_id,
204+
self.targets,
205+
)
215206
)
207+
current_target = current_tab.target
208+
209+
if logger.getEffectiveLevel() <= 10:
210+
changes = util.compare_target_info(current_target, target_info)
211+
changes_string = ""
212+
for change in changes:
213+
key, old, new = change
214+
changes_string += f"\n{key}: {old} => {new}\n"
215+
logger.debug(
216+
"target #%d has changed: %s"
217+
% (self.targets.index(current_tab), changes_string)
218+
)
216219

217-
current_tab.target = target_info
220+
current_tab.target = target_info
218221

219-
elif isinstance(event, cdp.target.TargetCreated):
220-
target_info = event.target_info
221-
from .tab import Tab
222+
elif isinstance(event, cdp.target.TargetCreated):
223+
target_info = event.target_info
224+
from .tab import Tab
222225

223-
new_target = Tab(
224-
(
225-
f"ws://{self.config.host}:{self.config.port}"
226-
f"/devtools/{target_info.type_ or 'page'}" # all types are 'page' internally in chrome apparently
227-
f"/{target_info.target_id}"
228-
),
229-
target=target_info,
230-
browser=self,
231-
)
226+
new_target = Tab(
227+
(
228+
f"ws://{self.config.host}:{self.config.port}"
229+
f"/devtools/{target_info.type_ or 'page'}" # all types are 'page' internally in chrome apparently
230+
f"/{target_info.target_id}"
231+
),
232+
target=target_info,
233+
browser=self,
234+
)
232235

233-
self.targets.append(new_target)
236+
self.targets.append(new_target)
234237

235-
logger.debug("target #%d created => %s", len(self.targets), new_target)
238+
logger.debug("target #%d created => %s", len(self.targets), new_target)
236239

237-
elif isinstance(event, cdp.target.TargetDestroyed):
238-
current_tab = next(
239-
filter(lambda item: item.target_id == event.target_id, self.targets)
240-
)
241-
logger.debug(
242-
"target removed. id # %d => %s"
243-
% (self.targets.index(current_tab), current_tab)
244-
)
245-
self.targets.remove(current_tab)
240+
elif isinstance(event, cdp.target.TargetDestroyed):
241+
current_tab = next(
242+
filter(lambda item: item.target_id == event.target_id, self.targets)
243+
)
244+
logger.debug(
245+
"target removed. id # %d => %s"
246+
% (self.targets.index(current_tab), current_tab)
247+
)
248+
self.targets.remove(current_tab)
246249

247250
async def get(
248251
self, url="about:blank", new_tab: bool = False, new_window: bool = False

zendriver/core/connection.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -723,15 +723,21 @@ async def listener_loop(self):
723723
try:
724724
if iscoroutinefunction(callback):
725725
try:
726-
await callback(event, self.connection)
726+
asyncio.create_task(
727+
callback(event, self.connection)
728+
)
727729
except TypeError:
728-
await callback(event)
730+
asyncio.create_task(callback(event))
729731
else:
730732
callback = typing.cast(Callable, callback)
731-
try:
732-
callback(event, self.connection)
733-
except TypeError:
734-
callback(event)
733+
734+
def run_callback():
735+
try:
736+
callback(event, self.connection)
737+
except TypeError:
738+
callback(event)
739+
740+
asyncio.create_task(asyncio.to_thread(run_callback))
735741
except Exception as e:
736742
logger.warning(
737743
"exception in callback %s for event %s => %s",

0 commit comments

Comments
 (0)