diff --git a/examples/cdp_mode/raw_cdp_drivers.py b/examples/cdp_mode/raw_cdp_drivers.py new file mode 100644 index 00000000000..fb7e0b992cf --- /dev/null +++ b/examples/cdp_mode/raw_cdp_drivers.py @@ -0,0 +1,27 @@ +# An example of switching between multiple drivers +from seleniumbase import SB + +with SB(uc=True, test=True) as sb: + url1 = "https://seleniumbase.io/antibot/login" + sb.activate_cdp_mode(url1) + url2 = "https://seleniumbase.io/hobbit/login" + driver2 = sb.get_new_driver(undetectable=True) + sb.activate_cdp_mode(url2) + sb.sleep(1) + sb.switch_to_default_driver() + sb.assert_url_contains("antibot") + print(sb.get_current_url()) + sb.type("input#username", "demo_user") + sb.type("input#password", "secret_pass") + sb.cdp.gui_click_element("button") + sb.sleep(1) + sb.cdp.gui_click_element("a#log-in") + sb.assert_text("Welcome!", "h1") + sb.sleep(2) + sb.switch_to_driver(driver2) + sb.assert_url_contains("hobbit") + print(sb.get_current_url()) + sb.cdp.gui_click_element("button") + sb.assert_text("Welcome to Middle Earth!") + sb.click("img") + sb.sleep(3) diff --git a/examples/multiple_cdp_drivers.py b/examples/multiple_cdp_drivers.py new file mode 100644 index 00000000000..7f2924428ce --- /dev/null +++ b/examples/multiple_cdp_drivers.py @@ -0,0 +1,14 @@ +from seleniumbase import BaseCase +BaseCase.main(__name__, __file__, "--uc") + + +class MultipleDriversTest(BaseCase): + def test_multiple_drivers(self): + url1 = "https://seleniumbase.io/demo_page" + self.activate_cdp_mode(url1) + driver1 = self.driver + url2 = "https://seleniumbase.io/coffee/" + driver2 = self.get_new_driver(undetectable=True) + self.activate_cdp_mode(url2) + print("\n" + driver1.get_current_url()) + print(driver2.get_current_url()) diff --git a/examples/raw_cdp_drivers.py b/examples/raw_cdp_drivers.py new file mode 100644 index 00000000000..e105fabe56e --- /dev/null +++ b/examples/raw_cdp_drivers.py @@ -0,0 +1,17 @@ +from seleniumbase import SB + +with SB(uc=True, test=True) as sb: + url1 = "https://seleniumbase.io/demo_page" + sb.activate_cdp_mode(url1) + driver1 = sb.driver + url2 = "https://seleniumbase.io/coffee/" + driver2 = sb.get_new_driver(undetectable=True) + sb.activate_cdp_mode(url2) + print(driver1.get_current_url()) + print(driver2.get_current_url()) + sb.switch_to_default_driver() + sb.assert_url_contains("demo_page") + print(sb.get_current_url()) + sb.switch_to_driver(driver2) + sb.assert_url_contains("coffee") + print(sb.get_current_url()) diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt index 041fce6e933..11968232aa5 100644 --- a/mkdocs_build/requirements.txt +++ b/mkdocs_build/requirements.txt @@ -2,7 +2,7 @@ # Minimum Python version: 3.9 (for generating docs only) regex>=2024.11.6 -pymdown-extensions>=10.14.2 +pymdown-extensions>=10.14.3 pipdeptree>=2.25.0 python-dateutil>=2.8.2 Markdown==3.7 @@ -11,10 +11,10 @@ ghp-import==2.1.0 watchdog==6.0.0 cairocffi==1.7.1 pathspec==0.12.1 -Babel==2.16.0 +Babel==2.17.0 paginate==0.5.7 mkdocs==1.6.1 -mkdocs-material==9.5.50 +mkdocs-material==9.6.1 mkdocs-exclude-search==0.6.6 mkdocs-simple-hooks==0.1.5 mkdocs-material-extensions==1.3.1 diff --git a/requirements.txt b/requirements.txt index c183efc6a54..af495d6bef6 100755 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ setuptools~=70.2;python_version<"3.10" setuptools>=75.8.0;python_version>="3.10" wheel>=0.45.1 attrs>=25.1.0 -certifi>=2024.12.14 +certifi>=2025.1.31 exceptiongroup>=1.2.2 websockets~=13.1;python_version<"3.9" websockets>=14.2;python_version>="3.9" diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index 5060896cea9..813b56028a7 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.34.4" +__version__ = "4.34.5" diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 689b5b2fc99..117fdc62d87 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -749,6 +749,7 @@ def uc_open_with_cdp_mode(driver, url=None): core_items.browser = cdp.browser core_items.tab = cdp.tab core_items.util = cdp.util + cdp._swap_driver = CDPM._swap_driver cdp.core = core_items cdp.loop = cdp.get_event_loop() driver.cdp = cdp diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index f35b1a3b60a..403011e894e 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -19,6 +19,11 @@ def __init__(self, loop, page, driver): self.page = page self.driver = driver + def _swap_driver(self, driver): + self.driver = driver + self.page = driver.cdp.page + self.loop = driver.cdp.loop + def __slow_mode_pause_if_set(self): if ( (hasattr(sb_config, "demo_mode") and sb_config.demo_mode) diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index 81696196ce8..8856ba57be4 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -4358,6 +4358,8 @@ def switch_to_driver(self, driver): self.driver = driver if self.driver in self._drivers_browser_map: self.browser = self._drivers_browser_map[self.driver] + if self.__is_cdp_swap_needed(): + self.cdp._swap_driver(self.driver) self.bring_active_window_to_front() def switch_to_default_driver(self): @@ -4366,6 +4368,8 @@ def switch_to_default_driver(self): self.driver = self._default_driver if self.driver in self._drivers_browser_map: self.browser = self._drivers_browser_map[self.driver] + if self.__is_cdp_swap_needed(): + self.cdp._swap_driver(self.driver) self.bring_active_window_to_front() def save_screenshot( diff --git a/seleniumbase/undetected/__init__.py b/seleniumbase/undetected/__init__.py index 696c888b956..e72cd837276 100644 --- a/seleniumbase/undetected/__init__.py +++ b/seleniumbase/undetected/__init__.py @@ -25,6 +25,7 @@ "CDP", "find_chrome_executable", ) +IS_MAC = "darwin" in sys.platform IS_POSIX = sys.platform.startswith(("darwin", "cygwin", "linux")) logger = logging.getLogger("uc") logger.setLevel(logging.getLogger().getEffectiveLevel()) @@ -311,7 +312,21 @@ def __init__( setattr(service_, "creationflags", creationflags) if hasattr(service_, "creation_flags"): setattr(service_, "creation_flags", creationflags) - super().__init__(options=options, service=service_) + try: + super().__init__(options=options, service=service_) + except OSError as e: + if IS_MAC and "Bad CPU type in executable" in str(e): + print(str(e)) + message = ( + "Missing a macOS dependency:\n" + "Your Mac needs Rosetta 2 to use UC Mode!\n" + 'Run: "softwareupdate --install-rosetta"\n' + "Info: " + "https://apple.stackexchange.com/a/408379/607628" + ) + raise Exception(message) + else: + raise self.reactor = None if enable_cdp_events: if logging.getLogger().getEffectiveLevel() == logging.DEBUG: diff --git a/setup.py b/setup.py index 7afaba9712c..34275be5dbe 100755 --- a/setup.py +++ b/setup.py @@ -153,7 +153,7 @@ 'setuptools>=75.8.0;python_version>="3.10"', 'wheel>=0.45.1', 'attrs>=25.1.0', - "certifi>=2024.12.14", + "certifi>=2025.1.31", "exceptiongroup>=1.2.2", 'websockets~=13.1;python_version<"3.9"', 'websockets>=14.2;python_version>="3.9"',