diff --git a/DrissionPage/base.py b/DrissionPage/base.py index 34044c3..54b9a7c 100644 --- a/DrissionPage/base.py +++ b/DrissionPage/base.py @@ -71,7 +71,7 @@ class BaseElement(BaseParser): r = self._find_elements(loc_or_str, timeout=timeout, single=single, relative=relative, raise_err=raise_err) if not single or raise_err is False: return r - if not r and (Settings.raise_ele_not_found or raise_err is True): + if not r and (Settings.raise_when_ele_not_found or raise_err is True): raise ElementNotFoundError return r @@ -151,7 +151,7 @@ class DrissionElement(BaseElement): filter_loc = '' nodes = self.children(filter_loc=filter_loc, timeout=timeout, ele_only=ele_only) if not nodes: - if Settings.raise_ele_not_found: + if Settings.raise_when_ele_not_found: raise ElementNotFoundError else: return NoneElement() @@ -159,7 +159,7 @@ class DrissionElement(BaseElement): try: return nodes[index - 1] except IndexError: - if Settings.raise_ele_not_found: + if Settings.raise_when_ele_not_found: raise ElementNotFoundError else: return NoneElement() @@ -178,7 +178,7 @@ class DrissionElement(BaseElement): nodes = self._get_brothers(index, filter_loc, 'preceding', timeout=timeout, ele_only=ele_only) if nodes: return nodes[-1] - if Settings.raise_ele_not_found: + if Settings.raise_when_ele_not_found: raise ElementNotFoundError else: return NoneElement() @@ -197,7 +197,7 @@ class DrissionElement(BaseElement): nodes = self._get_brothers(index, filter_loc, 'following', timeout=timeout, ele_only=ele_only) if nodes: return nodes[0] - if Settings.raise_ele_not_found: + if Settings.raise_when_ele_not_found: raise ElementNotFoundError else: return NoneElement() @@ -216,7 +216,7 @@ class DrissionElement(BaseElement): nodes = self._get_brothers(index, filter_loc, 'preceding', False, timeout=timeout, ele_only=ele_only) if nodes: return nodes[-1] - if Settings.raise_ele_not_found: + if Settings.raise_when_ele_not_found: raise ElementNotFoundError else: return NoneElement() @@ -235,7 +235,7 @@ class DrissionElement(BaseElement): nodes = self._get_brothers(index, filter_loc, 'following', False, timeout, ele_only=ele_only) if nodes: return nodes[0] - if Settings.raise_ele_not_found: + if Settings.raise_when_ele_not_found: raise ElementNotFoundError else: return NoneElement() @@ -444,7 +444,7 @@ class BasePage(BaseParser): if not single or raise_err is False: return r - if not r and (Settings().raise_ele_not_found is True or raise_err is True): + if not r and (Settings.raise_when_ele_not_found is True or raise_err is True): raise ElementNotFoundError return r diff --git a/DrissionPage/chromium_base.py b/DrissionPage/chromium_base.py index e972da5..abc4257 100644 --- a/DrissionPage/chromium_base.py +++ b/DrissionPage/chromium_base.py @@ -15,12 +15,12 @@ from requests import Session from .base import BasePage from .chromium_driver import ChromiumDriver from .chromium_element import ChromiumScroll, ChromiumElement, run_js, make_chromium_ele -from .commons.constants import HANDLE_ALERT_METHOD, ERROR, NoneElement +from .commons.constants import HANDLE_ALERT_METHOD, ERROR, NoneElement, Settings from .commons.locator import get_loc from .commons.tools import get_usable_path, clean_folder from .commons.web import set_browser_cookies from .errors import ContextLossError, ElementLossError, AlertExistsError, CDPError, TabClosedError, \ - NoRectError, BrowserConnectError, GetDocumentError + NoRectError, BrowserConnectError, GetDocumentError, WaitTimeoutError from .network_listener import NetworkListener from .session_element import make_session_ele @@ -1002,66 +1002,78 @@ class ChromiumBaseWaiter(object): """ self._driver = page_or_ele - def ele_delete(self, loc_or_ele, timeout=None): + def ele_delete(self, loc_or_ele, timeout=None, raise_err=None): """等待元素从DOM中删除 :param loc_or_ele: 要等待的元素,可以是已有元素、定位符 :param timeout: 超时时间,默认读取页面超时时间 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ ele = self._driver._ele(loc_or_ele, raise_err=False, timeout=0) - return ele.wait.delete(timeout) if ele else True + return ele.wait.delete(timeout, raise_err=raise_err) if ele else True - def ele_display(self, loc_or_ele, timeout=None): + def ele_display(self, loc_or_ele, timeout=None, raise_err=None): """等待元素变成显示状态 :param loc_or_ele: 要等待的元素,可以是已有元素、定位符 :param timeout: 超时时间,默认读取页面超时时间 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ ele = self._driver._ele(loc_or_ele, raise_err=False, timeout=0) - return ele.wait.display(timeout) + return ele.wait.display(timeout, raise_err=raise_err) - def ele_hidden(self, loc_or_ele, timeout=None): + def ele_hidden(self, loc_or_ele, timeout=None, raise_err=None): """等待元素变成隐藏状态 :param loc_or_ele: 要等待的元素,可以是已有元素、定位符 :param timeout: 超时时间,默认读取页面超时时间 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ ele = self._driver._ele(loc_or_ele, raise_err=False, timeout=0) - return ele.wait.hidden(timeout) + return ele.wait.hidden(timeout, raise_err=raise_err) - def ele_load(self, loc, timeout=None): + def ele_load(self, loc, timeout=None, raise_err=None): """等待元素加载到DOM :param loc: 要等待的元素,输入定位符 :param timeout: 超时时间,默认读取页面超时时间 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 成功返回元素对象,失败返回False """ ele = self._driver._ele(loc, raise_err=False, timeout=timeout) - return ele if ele else False + if ele: + return True + if raise_err is True or Settings.raise_when_wait_failed is True: + raise WaitTimeoutError('等待元素加载失败。') + else: + return False - def load_start(self, timeout=None): + def load_start(self, timeout=None, raise_err=None): """等待页面开始加载 :param timeout: 超时时间,为None时使用页面timeout属性 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._loading(timeout=timeout, gap=.002) + return self._loading(timeout=timeout, gap=.002, raise_err=raise_err) - def load_complete(self, timeout=None): + def load_complete(self, timeout=None, raise_err=None): """等待页面开始加载 :param timeout: 超时时间,为None时使用页面timeout属性 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._loading(timeout=timeout, start=False) + return self._loading(timeout=timeout, start=False, raise_err=raise_err) def upload_paths_inputted(self): """等待自动填写上传文件路径""" while self._driver._upload_list: sleep(.01) - def _loading(self, timeout=None, start=True, gap=.01): + def _loading(self, timeout=None, start=True, gap=.01, raise_err=None): """等待页面开始加载或加载完成 :param timeout: 超时时间,为None时使用页面timeout属性 :param start: 等待开始还是结束 :param gap: 间隔秒数 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ if timeout != 0: @@ -1072,7 +1084,11 @@ class ChromiumBaseWaiter(object): if self._driver.is_loading == start: return True sleep(gap) - return False + + if raise_err is True or Settings.raise_when_wait_failed is True: + raise WaitTimeoutError('等待页面加载失败。') + else: + return False class ChromiumPageScroll(ChromiumScroll): diff --git a/DrissionPage/chromium_base.pyi b/DrissionPage/chromium_base.pyi index 11c5878..7b275a8 100644 --- a/DrissionPage/chromium_base.pyi +++ b/DrissionPage/chromium_base.pyi @@ -217,19 +217,23 @@ class ChromiumBaseWaiter(object): def __init__(self, page: ChromiumBase): self._driver: ChromiumBase = ... - def ele_delete(self, loc_or_ele: Union[str, tuple, ChromiumElement], timeout: float = None) -> bool: ... + def ele_delete(self, loc_or_ele: Union[str, tuple, ChromiumElement], timeout: float = None, + raise_err: bool = None) -> bool: ... - def ele_display(self, loc_or_ele: Union[str, tuple, ChromiumElement], timeout: float = None) -> bool: ... + def ele_display(self, loc_or_ele: Union[str, tuple, ChromiumElement], timeout: float = None, + raise_err: bool = None) -> bool: ... - def ele_hidden(self, loc_or_ele: Union[str, tuple, ChromiumElement], timeout: float = None) -> bool: ... + def ele_hidden(self, loc_or_ele: Union[str, tuple, ChromiumElement], timeout: float = None, + raise_err: bool = None) -> bool: ... - def ele_load(self, loc: Union[str, tuple], timeout: float = None) -> Union[bool, ChromiumElement]: ... + def ele_load(self, loc: Union[str, tuple], timeout: float = None, + raise_err: bool = None) -> Union[bool, ChromiumElement]: ... - def _loading(self, timeout: float = None, start: bool = True, gap: float = .01) -> bool: ... + def _loading(self, timeout: float = None, start: bool = True, gap: float = .01, raise_err: bool = None) -> bool: ... - def load_start(self, timeout: float = None) -> bool: ... + def load_start(self, timeout: float = None, raise_err: bool = None) -> bool: ... - def load_complete(self, timeout: float = None) -> bool: ... + def load_complete(self, timeout: float = None, raise_err: bool = None) -> bool: ... def upload_paths_inputted(self) -> None: ... diff --git a/DrissionPage/chromium_element.py b/DrissionPage/chromium_element.py index 5aad97a..03fb5f9 100644 --- a/DrissionPage/chromium_element.py +++ b/DrissionPage/chromium_element.py @@ -14,7 +14,7 @@ from .commons.keys import keys_to_typing, keyDescriptionForString, keyDefinition from .commons.locator import get_loc from .commons.web import make_absolute_link, get_ele_txt, format_html, is_js_func, location_in_viewport, offset_scroll from .errors import ContextLossError, ElementLossError, JavaScriptError, NoRectError, ElementNotFoundError, \ - CDPError, NoResourceError, CanNotClickError + CDPError, NoResourceError, CanNotClickError, WaitTimeoutError from .session_element import make_session_ele @@ -839,7 +839,7 @@ class ChromiumShadowRoot(BaseElement): """ nodes = self.children(filter_loc=filter_loc) if not nodes: - if Settings.raise_ele_not_found: + if Settings.raise_when_ele_not_found: raise ElementNotFoundError else: return NoneElement() @@ -847,7 +847,7 @@ class ChromiumShadowRoot(BaseElement): try: return nodes[index - 1] except IndexError: - if Settings.raise_ele_not_found: + if Settings.raise_when_ele_not_found: raise ElementNotFoundError else: return NoneElement() @@ -861,7 +861,7 @@ class ChromiumShadowRoot(BaseElement): nodes = self.nexts(filter_loc=filter_loc) if nodes: return nodes[index - 1] - if Settings.raise_ele_not_found: + if Settings.raise_when_ele_not_found: raise ElementNotFoundError else: return NoneElement() @@ -876,7 +876,7 @@ class ChromiumShadowRoot(BaseElement): nodes = self.befores(filter_loc=filter_loc) if nodes: return nodes[index - 1] - if Settings.raise_ele_not_found: + if Settings.raise_when_ele_not_found: raise ElementNotFoundError else: return NoneElement() @@ -891,7 +891,7 @@ class ChromiumShadowRoot(BaseElement): nodes = self.afters(filter_loc=filter_loc) if nodes: return nodes[index - 1] - if Settings.raise_ele_not_found: + if Settings.raise_when_ele_not_found: raise ElementNotFoundError else: return NoneElement() @@ -1633,9 +1633,9 @@ class Click(object): if by_js is not False: self._ele.run_js('this.click();') return True - - if Settings.raise_click_failed: + if Settings.raise_when_click_failed: raise CanNotClickError + return False def right(self): @@ -2039,58 +2039,66 @@ class ChromiumElementWaiter(object): self._page = page self._ele = ele - def delete(self, timeout=None): + def delete(self, timeout=None, raise_err=None): """等待元素从dom删除 :param timeout: 超时时间,为None使用元素所在页面timeout属性 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_alive', False, timeout) + return self._wait_state('is_alive', False, timeout, raise_err) - def display(self, timeout=None): + def display(self, timeout=None, raise_err=None): """等待元素从dom显示 :param timeout: 超时时间,为None使用元素所在页面timeout属性 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_displayed', True, timeout) + return self._wait_state('is_displayed', True, timeout, raise_err) - def hidden(self, timeout=None): + def hidden(self, timeout=None, raise_err=None): """等待元素从dom隐藏 :param timeout: 超时时间,为None使用元素所在页面timeout属性 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_displayed', False, timeout) + return self._wait_state('is_displayed', False, timeout, raise_err) - def covered(self, timeout=None): + def covered(self, timeout=None, raise_err=None): """等待当前元素被遮盖 :param timeout:超时时间,为None使用元素所在页面timeout属性 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_covered', True, timeout) + return self._wait_state('is_covered', True, timeout, raise_err) - def not_covered(self, timeout=None): + def not_covered(self, timeout=None, raise_err=None): """等待当前元素被遮盖 :param timeout:超时时间,为None使用元素所在页面timeout属性 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_covered', False, timeout) + return self._wait_state('is_covered', False, timeout, raise_err) - def enabled(self, timeout=None): + def enabled(self, timeout=None, raise_err=None): """等待当前元素变成可用 :param timeout:超时时间,为None使用元素所在页面timeout属性 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_enabled', True, timeout) + return self._wait_state('is_enabled', True, timeout, raise_err) - def disabled(self, timeout=None): + def disabled(self, timeout=None, raise_err=None): """等待当前元素变成可用 :param timeout:超时时间,为None使用元素所在页面timeout属性 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_enabled', False, timeout) + return self._wait_state('is_enabled', False, timeout, raise_err) - def disabled_or_delete(self, timeout=None): + def disabled_or_delete(self, timeout=None, raise_err=None): """等待当前元素变成不可用或从DOM移除 :param timeout:超时时间,为None使用元素所在页面timeout属性 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ if timeout is None: @@ -2101,13 +2109,17 @@ class ChromiumElementWaiter(object): return True sleep(.05) - return False + if raise_err is True or Settings.raise_when_wait_failed is True: + raise WaitTimeoutError('等待元素隐藏或删除失败。') + else: + return False - def _wait_state(self, attr, mode=False, timeout=None): + def _wait_state(self, attr, mode=False, timeout=None, raise_err=None): """等待元素某个bool状态到达指定状态 :param attr: 状态名称 :param mode: True或False :param timeout: 超时时间,为None使用元素所在页面timeout属性 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ if timeout is None: @@ -2118,7 +2130,10 @@ class ChromiumElementWaiter(object): return True sleep(.05) - return False + if raise_err is True or Settings.raise_when_wait_failed is True: + raise WaitTimeoutError('等待元素状态改变失败。') + else: + return False class Pseudo(object): diff --git a/DrissionPage/chromium_element.pyi b/DrissionPage/chromium_element.pyi index 168bad7..a218d56 100644 --- a/DrissionPage/chromium_element.pyi +++ b/DrissionPage/chromium_element.pyi @@ -561,23 +561,23 @@ class ChromiumElementWaiter(object): self._ele: ChromiumElement = ... self._page: ChromiumBase = ... - def delete(self, timeout: float = None) -> bool: ... + def delete(self, timeout: float = None, raise_err: bool = None) -> bool: ... - def display(self, timeout: float = None) -> bool: ... + def display(self, timeout: float = None, raise_err: bool = None) -> bool: ... - def hidden(self, timeout: float = None) -> bool: ... + def hidden(self, timeout: float = None, raise_err: bool = None) -> bool: ... - def covered(self, timeout: float = None) -> bool: ... + def covered(self, timeout: float = None, raise_err: bool = None) -> bool: ... - def not_covered(self, timeout: float = None) -> bool: ... + def not_covered(self, timeout: float = None, raise_err: bool = None) -> bool: ... - def enabled(self, timeout: float = None) -> bool: ... + def enabled(self, timeout: float = None, raise_err: bool = None) -> bool: ... - def disabled(self, timeout: float = None) -> bool: ... + def disabled(self, timeout: float = None, raise_err: bool = None) -> bool: ... - def disabled_or_delete(self, timeout: float = None) -> bool: ... + def disabled_or_delete(self, timeout: float = None, raise_err: bool = None) -> bool: ... - def _wait_state(self, attr: str, mode: bool = False, timeout: float = None) -> bool: ... + def _wait_state(self, attr: str, mode: bool = False, timeout: float = None, raise_err: bool = None) -> bool: ... class Pseudo(object): diff --git a/DrissionPage/chromium_page.py b/DrissionPage/chromium_page.py index 00ec09d..81bde60 100644 --- a/DrissionPage/chromium_page.py +++ b/DrissionPage/chromium_page.py @@ -10,9 +10,10 @@ from .chromium_base import ChromiumBase, Timeout, ChromiumBaseSetter, ChromiumBa from .chromium_driver import ChromiumDriver from .chromium_tab import ChromiumTab from .commons.browser import connect_browser +from .commons.constants import Settings from .commons.tools import port_is_using from .configs.chromium_options import ChromiumOptions -from .errors import BrowserConnectError +from .errors import BrowserConnectError, WaitTimeoutError class ChromiumPage(ChromiumBase): @@ -152,24 +153,6 @@ class ChromiumPage(ChromiumBase): self._set = ChromiumPageSetter(self) return self._set - # @property - # def download_path(self): - # """返回默认下载路径""" - # p = self._download_path or '' - # return str(Path(p).absolute()) - # - # @property - # def download_set(self): - # """返回用于设置下载参数的对象""" - # if self._download_set is None: - # self._download_set = BaseDownloadSetter(self) - # return self._download_set - # - # @property - # def download(self): - # """返回下载器对象""" - # return self.download_set._switched_DownloadKit - @property def rect(self): if self._rect is None: @@ -385,29 +368,23 @@ class ChromiumPageWaiter(ChromiumBaseWaiter): super().__init__(page) self._listener = None - def new_tab(self, timeout=None): + def new_tab(self, timeout=None, raise_err=None): """等待新标签页出现 :param timeout: 等待超时时间,为None则使用页面对象timeout属性 - :return: 是否等到下载开始 + :param raise_err: 等待识别时是否报错,为None时根据Settings设置 + :return: 是否等到新标签页出现 """ timeout = timeout if timeout is not None else self._driver.timeout end_time = perf_counter() + timeout - while self._driver.tab_id == self._driver.latest_tab and perf_counter() < end_time: + while perf_counter() < end_time: + if self._driver.tab_id != self._driver.latest_tab: + return True sleep(.01) - # def download_begin(self, timeout=1.5): - # """等待浏览器下载开始 - # :param timeout: 等待超时时间,为None则使用页面对象timeout属性 - # :return: 是否等到下载开始 - # """ - # return self._driver.download_set.wait_download_begin(timeout) - # - # def download_finish(self, timeout=None): - # """等待下载结束 - # :param timeout: 等待超时时间,为None则使用页面对象timeout属性 - # :return: 是否等到下载结束 - # """ - # return self._driver.download_set.wait_download_finish(timeout) + if raise_err is True or Settings.raise_when_wait_failed is True: + raise WaitTimeoutError('等待新标签页失败。') + else: + return False class ChromiumTabRect(object): diff --git a/DrissionPage/chromium_page.pyi b/DrissionPage/chromium_page.pyi index cfcb25e..2a9eda4 100644 --- a/DrissionPage/chromium_page.pyi +++ b/DrissionPage/chromium_page.pyi @@ -102,11 +102,11 @@ class ChromiumPageWaiter(ChromiumBaseWaiter): _driver: ChromiumPage = ... _listener: Union[NetworkListener, None] = ... - def download_begin(self, timeout: float = 1.5) -> bool: ... + # def download_begin(self, timeout: float = 1.5) -> bool: ... - def download_finish(self, timeout: float = None) -> bool: ... + # def download_finish(self, timeout: float = None) -> bool: ... - def new_tab(self, timeout: float = None) -> bool: ... + def new_tab(self, timeout: float = None, raise_err: bool = None) -> bool: ... class ChromiumTabRect(object): diff --git a/DrissionPage/commons/constants.py b/DrissionPage/commons/constants.py index c06c2c4..2a219c7 100644 --- a/DrissionPage/commons/constants.py +++ b/DrissionPage/commons/constants.py @@ -11,8 +11,9 @@ ERROR = 'error' class Settings(object): - raise_ele_not_found = False - raise_click_failed = False + raise_when_ele_not_found = False + raise_when_click_failed = False + raise_when_wait_failed = False class NoneElement(object): diff --git a/DrissionPage/easy_set.py b/DrissionPage/easy_set.py index 91fcacb..d783c5b 100644 --- a/DrissionPage/easy_set.py +++ b/DrissionPage/easy_set.py @@ -7,19 +7,10 @@ from os import popen from pathlib import Path from re import search -from .commons.constants import Settings from .configs.chromium_options import ChromiumOptions from .configs.options_manage import OptionsManager -def raise_when_ele_not_found(on_off=True): - """设置全局变量,找不到元素时是否抛出异常 - :param on_off: True 或 False - :return: None - """ - Settings.raise_ele_not_found = on_off - - def configs_to_here(save_name=None): """把默认ini文件复制到当前目录 :param save_name: 指定文件名,为None则命名为'dp_configs.ini' diff --git a/DrissionPage/easy_set.pyi b/DrissionPage/easy_set.pyi index d70e8b9..3e8fc47 100644 --- a/DrissionPage/easy_set.pyi +++ b/DrissionPage/easy_set.pyi @@ -7,9 +7,6 @@ from pathlib import Path from typing import Union -def raise_when_ele_not_found(on_off: bool = True) -> None: ... - - def configs_to_here(file_name: Union[Path, str] = None) -> None: ... diff --git a/DrissionPage/errors.py b/DrissionPage/errors.py index 4eabfa6..7bab148 100644 --- a/DrissionPage/errors.py +++ b/DrissionPage/errors.py @@ -58,3 +58,7 @@ class CanNotClickError(BaseError): class GetDocumentError(BaseError): _info = '获取文档失败。' + + +class WaitTimeoutError(BaseError): + _info = '等待失败。'