diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py index 20bda69..b01aec3 100644 --- a/DrissionPage/_base/browser.py +++ b/DrissionPage/_base/browser.py @@ -34,6 +34,7 @@ __ERROR__ = 'error' class Chromium(object): _BROWSERS = {} + _lock = Lock() def __new__(cls, addr_or_opts=None, session_options=None): """ @@ -42,8 +43,12 @@ class Chromium(object): """ opt = handle_options(addr_or_opts) is_headless, browser_id, is_exists = run_browser(opt) - if browser_id in cls._BROWSERS: - return cls._BROWSERS[browser_id] + with cls._lock: + if browser_id in cls._BROWSERS: + r = cls._BROWSERS[browser_id] + while not hasattr(r, '_driver'): + sleep(.1) + return r r = object.__new__(cls) r._chromium_options = opt r.is_headless = is_headless @@ -62,11 +67,9 @@ class Chromium(object): self._created = True self._type = 'Chromium' - self._frames = {} self._drivers = {} self._all_drivers = {} - self._lock = Lock() self._set = None self._wait = None diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 995e0ba..08f22fb 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -193,7 +193,7 @@ class ChromiumElement(DrissionElement): def wait(self): """返回用于等待的对象""" if self._wait is None: - self._wait = ElementWaiter(self.owner, self) + self._wait = ElementWaiter(self) return self._wait @property @@ -1674,6 +1674,9 @@ def parse_js_result(page, ele, result, end_time): elif the_type == 'undefined': return None + elif the_type == 'function': + return result['description'] + else: return result['value'] diff --git a/DrissionPage/_units/waiter.py b/DrissionPage/_units/waiter.py index 3486cb6..2483e07 100644 --- a/DrissionPage/_units/waiter.py +++ b/DrissionPage/_units/waiter.py @@ -13,6 +13,9 @@ from ..errors import WaitTimeoutError, NoRectError class OriginWaiter(object): + def __init__(self, owner): + self._owner = owner + def __call__(self, second, scope=None): """等待若干秒,如传入两个参数,等待时间为这两个数间的一个随机数 :param second: 秒数 @@ -24,11 +27,10 @@ class OriginWaiter(object): else: from random import uniform sleep(uniform(second, scope)) + return self._owner class BrowserWaiter(OriginWaiter): - def __init__(self, owner): - self._owner = owner def new_tab(self, timeout=None, curr_tab=None, raise_err=None): """等待新标签页出现 @@ -105,11 +107,6 @@ class BrowserWaiter(OriginWaiter): class BaseWaiter(OriginWaiter): - def __init__(self, page_or_ele): - """ - :param page_or_ele: 页面对象或元素对象 - """ - self._driver = page_or_ele def ele_deleted(self, loc_or_ele, timeout=None, raise_err=None): """等待元素从DOM中删除 @@ -118,7 +115,7 @@ class BaseWaiter(OriginWaiter): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - ele = self._driver._ele(loc_or_ele, raise_err=False, timeout=0) + ele = self._owner._ele(loc_or_ele, raise_err=False, timeout=0) return ele.wait.deleted(timeout, raise_err=raise_err) if ele else True def ele_displayed(self, loc_or_ele, timeout=None, raise_err=None): @@ -129,9 +126,9 @@ class BaseWaiter(OriginWaiter): :return: 是否等待成功 """ if timeout is None: - timeout = self._driver.timeout + timeout = self._owner.timeout end_time = perf_counter() + timeout - ele = self._driver._ele(loc_or_ele, raise_err=False, timeout=timeout) + ele = self._owner._ele(loc_or_ele, raise_err=False, timeout=timeout) timeout = end_time - perf_counter() if timeout <= 0: if raise_err is True or Settings.raise_when_wait_failed is True: @@ -148,9 +145,9 @@ class BaseWaiter(OriginWaiter): :return: 是否等待成功 """ if timeout is None: - timeout = self._driver.timeout + timeout = self._owner.timeout end_time = perf_counter() + timeout - ele = self._driver._ele(loc_or_ele, raise_err=False, timeout=timeout) + ele = self._owner._ele(loc_or_ele, raise_err=False, timeout=timeout) timeout = end_time - perf_counter() if timeout <= 0: if raise_err is True or Settings.raise_when_wait_failed is True: @@ -199,10 +196,10 @@ class BaseWaiter(OriginWaiter): else [get_loc(l)[1] for l in locators]) method = any if any_one else all - timeout = self._driver.timeout if timeout is None else timeout + timeout = self._owner.timeout if timeout is None else timeout end_time = perf_counter() + timeout while perf_counter() < end_time: - if method([_find(l, self._driver.driver) for l in locators]): + if method([_find(l, self._owner.driver) for l in locators]): return True sleep(.01) if raise_err is True or Settings.raise_when_wait_failed is True: @@ -228,9 +225,9 @@ class BaseWaiter(OriginWaiter): def upload_paths_inputted(self): """等待自动填写上传文件路径""" - end_time = perf_counter() + self._driver.timeout + end_time = perf_counter() + self._owner.timeout while perf_counter() < end_time: - if not self._driver._upload_list: + if not self._owner._upload_list: return True sleep(.01) return False @@ -241,22 +238,22 @@ class BaseWaiter(OriginWaiter): :param cancel_it: 是否取消该任务 :return: 成功返回任务对象,失败返回False """ - if not self._driver.browser._dl_mgr._running: + if not self._owner.browser._dl_mgr._running: raise RuntimeError('此功能需显式设置下载路径(使用set.download_path()方法、配置对象或ini文件均可)。') - self._driver.browser._dl_mgr.set_flag(self._driver.tab_id, False if cancel_it else True) + self._owner.browser._dl_mgr.set_flag(self._owner.tab_id, False if cancel_it else True) if timeout is None: - timeout = self._driver.timeout + timeout = self._owner.timeout r = False end_time = perf_counter() + timeout while perf_counter() < end_time: - v = self._driver.browser._dl_mgr.get_flag(self._driver.tab_id) + v = self._owner.browser._dl_mgr.get_flag(self._owner.tab_id) if not isinstance(v, bool): r = v break sleep(.005) - self._driver.browser._dl_mgr.set_flag(self._driver.tab_id, None) + self._owner.browser._dl_mgr.set_flag(self._owner.tab_id, None) return r def url_change(self, text, exclude=False, timeout=None, raise_err=None): @@ -289,14 +286,14 @@ class BaseWaiter(OriginWaiter): :return: 是否等待成功 """ if timeout is None: - timeout = self._driver.timeout + timeout = self._owner.timeout end_time = perf_counter() + timeout while perf_counter() < end_time: if arg == 'url': - val = self._driver.url + val = self._owner.url elif arg == 'title': - val = self._driver.title + val = self._owner.title else: raise ValueError if (not exclude and text in val) or (exclude and text not in val): @@ -318,10 +315,10 @@ class BaseWaiter(OriginWaiter): """ if timeout != 0: if timeout is None or timeout is True: - timeout = self._driver.timeout + timeout = self._owner.timeout end_time = perf_counter() + timeout while perf_counter() < end_time: - if self._driver._is_loading == start: + if self._owner._is_loading == start: return True sleep(gap) @@ -340,23 +337,23 @@ class TabWaiter(BaseWaiter): :param cancel_if_timeout: 超时时是否取消剩余任务 :return: 是否等待成功 """ - if not self._driver.browser._dl_mgr._running: + if not self._owner.browser._dl_mgr._running: raise RuntimeError('此功能需显式设置下载路径(使用set.download_path()方法、配置对象或ini文件均可)。') if not timeout: - while self._driver.browser._dl_mgr.get_tab_missions(self._driver.tab_id): + while self._owner.browser._dl_mgr.get_tab_missions(self._owner.tab_id): sleep(.5) return True else: end_time = perf_counter() + timeout while perf_counter() < end_time: - if not self._driver.browser._dl_mgr.get_tab_missions(self._driver.tab_id): + if not self._owner.browser._dl_mgr.get_tab_missions(self._owner.tab_id): return True sleep(.5) - if self._driver.browser._dl_mgr.get_tab_missions(self._driver.tab_id): + if self._owner.browser._dl_mgr.get_tab_missions(self._owner.tab_id): if cancel_if_timeout: - for m in self._driver.browser._dl_mgr.get_tab_missions(self._driver.tab_id): + for m in self._owner.browser._dl_mgr.get_tab_missions(self._owner.tab_id): m.cancel() return False else: @@ -364,25 +361,22 @@ class TabWaiter(BaseWaiter): def alert_closed(self): """等待弹出框关闭""" - while not self._driver.states.has_alert: + while not self._owner.states.has_alert: sleep(.2) - while self._driver.states.has_alert: + while self._owner.states.has_alert: sleep(.2) class PageWaiter(TabWaiter): """ChromiumPage和MixPage的等待对象""" - def __init__(self, page): - super().__init__(page) - def new_tab(self, timeout=None, raise_err=None): """等待新标签页出现 :param timeout: 超时时间(秒),为None则使用页面对象timeout属性 :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 等到新标签页返回其id,否则返回False """ - return self._driver.browser.wait.new_tab(timeout=timeout, raise_err=raise_err) + return self._owner.browser.wait.new_tab(timeout=timeout, raise_err=raise_err) def all_downloads_done(self, timeout=None, cancel_if_timeout=True): """等待所有浏览器下载任务结束 @@ -390,19 +384,16 @@ class PageWaiter(TabWaiter): :param cancel_if_timeout: 超时时是否取消剩余任务 :return: 是否等待成功 """ - return self._driver.browser.wait.all_downloads_done(timeout=timeout, cancel_if_timeout=cancel_if_timeout) + return self._owner.browser.wait.all_downloads_done(timeout=timeout, cancel_if_timeout=cancel_if_timeout) class ElementWaiter(OriginWaiter): """等待元素在dom中某种状态,如删除、显示、隐藏""" - def __init__(self, owner, ele): - """等待元素在dom中某种状态,如删除、显示、隐藏 - :param owner: 元素所在页面 - :param ele: 要执行等待的元素 - """ - self._owner = owner - self._ele = ele + @property + def _timeout(self): + """返回超时设置""" + return self._owner.owner.timeout def deleted(self, timeout=None, raise_err=None): """等待元素从dom删除 @@ -467,10 +458,10 @@ class ElementWaiter(OriginWaiter): :return: 是否等待成功 """ if timeout is None: - timeout = self._owner.timeout + timeout = self._timeout end_time = perf_counter() + timeout while perf_counter() < end_time: - if not self._ele.states.is_enabled or not self._ele.states.is_alive: + if not self._owner.states.is_enabled or not self._owner.states.is_alive: return True sleep(.05) @@ -487,12 +478,12 @@ class ElementWaiter(OriginWaiter): :return: 是否等待成功 """ if timeout is None: - timeout = self._owner.timeout + timeout = self._timeout end_time = perf_counter() + timeout while perf_counter() < end_time: try: - size = self._ele.states.has_rect - location = self._ele.rect.location + size = self._owner.states.has_rect + location = self._owner.rect.location break except NoRectError: pass @@ -502,10 +493,10 @@ class ElementWaiter(OriginWaiter): while perf_counter() < end_time: sleep(gap) - if self._ele.rect.size == size and self._ele.rect.location == location: + if self._owner.rect.size == size and self._owner.rect.location == location: return True - size = self._ele.rect.size - location = self._ele.rect.location + size = self._owner.rect.size + location = self._owner.rect.location if raise_err is True or Settings.raise_when_wait_failed is True: raise WaitTimeoutError(f'等待元素停止运动失败(等待{timeout}秒)。') @@ -543,15 +534,15 @@ class ElementWaiter(OriginWaiter): :param err_text: 抛出错误时显示的信息 :return: 是否等待成功 """ - a = self._ele.states.__getattribute__(attr) + a = self._owner.states.__getattribute__(attr) if (a and mode) or (not a and not mode): return True if isinstance(a, bool) else a if timeout is None: - timeout = self._owner.timeout + timeout = self._timeout end_time = perf_counter() + timeout while perf_counter() < end_time: - a = self._ele.states.__getattribute__(attr) + a = self._owner.states.__getattribute__(attr) if (a and mode) or (not a and not mode): return True if isinstance(a, bool) else a sleep(.05) @@ -564,9 +555,14 @@ class ElementWaiter(OriginWaiter): class FrameWaiter(BaseWaiter, ElementWaiter): - def __init__(self, frame): + def __init__(self, owner): """ - :param frame: ChromiumFrame对象 + :param owner: ChromiumFrame对象 """ - super().__init__(frame) - super(BaseWaiter, self).__init__(frame, frame.frame_ele) + super().__init__(owner) + super(BaseWaiter, self).__init__(owner.frame_ele) + + @property + def _timeout(self): + """返回超时设置""" + return self._owner.timeout diff --git a/DrissionPage/_units/waiter.pyi b/DrissionPage/_units/waiter.pyi index 243a014..d8824ac 100644 --- a/DrissionPage/_units/waiter.pyi +++ b/DrissionPage/_units/waiter.pyi @@ -13,28 +13,36 @@ from .._elements.chromium_element import ChromiumElement from .._pages.chromium_base import ChromiumBase from .._pages.chromium_frame import ChromiumFrame from .._pages.chromium_page import ChromiumPage +from .._pages.mix_page import MixPage +from .._pages.tabs import ChromiumTab, MixTab class OriginWaiter(object): - def __call__(self, second: float, scope: float = None) -> None: ... + _owner = ... + + def __init__(self, owner): ... + + def __call__(self, second: float, scope: float = None): ... class BrowserWaiter(OriginWaiter): - def __init__(self, owner: Chromium): - self._owner = owner + _owner: Chromium = ... + + def __init__(self, owner: Chromium): ... + + def __call__(self, second: float, scope: float = None) -> Chromium: ... def download_begin(self, timeout: float = None, cancel_it: bool = False) -> DownloadMission: ... def new_tab(self, timeout: float = None, curr_tab: str = None, raise_err: bool = None) -> Union[str, bool]: ... - def all_downloads_done(self, timeout: float = None, cancel_if_timeout: bool = True): ... + def all_downloads_done(self, timeout: float = None, cancel_if_timeout: bool = True) -> bool: ... class BaseWaiter(OriginWaiter): - def __init__(self, page: ChromiumBase): - self._driver: ChromiumBase = ... + _owner: ChromiumBase = ... - def __call__(self, second: float, scope: float = None) -> None: ... + def __call__(self, second: float, scope: float = None) -> ChromiumBase: ... def ele_deleted(self, loc_or_ele: Union[str, tuple, ChromiumElement], @@ -76,6 +84,11 @@ class BaseWaiter(OriginWaiter): class TabWaiter(BaseWaiter): + _owner: Union[ChromiumTab, MixTab] = ... + + def __init__(self, owner: Union[ChromiumTab, MixTab]): ... + + def __call__(self, second: float, scope: float = None) -> Union[ChromiumTab, MixTab]: ... def downloads_done(self, timeout: float = None, cancel_if_timeout: bool = True) -> bool: ... @@ -83,7 +96,11 @@ class TabWaiter(BaseWaiter): class PageWaiter(TabWaiter): - _driver: ChromiumPage = ... + _owner: Union[ChromiumPage, MixPage] = ... + + def __init__(self, owner: Union[ChromiumPage, MixPage]): ... + + def __call__(self, second: float, scope: float = None) -> Union[ChromiumPage, MixPage]: ... def new_tab(self, timeout: float = None, raise_err: bool = None) -> Union[str, bool]: ... @@ -91,11 +108,14 @@ class PageWaiter(TabWaiter): class ElementWaiter(OriginWaiter): - def __init__(self, owner: ChromiumBase, ele: ChromiumElement): - self._ele: ChromiumElement = ... - self._owner: ChromiumBase = ... + _owner: ChromiumElement = ... - def __call__(self, second: float, scope: float = None) -> None: ... + def __init__(self, owner: ChromiumElement): ... + + def __call__(self, second: float, scope: float = None) -> ChromiumElement: ... + + @property + def _timeout(self) -> float: ... def deleted(self, timeout: float = None, raise_err: bool = None) -> bool: ... @@ -130,4 +150,8 @@ class ElementWaiter(OriginWaiter): class FrameWaiter(BaseWaiter, ElementWaiter): - def __init__(self, frame: ChromiumFrame): ... + _owner: ChromiumFrame = ... + + def __init__(self, owner: ChromiumFrame): ... + + def __call__(self, second: float, scope: float = None) -> ChromiumFrame: ...