-
-
Notifications
You must be signed in to change notification settings - Fork 8.6k
Description
What happened?
I have noticed that on my Python-based Seleniums runs, when using a WebDriverWait to check (let's say by CSS selector) for a non existing element in the DOM, the call never takes less than 500ms, whereas I consistently get <100ms for existing elements.
The behavior does not arise in Java implementations of the same scenario.
My best guess is that the Java and Python implementation have different sleep strategy orchestration.
Current python loop :
selenium/py/selenium/webdriver/support/wait.py
Lines 94 to 105 in 1b64798
| 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.monotonic() > end_time: | |
| break | |
| raise TimeoutException(message, screen, stacktrace) |
Current java loop :
selenium/java/src/org/openqa/selenium/support/ui/FluentWait.java
Lines 201 to 237 in 1b64798
| while (true) { | |
| try { | |
| V value = isTrue.apply(input); | |
| if (value != null && (Boolean.class != value.getClass() || Boolean.TRUE.equals(value))) { | |
| return value; | |
| } | |
| // Clear the last exception; if another retry or timeout exception would | |
| // be caused by a false or null value, the last exception is not the | |
| // cause of the timeout. | |
| lastException = null; | |
| } catch (Throwable e) { | |
| lastException = propagateIfNotIgnored(e); | |
| } | |
| // Check the timeout after evaluating the function to ensure conditions | |
| // with a zero timeout can succeed. | |
| if (end.isBefore(clock.instant())) { | |
| String message = messageSupplier != null ? messageSupplier.get() : null; | |
| String timeoutMessage = | |
| String.format( | |
| "Expected condition failed: %s (tried for %d second(s) with %d milliseconds" | |
| + " interval)", | |
| message == null ? "waiting for " + isTrue : message, | |
| timeout.getSeconds(), | |
| interval.toMillis()); | |
| throw timeoutException(timeoutMessage, lastException); | |
| } | |
| try { | |
| sleeper.sleep(interval); | |
| } catch (InterruptedException e) { | |
| Thread.currentThread().interrupt(); | |
| throw new WebDriverException(e); | |
| } | |
| } |
Note that the Python orchestration is :
- Timestamp the timeout end
- Perform the lookup
- sleep
- Check now vs. timeout to exit
The Java implementation is :
- Timestamp the timeout end
- Perform the lookup
- Check new vs. timeout to exit
- Sleep
The inversion of steps 3 and 4 is manifest IMO.
The Java version looks more sensible to me, as it allows for effectively having timeout=0 checks, while python implementations in practice will never get a sub pollingInterval check (for non-existing elements).
How can we reproduce the issue?
WebDriverWait(driver, 0).until(
visibility_of_any_elements_located((By.CSS_SELECTOR, "#anyIdThatDoesNotExist"))
);
Notice you will never get <500ms (`POLL_FREQUENCY`) results.Relevant log output
N/AOperating System
macOs, Linux
Selenium version
4.0.25
What are the browser(s) and version(s) where you see this issue?
Chrome 129
What are the browser driver(s) and version(s) where you see this issue?
ChromeDriver 129
Are you using Selenium Grid?
No response