Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/cdp_mode/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ sb.cdp.get_rd_port() # Returns the remote-debugging port
sb.cdp.get_rd_url() # Returns the remote-debugging URL
sb.cdp.get_endpoint_url() # Same as sb.cdp.get_rd_url()
sb.cdp.get_port() # Same as sb.cdp.get_rd_port()
sb.cdp.get_websocket_url() # Returns the websocket URL
sb.cdp.add_handler(event, handler)
sb.cdp.find_element(selector, best_match=False, timeout=None)
sb.cdp.find(selector, best_match=False, timeout=None)
Expand Down
15 changes: 15 additions & 0 deletions examples/cdp_mode/raw_amazon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
url = "https://www.amazon.com"
sb.activate_cdp_mode(url)
sb.sleep(2)
sb.click_if_visible('button[alt="Continue shopping"]')
sb.sleep(2)
sb.press_keys('input[role="searchbox"]', "TI-89\n")
sb.sleep(3)
print(sb.get_page_title())
sb.save_as_pdf_to_logs()
sb.save_page_source_to_logs()
sb.save_screenshot_to_logs()
print("Logs have been saved to: ./latest_logs/")
15 changes: 15 additions & 0 deletions examples/cdp_mode/raw_facebook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
url = "https://www.facebook.com/SeleniumBase"
sb.activate_cdp_mode(url)
sb.sleep(1)
sb.click_if_visible('[aria-label="Close"] i')
sb.sleep(1)
for i in range(14):
sb.cdp.scroll_down(15)
print(sb.get_page_title())
sb.save_as_pdf_to_logs()
sb.save_page_source_to_logs()
sb.save_screenshot_to_logs()
print("Logs have been saved to: ./latest_logs/")
8 changes: 8 additions & 0 deletions examples/cdp_mode/raw_mycdp_cookies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import mycdp
from seleniumbase import SB

with SB(uc=True, test=True) as sb:
sb.activate_cdp_mode("https://learn.microsoft.com/en-us/")
tab = sb.cdp.get_active_tab()
loop = sb.cdp.get_event_loop()
print(loop.run_until_complete(tab.send(mycdp.storage.get_cookies())))
9 changes: 9 additions & 0 deletions examples/cdp_mode/raw_reuse_browser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""Test connecting to an existing browser."""
from seleniumbase import sb_cdp

sb1 = sb_cdp.Chrome("https://example.com")
port = sb1.get_rd_port()
sb2 = sb_cdp.Chrome(host="127.0.0.1", port=port)
print("The remote-debugging port: %s" % port)
assert sb1.get_rd_port() == sb2.get_rd_port()
assert sb1.get_current_url() == sb2.get_current_url()
1 change: 1 addition & 0 deletions help_docs/cdp_mode_methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ sb.cdp.get_rd_port() # Returns the remote-debugging port
sb.cdp.get_rd_url() # Returns the remote-debugging URL
sb.cdp.get_endpoint_url() # Same as sb.cdp.get_rd_url()
sb.cdp.get_port() # Same as sb.cdp.get_rd_port()
sb.cdp.get_websocket_url() # Returns the websocket URL
sb.cdp.add_handler(event, handler)
sb.cdp.find_element(selector, best_match=False, timeout=None)
sb.cdp.find(selector, best_match=False, timeout=None)
Expand Down
3 changes: 2 additions & 1 deletion mkdocs_build/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# mkdocs dependencies for generating the seleniumbase.io website
# Minimum Python version: 3.10 (for generating docs only)

regex>=2026.2.19
regex>=2026.2.28
pymdown-extensions>=10.21
pipdeptree>=2.31.0
python-dateutil>=2.8.2
Expand All @@ -17,4 +17,5 @@ mkdocs==1.6.1
mkdocs-material==9.6.23
mkdocs-exclude-search==0.6.6
mkdocs-simple-hooks==0.1.5
mkdocs-get-deps==0.2.0
mkdocs-material-extensions==1.3.1
10 changes: 5 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
pip>=26.0.1
packaging>=26.0
setuptools~=70.2;python_version<"3.10"
setuptools>=82.0.0;python_version>="3.10"
setuptools>=82.0.1;python_version>="3.10"
wheel>=0.46.3
attrs>=25.4.0
certifi>=2026.2.25
exceptiongroup>=1.3.1
websockets~=15.0.1;python_version<"3.10"
websockets>=16.0;python_version>="3.10"
filelock~=3.19.1;python_version<"3.10"
filelock>=3.24.3;python_version>="3.10"
filelock>=3.25.1;python_version>="3.10"
fasteners>=0.20
mycdp>=1.3.2
mycdp>=1.3.3
pynose>=1.5.5
platformdirs~=4.4.0;python_version<"3.10"
platformdirs>=4.9.2;python_version>="3.10"
platformdirs>=4.9.4;python_version>="3.10"
typing-extensions>=4.15.0
sbvirtualdisplay>=1.4.0
MarkupSafe>=3.0.3
Expand All @@ -29,7 +29,7 @@ pyreadline3>=3.5.4;platform_system=="Windows"
tabcompleter>=1.4.0
pdbp>=1.8.2
idna>=3.11
charset-normalizer>=3.4.4,<4
charset-normalizer>=3.4.5,<4
urllib3>=1.26.20,<2;python_version<"3.10"
urllib3>=1.26.20,<3;python_version>="3.10"
requests~=2.32.5
Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/__version__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# seleniumbase package
__version__ = "4.47.1"
__version__ = "4.47.2"
1 change: 1 addition & 0 deletions seleniumbase/core/browser_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,7 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
cdp.get_rd_port = CDPM.get_rd_port
cdp.get_rd_url = CDPM.get_rd_url
cdp.get_endpoint_url = CDPM.get_endpoint_url
cdp.get_websocket_url = CDPM.get_websocket_url
cdp.get_port = CDPM.get_port
cdp.find_element = CDPM.find_element
cdp.find = CDPM.find_element
Expand Down
13 changes: 0 additions & 13 deletions seleniumbase/core/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ class DatabaseManager:
def __init__(self, database_env="test", conf_creds=None):
"""Create a connection to the MySQL DB."""
import fasteners
import sys
import time
from seleniumbase import config as sb_config
from seleniumbase.config import settings
Expand All @@ -19,18 +18,6 @@ def __init__(self, database_env="test", conf_creds=None):
constants.PipInstall.FINDLOCK
)
with pip_find_lock:
if sys.version_info < (3, 9):
# Fix bug with newer cryptography on Python 3.8:
# "pyo3_runtime.PanicException: Python API call failed"
# (Match the version needed for pdfminer.six functions)
try:
import cryptography
if cryptography.__version__ != "39.0.2":
shared_utils.pip_install(
"cryptography", version="39.0.2"
)
except Exception:
shared_utils.pip_install("cryptography", version="39.0.2")
try:
import cryptography # noqa: F401
import pymysql
Expand Down
5 changes: 5 additions & 0 deletions seleniumbase/core/sb_cdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ def get_port(self):
"""Same as get_rd_port(), which returns the remote-debugging port."""
return self.get_rd_port()

def get_websocket_url(self):
"""Returns the websocket URL of the active tab.
The websocket URL starts with `ws://`."""
return self.get_active_tab().websocket_url

def add_handler(self, event, handler):
self.page.add_handler(event, handler)

Expand Down
35 changes: 2 additions & 33 deletions seleniumbase/fixtures/base_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -14732,36 +14732,10 @@ def __get_shadow_element(
is_present = False
for selector_part in selectors[1:]:
shadow_root = None
if (
(self.is_chromium() or self.browser == "firefox")
and int(self.__get_major_browser_version()) >= 96
):
if self.is_chromium() or self.browser == "firefox":
try:
shadow_root = element.shadow_root
except Exception:
if self.is_chromium():
chrome_dict = self.driver.capabilities["chrome"]
chrome_dr_version = chrome_dict["chromedriverVersion"]
chromedriver_version = chrome_dr_version.split(" ")[0]
major_c_dr_version = chromedriver_version.split(".")[0]
if int(major_c_dr_version) < 96:
upgrade_to = "latest"
major_browser_version = (
self.__get_major_browser_version()
)
if int(major_browser_version) >= 96:
upgrade_to = str(major_browser_version)
message = (
"You need to upgrade to a newer\n"
"version of chromedriver to interact\n"
"with Shadow root elements!\n"
"(Current driver version is: %s)"
"\n(Minimum driver version is: 96.*)"
"\nTo upgrade, run this:"
'\n"seleniumbase get chromedriver %s"'
% (chromedriver_version, upgrade_to)
)
raise Exception(message)
if timeout != 0.1: # Skip wait for special 0.1 (See above)
time.sleep(2)
try:
Expand All @@ -14770,12 +14744,7 @@ def __get_shadow_element(
raise Exception(
"Element {%s} has no shadow root!" % selector_chain
)
else: # This part won't work on Chrome 96 or newer.
# If using Chrome 96 or newer (and on an old Python version),
# you'll need to upgrade in order to access Shadow roots.
# Firefox users will likely hit:
# https://github.com/mozilla/geckodriver/issues/1711
# When Firefox adds support, switch to element.shadow_root
else:
try:
shadow_root = self.execute_script(
"return arguments[0].shadowRoot;", element
Expand Down
1 change: 1 addition & 0 deletions seleniumbase/undetected/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ def __init__(
options.add_argument("--remote-debugging-host=%s" % debug_host)
options.add_argument("--remote-debugging-port=%s" % debug_port)
if user_data_dir:
user_data_dir = os.path.abspath(user_data_dir)
options.add_argument("--user-data-dir=%s" % user_data_dir)
language, keep_user_data_dir = None, bool(user_data_dir)
# See if a custom user profile is specified in options
Expand Down
4 changes: 4 additions & 0 deletions seleniumbase/undetected/cdp_driver/cdp_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,8 @@ async def start(
platform_var = platform_var[1:-1]
if IS_LINUX and not headless and not headed and not xvfb:
xvfb = True # The default setting on Linux
if port and not host:
host = "127.0.0.1" # Assume localhost
if not host or not port:
# The browser hasn't been launched yet. (May need a virtual display)
__activate_virtual_display_as_needed(
Expand Down Expand Up @@ -611,6 +613,8 @@ async def start(
elif udd_string.startswith("'") and udd_string.endswith("'"):
udd_string = udd_string[1:-1]
user_data_dir = udd_string
if user_data_dir:
user_data_dir = os.path.abspath(user_data_dir)
if not browser_executable_path:
browser = None
if "browser" in kwargs:
Expand Down
30 changes: 0 additions & 30 deletions seleniumbase/undetected/cdp_driver/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,31 +133,6 @@ def __repr__(self):
return fmt


class EventTransaction(Transaction):
event = None
value = None

def __init__(self, event_object):
try:
super().__init__(None)
except BaseException:
pass
self.set_result(event_object)
self.event = self.value = self.result()

def __repr__(self):
status = "finished"
success = False if self.exception() else True
event_object = self.result()
fmt = (
f"{self.__class__.__name__}\n\t"
f"event: {event_object.__class__.__module__}.{event_object.__class__.__name__}\n\t" # noqa
f"status: {status}\n\t"
f"success: {success}>"
)
return fmt


class CantTouchThis(type):
def __setattr__(cls, attr, value):
""":meta private:"""
Expand Down Expand Up @@ -619,11 +594,6 @@ async def listener_loop(self):
# Probably an event
try:
event = cdp.util.parse_json_event(message)
# event_tx = EventTransaction(event)
# if not self.connection.mapper:
# self.connection.__count__ = itertools.count(0)
# event_tx.id = next(self.connection.__count__)
# self.connection.mapper[event_tx.id] = event_tx
except Exception as e:
logger.info(
"%s: %s during parsing of json from event : %s"
Expand Down
10 changes: 5 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,20 +149,20 @@
'pip>=26.0.1',
'packaging>=26.0',
'setuptools~=70.2;python_version<"3.10"', # Newer ones had issues
'setuptools>=82.0.0;python_version>="3.10"',
'setuptools>=82.0.1;python_version>="3.10"',
'wheel>=0.46.3',
'attrs>=25.4.0',
'certifi>=2026.2.25',
'exceptiongroup>=1.3.1',
'websockets~=15.0.1;python_version<"3.10"',
'websockets>=16.0;python_version>="3.10"',
'filelock~=3.19.1;python_version<"3.10"',
'filelock>=3.24.3;python_version>="3.10"',
'filelock>=3.25.1;python_version>="3.10"',
'fasteners>=0.20',
'mycdp>=1.3.2',
'mycdp>=1.3.3',
'pynose>=1.5.5',
'platformdirs~=4.4.0;python_version<"3.10"',
'platformdirs>=4.9.2;python_version>="3.10"',
'platformdirs>=4.9.4;python_version>="3.10"',
'typing-extensions>=4.15.0',
'sbvirtualdisplay>=1.4.0',
'MarkupSafe>=3.0.3',
Expand All @@ -177,7 +177,7 @@
'tabcompleter>=1.4.0',
'pdbp>=1.8.2',
'idna>=3.11',
'charset-normalizer>=3.4.4,<4',
'charset-normalizer>=3.4.5,<4',
'urllib3>=1.26.20,<2;python_version<"3.10"',
'urllib3>=1.26.20,<3;python_version>="3.10"',
'requests~=2.32.5',
Expand Down