diff --git a/DrissionPage/_base/driver.py b/DrissionPage/_base/driver.py index 77b1329..3b94f88 100644 --- a/DrissionPage/_base/driver.py +++ b/DrissionPage/_base/driver.py @@ -37,6 +37,7 @@ class Driver(object): self._stopped = Event() self.event_handlers = {} + self.immediate_event_handlers = {} self.method_results = {} self.event_queue = Queue() @@ -63,12 +64,14 @@ class Driver(object): print(f'发> {message_json}') break - if timeout is not None: - timeout = perf_counter() + timeout - + end_time = perf_counter() + timeout if timeout is not None else None self.method_results[ws_id] = Queue() try: self._ws.send(message_json) + if timeout == 0: + self.method_results.pop(ws_id, None) + return {'id': ws_id, 'result': {}} + except (OSError, WebSocketConnectionClosedException): self.method_results.pop(ws_id, None) return None @@ -82,14 +85,12 @@ class Driver(object): except Empty: if self.alert_flag: self.alert_flag = False - result = {'result': {'message': 'alert exists.'}} self.method_results.pop(ws_id, None) - return result + return {'result': {'message': 'alert exists.'}} - elif timeout is not None and perf_counter() > timeout: - result = {'error': {'message': 'timeout'}} + elif timeout is not None and perf_counter() > end_time: self.method_results.pop(ws_id, None) - return result + return {'error': {'message': 'timeout'}} continue @@ -119,8 +120,12 @@ class Driver(object): if 'method' in msg: if msg['method'].startswith('Page.javascriptDialog'): self.alert_flag = msg['method'].endswith('Opening') - - self.event_queue.put(msg) + if msg['method'] in self.immediate_event_handlers: + function = self.immediate_event_handlers.get(msg['method']) + if function: + function(**msg['params']) + else: + self.event_queue.put(msg) elif msg.get('id') in self.method_results: self.method_results[msg['id']].put(msg) @@ -138,11 +143,7 @@ class Driver(object): function = self.event_handlers.get(event['method']) if function: - # if self._debug: - # print(f'开始执行 {function.__name__}') function(**event['params']) - # if self._debug: - # print(f'执行 {function.__name__}完毕') self.event_queue.task_done() @@ -156,8 +157,8 @@ class Driver(object): if self._stopped.is_set(): return {'error': 'tab closed', 'type': 'tab_closed'} - timeout = kwargs.pop("_timeout", 20) - result = self._send({"method": _method, "params": kwargs}, timeout=timeout) + timeout = kwargs.pop('_timeout', 20) + result = self._send({'method': _method, 'params': kwargs}, timeout=timeout) if result is None: return {'error': 'tab closed', 'type': 'tab_closed'} if 'result' not in result and 'error' in result: @@ -199,19 +200,21 @@ class Driver(object): self.event_queue.queue.clear() return True - def set_callback(self, event, callback): + def set_callback(self, event, callback, immediate=False): """绑定cdp event和回调方法 :param event: cdp event :param callback: 绑定到cdp event的回调方法 + :param immediate: 是否要立即处理的动作 :return: None """ + handler = self.immediate_event_handlers if immediate else self.event_handlers if callback: - self.event_handlers[event] = callback + handler[event] = callback else: - self.event_handlers.pop(event, None) + handler.pop(event, None) def __str__(self): - return f"" + return f'' __repr__ = __str__ @@ -233,7 +236,7 @@ class BrowserDriver(Driver): self.browser = browser def __repr__(self): - return f"" + return f'' def get(self, url): r = get(url, headers={'Connection': 'close'}) diff --git a/DrissionPage/_base/driver.pyi b/DrissionPage/_base/driver.pyi index e2833da..6ea93b4 100644 --- a/DrissionPage/_base/driver.pyi +++ b/DrissionPage/_base/driver.pyi @@ -34,6 +34,7 @@ class Driver(object): _handle_event_th: Thread _stopped: Event event_handlers: dict + immediate_event_handlers: dict method_results: dict event_queue: Queue @@ -53,7 +54,7 @@ class Driver(object): def stop(self) -> bool: ... - def set_callback(self, event: str, callback: Union[Callable, None]) -> None: ... + def set_callback(self, event: str, callback: Union[Callable, None], immediate: bool = False) -> None: ... def __str__(self) -> str: ... diff --git a/DrissionPage/_functions/tools.py b/DrissionPage/_functions/tools.py index d5e140f..ead86eb 100644 --- a/DrissionPage/_functions/tools.py +++ b/DrissionPage/_functions/tools.py @@ -269,7 +269,8 @@ def raise_error(r): elif error == ('tab closed', 'No target with given id found'): raise PageClosedError elif error == 'timeout': - raise TimeoutError + return + # raise TimeoutError elif error == 'alert exists.': raise AlertExistsError elif error in ('Node does not have a layout object', 'Could not compute box model.'): diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py index e0d2a3e..554ad32 100644 --- a/DrissionPage/_pages/chromium_base.py +++ b/DrissionPage/_pages/chromium_base.py @@ -123,7 +123,7 @@ class ChromiumBase(BasePage): self._driver = self.browser._get_driver(tab_id) self._alert = Alert() - self._driver.set_callback('Page.javascriptDialogOpening', self._on_alert_open) + self._driver.set_callback('Page.javascriptDialogOpening', self._on_alert_open, immediate=True) self._driver.set_callback('Page.javascriptDialogClosed', self._on_alert_close) self._driver.run('DOM.enable') @@ -837,6 +837,12 @@ class ChromiumBase(BasePage): self.run_cdp_loaded('Network.clearBrowserCookies') def handle_alert(self, accept=True, send=None, timeout=None, next_one=False): + r = self._handle_alert(accept=accept, send=send, timeout=timeout, next_one=next_one) + while self._has_alert: + sleep(.1) + return r + + def _handle_alert(self, accept=True, send=None, timeout=None, next_one=False): """处理提示框,可以自动等待提示框出现 :param accept: True表示确认,False表示取消,其它值不会按按钮但依然返回文本值 :param send: 处理prompt提示框时可输入文本 @@ -858,10 +864,10 @@ class ChromiumBase(BasePage): return False res_text = self._alert.text - if self._alert.type == 'prompt': - self.driver.run('Page.handleJavaScriptDialog', accept=accept, promptText=send) - else: - self.driver.run('Page.handleJavaScriptDialog', accept=accept) + d = {'accept': accept, '_timeout': 0} + if self._alert.type == 'prompt' and send is not None: + d['promptText'] = send + self.driver.run('Page.handleJavaScriptDialog', **d) return res_text def _on_alert_open(self, **kwargs): @@ -875,9 +881,9 @@ class ChromiumBase(BasePage): self._has_alert = True if self._alert.auto is not None: - self.handle_alert(self._alert.auto) + self._handle_alert(self._alert.auto) elif self._alert.handle_next is not None: - self.handle_alert(self._alert.handle_next, self._alert.next_text) + self._handle_alert(self._alert.handle_next, self._alert.next_text) self._alert.handle_next = None def _on_alert_close(self, **kwargs): diff --git a/DrissionPage/_pages/chromium_base.pyi b/DrissionPage/_pages/chromium_base.pyi index db025c0..39a6ac5 100644 --- a/DrissionPage/_pages/chromium_base.pyi +++ b/DrissionPage/_pages/chromium_base.pyi @@ -227,6 +227,9 @@ class ChromiumBase(BasePage): def handle_alert(self, accept: bool = True, send: str = None, timeout: float = None, next_one: bool = False) -> Union[str, False]: ... + def _handle_alert(self, accept: bool = True, send: str = None, timeout: float = None, + next_one: bool = False) -> Union[str, False]: ... + def _on_alert_close(self, **kwargs): ... def _on_alert_open(self, **kwargs): ... diff --git a/DrissionPage/_units/clicker.py b/DrissionPage/_units/clicker.py index 27d6719..42f48fe 100644 --- a/DrissionPage/_units/clicker.py +++ b/DrissionPage/_units/clicker.py @@ -147,10 +147,10 @@ class Clicker(object): :return: None """ self._ele.page.run_cdp('Input.dispatchMouseEvent', type='mousePressed', - x=client_x, y=client_y, button=button, clickCount=count) + x=client_x, y=client_y, button=button, clickCount=count, _timeout=0.3) # sleep(.05) self._ele.page.run_cdp('Input.dispatchMouseEvent', type='mouseReleased', - x=client_x, y=client_y, button=button) + x=client_x, y=client_y, button=button, _timeout=0.2) # -------------即将废弃--------------