diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index b48a268bc29..830eb00bc33 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -467,7 +467,7 @@ sb.cdp.close_active_tab() sb.cdp.get_active_tab() sb.cdp.get_tabs() sb.cdp.get_window() -sb.cdp.get_text(selector) +sb.cdp.get_text(selector="body") sb.cdp.get_title() sb.cdp.get_current_url() sb.cdp.get_origin() diff --git a/examples/cdp_mode/raw_cdp_yc_news.py b/examples/cdp_mode/raw_cdp_yc_news.py index 55cb6ed0282..e9157e78ab5 100644 --- a/examples/cdp_mode/raw_cdp_yc_news.py +++ b/examples/cdp_mode/raw_cdp_yc_news.py @@ -5,3 +5,4 @@ elements = sb.find_elements("span.titleline > a") for element in elements: print("* " + element.text) +sb.driver.stop() diff --git a/examples/cdp_mode/raw_multi_c_async.py b/examples/cdp_mode/raw_multi_c_async.py new file mode 100644 index 00000000000..eed9486e060 --- /dev/null +++ b/examples/cdp_mode/raw_multi_c_async.py @@ -0,0 +1,32 @@ +# Testing multiple CDP drivers using the async API +import asyncio +from concurrent.futures import ThreadPoolExecutor +from random import randint +from seleniumbase import cdp_driver +from seleniumbase import decorators + + +async def main(url): + driver = await cdp_driver.start_async() + page = await driver.get(url) + await page.set_window_rect(randint(4, 600), randint(8, 410), 860, 500) + await page.sleep(2.6) + await page.solve_captcha() + await page.sleep(2.2) + if not await page.is_element_visible("img#captcha-success"): + await page.solve_captcha() + await page.sleep(2.2) + driver.stop() + + +def set_up_loop(url): + loop = asyncio.new_event_loop() + loop.run_until_complete(main(url)) + + +if __name__ == "__main__": + urls = ["https://seleniumbase.io/apps/turnstile" for i in range(4)] + with decorators.print_runtime("raw_multi_c_async.py"): + with ThreadPoolExecutor(max_workers=len(urls)) as executor: + for url in urls: + executor.submit(set_up_loop, url) diff --git a/examples/cdp_mode/raw_multi_captcha.py b/examples/cdp_mode/raw_multi_captcha.py index 40766fb0916..a05905128e0 100644 --- a/examples/cdp_mode/raw_multi_captcha.py +++ b/examples/cdp_mode/raw_multi_captcha.py @@ -8,10 +8,13 @@ def main(url): sb = sb_cdp.Chrome(url, lang="en", incognito=True) sb.set_window_rect(randint(4, 680), randint(8, 380), 840, 520) - sb.sleep(2) + sb.sleep(2.6) sb.gui_click_captcha() - sb.sleep(2) - sb.driver.quit() + sb.sleep(2.2) + if not sb.is_element_visible("img#captcha-success"): + sb.solve_captcha() + sb.sleep(2.2) + sb.driver.stop() if __name__ == "__main__": diff --git a/help_docs/cdp_mode_methods.md b/help_docs/cdp_mode_methods.md index f5a503ce578..0a9dc7f2ff7 100644 --- a/help_docs/cdp_mode_methods.md +++ b/help_docs/cdp_mode_methods.md @@ -93,7 +93,7 @@ sb.cdp.close_active_tab() sb.cdp.get_active_tab() sb.cdp.get_tabs() sb.cdp.get_window() -sb.cdp.get_text(selector) +sb.cdp.get_text(selector="body") sb.cdp.get_title() sb.cdp.get_current_url() sb.cdp.get_origin() diff --git a/help_docs/method_summary.md b/help_docs/method_summary.md index 151e843352e..77a70edf85d 100644 --- a/help_docs/method_summary.md +++ b/help_docs/method_summary.md @@ -72,7 +72,7 @@ self.click_link(link_text, timeout=None) self.click_partial_link(partial_link_text, timeout=None) # Duplicates: # self.click_partial_link_text(partial_link_text, timeout=None) -self.get_text(selector="html", by="css selector", timeout=None) +self.get_text(selector="body", by="css selector", timeout=None) self.get_attribute(selector, attribute, by="css selector", timeout=None, hard_fail=True) self.set_attribute(selector, attribute, value, by="css selector", timeout=None, scroll=False) self.set_attributes(selector, attribute, value, by="css selector") @@ -733,7 +733,7 @@ driver.is_element_visible(selector) driver.is_text_visible(text, selector) driver.is_exact_text_visible(text, selector) driver.is_attribute_present(selector, attribute) -driver.get_text(selector) +driver.get_text(selector="body") driver.js_click(selector) driver.get_active_element_css() driver.get_locale_code() diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt index 7494b46ea18..c15bb5df5b2 100644 --- a/mkdocs_build/requirements.txt +++ b/mkdocs_build/requirements.txt @@ -3,7 +3,7 @@ regex>=2026.2.28 pymdown-extensions>=10.21 -pipdeptree>=2.31.0 +pipdeptree>=2.33.0 python-dateutil>=2.8.2 Markdown==3.10.2 click==8.3.1 diff --git a/requirements.txt b/requirements.txt index 16cf4f34a2b..4bda62596de 100755 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ packaging>=26.0 setuptools~=70.2;python_version<"3.10" setuptools>=82.0.1;python_version>="3.10" wheel>=0.46.3 -attrs>=25.4.0 +attrs>=26.1.0 certifi>=2026.2.25 exceptiongroup>=1.3.1 websockets~=15.0.1;python_version<"3.10" @@ -11,7 +11,7 @@ websockets>=16.0;python_version>="3.10" filelock~=3.19.1;python_version<"3.10" filelock>=3.25.2;python_version>="3.10" fasteners>=0.20 -mycdp>=1.3.6 +mycdp>=1.3.7 pynose>=1.5.5 platformdirs~=4.4.0;python_version<"3.10" platformdirs>=4.9.4;python_version>="3.10" @@ -78,7 +78,7 @@ rich>=14.3.3,<15 coverage>=7.10.7;python_version<"3.10" coverage>=7.13.5;python_version>="3.10" -pytest-cov>=7.0.0 +pytest-cov>=7.1.0 flake8==7.3.0 mccabe==0.7.0 pyflakes==3.4.0 diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index 1da33aaa69d..dc6ed23571e 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.47.4" +__version__ = "4.47.5" diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index c5028c838f9..2edea7ca128 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -296,6 +296,7 @@ def extend_driver( driver.switch_to_tab = DM.switch_to_tab driver.switch_to_frame = DM.switch_to_frame driver.reset_window_size = DM.reset_window_size + driver.stop = driver.quit if recorder_ext: from seleniumbase.js_code.recorder_js import recorder_js recorder_code = ( diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index dac490054f8..d74a03bcd6f 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -1335,7 +1335,7 @@ def get_tabs(self): def get_window(self): return self.loop.run_until_complete(self.page.get_window()) - def get_text(self, selector): + def get_text(self, selector="body"): return self.find_element(selector).text_all def get_title(self): diff --git a/seleniumbase/fixtures/page_actions.py b/seleniumbase/fixtures/page_actions.py index 25632c1732b..49744e8aa3f 100644 --- a/seleniumbase/fixtures/page_actions.py +++ b/seleniumbase/fixtures/page_actions.py @@ -2138,7 +2138,7 @@ def wait_for_non_empty_text( def get_text( driver, - selector, + selector="body", by="css selector", timeout=settings.LARGE_TIMEOUT ): diff --git a/seleniumbase/undetected/cdp_driver/tab.py b/seleniumbase/undetected/cdp_driver/tab.py index 53a83e549eb..28b577cace9 100644 --- a/seleniumbase/undetected/cdp_driver/tab.py +++ b/seleniumbase/undetected/cdp_driver/tab.py @@ -9,7 +9,7 @@ import urllib.parse import warnings from contextlib import suppress -from filelock import FileLock +from filelock import AsyncFileLock from seleniumbase import config as sb_config from seleniumbase.fixtures import constants from seleniumbase.fixtures import js_utils @@ -1459,8 +1459,8 @@ async def __gui_click_recaptcha(self): y = gui_e_y + y_offset sb_config._saved_cf_x_y = (x, y) # For debugging later await self.sleep(0.11) - gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) - with await asyncio.to_thread(gui_lock.acquire): + gui_lock = AsyncFileLock(constants.MultiBrowser.PYAUTOGUILOCK) + async with gui_lock: await self.bring_to_front() await self.sleep(0.05) await self.click_with_offset( @@ -1485,8 +1485,8 @@ async def __cdp_click_incapsula_hcaptcha(self): x_offset = 30 y_offset = 36 was_clicked = False - gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) - with gui_lock: # Prevent issues with multiple processes + gui_lock = AsyncFileLock(constants.MultiBrowser.PYAUTOGUILOCK) + async with gui_lock: await self.bring_to_front() await self.sleep(0.056) if "--debug" in sys.argv: @@ -1792,8 +1792,8 @@ async def solve_captcha(self): y = e_y + y_offset sb_config._saved_cf_x_y = (x, y) # For debugging later await self.sleep(0.11) - gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) - with await asyncio.to_thread(gui_lock.acquire): + gui_lock = AsyncFileLock(constants.MultiBrowser.PYAUTOGUILOCK) + async with gui_lock: await self.bring_to_front() await self.sleep(0.05) await self.click_with_offset( diff --git a/setup.py b/setup.py index 6584b95226c..52d6f309456 100755 --- a/setup.py +++ b/setup.py @@ -151,7 +151,7 @@ 'setuptools~=70.2;python_version<"3.10"', # Newer ones had issues 'setuptools>=82.0.1;python_version>="3.10"', 'wheel>=0.46.3', - 'attrs>=25.4.0', + 'attrs>=26.1.0', 'certifi>=2026.2.25', 'exceptiongroup>=1.3.1', 'websockets~=15.0.1;python_version<"3.10"', @@ -159,7 +159,7 @@ 'filelock~=3.19.1;python_version<"3.10"', 'filelock>=3.25.2;python_version>="3.10"', 'fasteners>=0.20', - 'mycdp>=1.3.6', + 'mycdp>=1.3.7', 'pynose>=1.5.5', 'platformdirs~=4.4.0;python_version<"3.10"', 'platformdirs>=4.9.4;python_version>="3.10"', @@ -235,7 +235,7 @@ "coverage": [ 'coverage>=7.10.7;python_version<"3.10"', 'coverage>=7.13.5;python_version>="3.10"', - 'pytest-cov>=7.0.0', + 'pytest-cov>=7.1.0', ], # pip install -e .[flake8] # Usage: flake8