WebDriverWait(self.driver, wait_time).until(EC.visibility_of_element_located((by, locator)))源码解析
看WebDriverWait(self.driver, wait_time).until(EC.visibility_of_element_located((by, locator)))源码的时候,不太明白visibility_of_element_located((by, locator))内为什么是两层括号,因为源码是这样写的:
class visibility_of_element_located(object): """ An expectation for checking that an element is present on the DOM of a page and visible. Visibility means that the element is not only displayed but also has a height and width that is greater than 0. locator - used to find the element returns the WebElement once it is located and visible """ def __init__(self, locator): self.locator = locator def __call__(self, driver): try: return _element_if_visible(_find_element(driver, self.locator)) except StaleElementReferenceException: return False
__call__()的作用是,让实例对象也像函数一样作为可调用对象来使用,正常来讲visibility_of_element_located(locator)(driver)才对,直到看了下until的源码,才发现有这一层:value = method(self._driver),其中这个method就是EC.visibility_of_element_located((by, locator))实例对象,method(self._driver)就是EC.visibility_of_element_located((by, locator))(self._driver),也就是把实例对象作为参数一样传了个参数,self._driver。其实前者((by, locator))是__init__()做初始化,只不过参数是个元组(by, locator),这个元组作为实例对象的属性self.locator,而后者(self._driver)就是__call__()的作用
def until(self, method, message=''): """Calls the method provided with the driver as an argument until the \ return value is not False.""" screen = None stacktrace = None end_time = time.time() + self._timeout while True: try: value = method(self._driver) if value: return value except self._ignored_exceptions as exc: screen = getattr(exc, 'screen', None) stacktrace = getattr(exc, 'stacktrace', None) time.sleep(self._poll) if time.time() > end_time: break raise TimeoutException(message, screen, stacktrace)
当使用EC.visibility_of_element_located((by, locator))(self._driver)时,总共进行了以下的步骤:
1. 实例化一个EC.visibility_of_element_located,传入参数(by, locator),将其赋值给属性self.locator
2. 通过__call__(),将参数self._driver(实际上这个self.driver就是WebDriverWait中的self.driver传进来的)和self.locator传给内层方法_find_element()
2.1 这个find_element(self._driver, self.locator)对应着源码中的driver和by,再通过*by解包(by, locator),使其这样调用driver.find_element(by, locator)
def _find_element(driver, by): """Looks up an element. Logs and re-raises ``WebDriverException`` if thrown.""" try: return driver.find_element(*by) except NoSuchElementException as e: raise e except WebDriverException as e: raise e
2.2 然后driver.find_element(by, locator)找到元素对象并返回给_find_element()方法
2.3 _find_element()方法拿到元素对象,此时作为参数传递给_element_if_visisble()方法,element_if_visisble()方法通过判断元素是否可见,返回True或False
until作为一个判断条件,来决定是否继续寻找可见元素,主要有这几种情况:如果EC.visibility_of_element_located((by, locator))(self._driver)返回为True,则返回对应的True;如果已超时,则抛出超时异常