seleniumbase 4.34.15__py3-none-any.whl → 4.34.16__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
1
  # seleniumbase package
2
- __version__ = "4.34.15"
2
+ __version__ = "4.34.16"
@@ -718,6 +718,10 @@ def uc_open_with_cdp_mode(driver, url=None):
718
718
  cdp.is_selected = CDPM.is_selected
719
719
  cdp.is_element_present = CDPM.is_element_present
720
720
  cdp.is_element_visible = CDPM.is_element_visible
721
+ cdp.is_text_visible = CDPM.is_text_visible
722
+ cdp.is_exact_text_visible = CDPM.is_exact_text_visible
723
+ cdp.wait_for_text = CDPM.wait_for_text
724
+ cdp.wait_for_text_not_visible = CDPM.wait_for_text_not_visible
721
725
  cdp.wait_for_element_visible = CDPM.wait_for_element_visible
722
726
  cdp.assert_element = CDPM.assert_element
723
727
  cdp.assert_element_visible = CDPM.assert_element_visible
@@ -731,6 +735,7 @@ def uc_open_with_cdp_mode(driver, url=None):
731
735
  cdp.assert_url_contains = CDPM.assert_url_contains
732
736
  cdp.assert_text = CDPM.assert_text
733
737
  cdp.assert_exact_text = CDPM.assert_exact_text
738
+ cdp.assert_text_not_visible = CDPM.assert_text_not_visible
734
739
  cdp.assert_true = CDPM.assert_true
735
740
  cdp.assert_false = CDPM.assert_false
736
741
  cdp.assert_equal = CDPM.assert_equal
@@ -2280,6 +2285,7 @@ def _set_chrome_options(
2280
2285
  or proxy_string
2281
2286
  ):
2282
2287
  chrome_options.add_argument("--ignore-certificate-errors")
2288
+ chrome_options.add_argument("--ignore-ssl-errors=yes")
2283
2289
  if not enable_ws:
2284
2290
  chrome_options.add_argument("--disable-web-security")
2285
2291
  if (
@@ -4231,6 +4237,7 @@ def get_local_driver(
4231
4237
  edge_options.add_argument("--log-level=3")
4232
4238
  edge_options.add_argument("--no-first-run")
4233
4239
  edge_options.add_argument("--ignore-certificate-errors")
4240
+ edge_options.add_argument("--ignore-ssl-errors=yes")
4234
4241
  if devtools and not headless:
4235
4242
  edge_options.add_argument("--auto-open-devtools-for-tabs")
4236
4243
  edge_options.add_argument("--allow-file-access-from-files")
@@ -62,6 +62,7 @@ class CDPMethods():
62
62
  lambda destination: self.__mouse_drag(element, destination)
63
63
  )
64
64
  element.mouse_move = lambda: self.__mouse_move(element)
65
+ element.press_keys = lambda text: self.__press_keys(element, text)
65
66
  element.query_selector = (
66
67
  lambda selector: self.__query_selector(element, selector)
67
68
  )
@@ -211,7 +212,8 @@ class CDPMethods():
211
212
  element = self.__add_sync_methods(element.parent)
212
213
  return self.__add_sync_methods(element)
213
214
  elif (
214
- element.parent.parent
215
+ element.parent
216
+ and element.parent.parent
215
217
  and tag_name in element.parent.parent.tag_name.lower()
216
218
  and text.strip() in element.parent.parent.text
217
219
  ):
@@ -272,7 +274,8 @@ class CDPMethods():
272
274
  if element not in updated_elements:
273
275
  updated_elements.append(element)
274
276
  elif (
275
- element.parent.parent
277
+ element.parent
278
+ and element.parent.parent
276
279
  and tag_name in element.parent.parent.tag_name.lower()
277
280
  and text.strip() in element.parent.parent.text
278
281
  ):
@@ -445,6 +448,23 @@ class CDPMethods():
445
448
  self.loop.run_until_complete(element.mouse_move_async())
446
449
  )
447
450
 
451
+ def __press_keys(self, element, text):
452
+ element.scroll_into_view()
453
+ submit = False
454
+ if text.endswith("\n") or text.endswith("\r"):
455
+ submit = True
456
+ text = text[:-1]
457
+ for key in text:
458
+ element.send_keys(key)
459
+ time.sleep(0.044)
460
+ if submit:
461
+ element.send_keys("\r\n")
462
+ time.sleep(0.044)
463
+ self.__slow_mode_pause_if_set()
464
+ return (
465
+ self.loop.run_until_complete(self.page.wait())
466
+ )
467
+
448
468
  def __query_selector(self, element, selector):
449
469
  selector = self.__convert_to_css_if_xpath(selector)
450
470
  element2 = self.loop.run_until_complete(
@@ -1681,21 +1701,79 @@ class CDPMethods():
1681
1701
  return True
1682
1702
  return False
1683
1703
 
1684
- def wait_for_element_visible(self, selector, timeout=None):
1704
+ def is_text_visible(self, text, selector="body"):
1705
+ selector = self.__convert_to_css_if_xpath(selector)
1706
+ text = text.strip()
1707
+ element = None
1708
+ try:
1709
+ element = self.find_element(selector, timeout=0.1)
1710
+ except Exception:
1711
+ return False
1712
+ with suppress(Exception):
1713
+ if text in element.text_all:
1714
+ return True
1715
+ return False
1716
+
1717
+ def is_exact_text_visible(self, text, selector="body"):
1718
+ selector = self.__convert_to_css_if_xpath(selector)
1719
+ text = text.strip()
1720
+ element = None
1721
+ try:
1722
+ element = self.find_element(selector, timeout=0.1)
1723
+ except Exception:
1724
+ return False
1725
+ with suppress(Exception):
1726
+ if text == element.text_all.strip():
1727
+ return True
1728
+ return False
1729
+
1730
+ def wait_for_text(self, text, selector="body", timeout=None):
1685
1731
  if not timeout:
1686
1732
  timeout = settings.SMALL_TIMEOUT
1733
+ start_ms = time.time() * 1000.0
1734
+ stop_ms = start_ms + (timeout * 1000.0)
1735
+ text = text.strip()
1736
+ element = None
1687
1737
  try:
1688
- self.select(selector, timeout=timeout)
1738
+ element = self.find_element(selector, timeout=timeout)
1689
1739
  except Exception:
1690
- raise Exception("Element {%s} was not found!" % selector)
1691
- for i in range(30):
1692
- if self.is_element_visible(selector):
1693
- return self.select(selector)
1740
+ raise Exception("Element {%s} not found!" % selector)
1741
+ for i in range(int(timeout * 10)):
1742
+ with suppress(Exception):
1743
+ element = self.find_element(selector, timeout=0.1)
1744
+ if text in element.text_all:
1745
+ return True
1746
+ now_ms = time.time() * 1000.0
1747
+ if now_ms >= stop_ms:
1748
+ break
1694
1749
  time.sleep(0.1)
1695
- raise Exception("Element {%s} was not visible!" % selector)
1750
+ raise Exception(
1751
+ "Text {%s} not found in {%s}! Actual text: {%s}"
1752
+ % (text, selector, element.text_all)
1753
+ )
1696
1754
 
1697
- def assert_element(self, selector, timeout=None):
1698
- """Same as assert_element_visible()"""
1755
+ def wait_for_text_not_visible(self, text, selector="body", timeout=None):
1756
+ if not timeout:
1757
+ timeout = settings.SMALL_TIMEOUT
1758
+ text = text.strip()
1759
+ start_ms = time.time() * 1000.0
1760
+ stop_ms = start_ms + (timeout * 1000.0)
1761
+ for i in range(int(timeout * 10)):
1762
+ if not self.is_text_visible(text, selector):
1763
+ return True
1764
+ now_ms = time.time() * 1000.0
1765
+ if now_ms >= stop_ms:
1766
+ break
1767
+ time.sleep(0.1)
1768
+ plural = "s"
1769
+ if timeout == 1:
1770
+ plural = ""
1771
+ raise Exception(
1772
+ "Text {%s} in {%s} was still visible after %s second%s!"
1773
+ % (text, selector, timeout, plural)
1774
+ )
1775
+
1776
+ def wait_for_element_visible(self, selector, timeout=None):
1699
1777
  if not timeout:
1700
1778
  timeout = settings.SMALL_TIMEOUT
1701
1779
  try:
@@ -1704,10 +1782,15 @@ class CDPMethods():
1704
1782
  raise Exception("Element {%s} was not found!" % selector)
1705
1783
  for i in range(30):
1706
1784
  if self.is_element_visible(selector):
1707
- return True
1785
+ return self.select(selector)
1708
1786
  time.sleep(0.1)
1709
1787
  raise Exception("Element {%s} was not visible!" % selector)
1710
1788
 
1789
+ def assert_element(self, selector, timeout=None):
1790
+ """Same as assert_element_visible()"""
1791
+ self.assert_element_visible(selector, timeout=timeout)
1792
+ return True
1793
+
1711
1794
  def assert_element_visible(self, selector, timeout=None):
1712
1795
  """Same as assert_element()"""
1713
1796
  if not timeout:
@@ -1852,29 +1935,9 @@ class CDPMethods():
1852
1935
  raise Exception(error % (expected, actual))
1853
1936
 
1854
1937
  def assert_text(self, text, selector="body", timeout=None):
1855
- if not timeout:
1856
- timeout = settings.SMALL_TIMEOUT
1857
- start_ms = time.time() * 1000.0
1858
- stop_ms = start_ms + (timeout * 1000.0)
1859
- text = text.strip()
1860
- element = None
1861
- try:
1862
- element = self.find_element(selector, timeout=timeout)
1863
- except Exception:
1864
- raise Exception("Element {%s} not found!" % selector)
1865
- for i in range(int(timeout * 10)):
1866
- with suppress(Exception):
1867
- element = self.find_element(selector, timeout=0.1)
1868
- if text in element.text_all:
1869
- return True
1870
- now_ms = time.time() * 1000.0
1871
- if now_ms >= stop_ms:
1872
- break
1873
- time.sleep(0.1)
1874
- raise Exception(
1875
- "Text {%s} not found in {%s}! Actual text: {%s}"
1876
- % (text, selector, element.text_all)
1877
- )
1938
+ """Same as wait_for_text()"""
1939
+ self.wait_for_text(text, selector=selector, timeout=timeout)
1940
+ return True
1878
1941
 
1879
1942
  def assert_exact_text(self, text, selector="body", timeout=None):
1880
1943
  if not timeout:
@@ -1904,6 +1967,13 @@ class CDPMethods():
1904
1967
  % (text, element.text_all, selector)
1905
1968
  )
1906
1969
 
1970
+ def assert_text_not_visible(self, text, selector="body", timeout=None):
1971
+ """Raises an exception if the text is still visible after timeout."""
1972
+ self.wait_for_text_not_visible(
1973
+ text, selector=selector, timeout=timeout
1974
+ )
1975
+ return True
1976
+
1907
1977
  def assert_true(self, expression):
1908
1978
  if not expression:
1909
1979
  raise AssertionError("%s is not true" % expression)
@@ -1454,6 +1454,8 @@ class BaseCase(unittest.TestCase):
1454
1454
 
1455
1455
  def is_text_visible(self, text, selector="body", by="css selector"):
1456
1456
  """Returns whether the text substring is visible in the element."""
1457
+ if self.__is_cdp_swap_needed():
1458
+ return self.cdp.is_text_visible(text, selector)
1457
1459
  self.wait_for_ready_state_complete()
1458
1460
  time.sleep(0.01)
1459
1461
  selector, by = self.__recalculate_selector(selector, by)
@@ -1464,6 +1466,8 @@ class BaseCase(unittest.TestCase):
1464
1466
  def is_exact_text_visible(self, text, selector="body", by="css selector"):
1465
1467
  """Returns whether the text is exactly equal to the element text.
1466
1468
  (Leading and trailing whitespace is ignored in the verification.)"""
1469
+ if self.__is_cdp_swap_needed():
1470
+ return self.cdp.is_exact_text_visible(text, selector)
1467
1471
  self.wait_for_ready_state_complete()
1468
1472
  time.sleep(0.01)
1469
1473
  selector, by = self.__recalculate_selector(selector, by)
@@ -9281,7 +9285,8 @@ class BaseCase(unittest.TestCase):
9281
9285
  "bottom_left", "bottom_center", "bottom_right"]
9282
9286
  max_messages: The limit of concurrent messages to display."""
9283
9287
  self.__check_scope()
9284
- self._check_browser()
9288
+ if not self.__is_cdp_swap_needed():
9289
+ self._check_browser()
9285
9290
  if not theme:
9286
9291
  theme = "default" # "flat"
9287
9292
  if not location:
@@ -9308,7 +9313,8 @@ class BaseCase(unittest.TestCase):
9308
9313
  You can also post messages by using =>
9309
9314
  self.execute_script('Messenger().post("My Message")') """
9310
9315
  self.__check_scope()
9311
- self._check_browser()
9316
+ if not self.__is_cdp_swap_needed():
9317
+ self._check_browser()
9312
9318
  if style not in ["info", "success", "error"]:
9313
9319
  style = "info"
9314
9320
  if not duration:
@@ -10326,6 +10332,10 @@ class BaseCase(unittest.TestCase):
10326
10332
  if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
10327
10333
  timeout = self.__get_new_timeout(timeout)
10328
10334
  selector, by = self.__recalculate_selector(selector, by)
10335
+ if self.__is_cdp_swap_needed():
10336
+ return self.cdp.wait_for_text(
10337
+ text, selector=selector, timeout=timeout
10338
+ )
10329
10339
  return page_actions.wait_for_text_not_visible(
10330
10340
  self.driver, text, selector, by, timeout
10331
10341
  )
@@ -13909,7 +13919,8 @@ class BaseCase(unittest.TestCase):
13909
13919
  js_utils.highlight_element_with_js(self.driver, element, loops, o_bs)
13910
13920
 
13911
13921
  def __highlight_with_jquery(self, selector, loops, o_bs):
13912
- self.wait_for_ready_state_complete()
13922
+ if not self.__is_cdp_swap_needed():
13923
+ self.wait_for_ready_state_complete()
13913
13924
  js_utils.highlight_with_jquery(self.driver, selector, loops, o_bs)
13914
13925
 
13915
13926
  def __highlight_with_js_2(self, message, selector, o_bs):
@@ -585,52 +585,60 @@ def highlight_with_jquery(driver, selector, loops=4, o_bs=""):
585
585
  '0px 0px 6px 6px rgba(128, 128, 128, 0.5)');"""
586
586
  % selector
587
587
  )
588
- safe_execute_script(driver, script)
588
+ with suppress(Exception):
589
+ safe_execute_script(driver, script)
589
590
  for n in range(loops):
590
591
  script = (
591
592
  """jQuery('%s').css('box-shadow',
592
593
  '0px 0px 6px 6px rgba(255, 0, 0, 1)');"""
593
594
  % selector
594
595
  )
595
- execute_script(driver, script)
596
+ with suppress(Exception):
597
+ execute_script(driver, script)
596
598
  time.sleep(0.0181)
597
599
  script = (
598
600
  """jQuery('%s').css('box-shadow',
599
601
  '0px 0px 6px 6px rgba(128, 0, 128, 1)');"""
600
602
  % selector
601
603
  )
602
- execute_script(driver, script)
604
+ with suppress(Exception):
605
+ execute_script(driver, script)
603
606
  time.sleep(0.0181)
604
607
  script = (
605
608
  """jQuery('%s').css('box-shadow',
606
609
  '0px 0px 6px 6px rgba(0, 0, 255, 1)');"""
607
610
  % selector
608
611
  )
609
- execute_script(driver, script)
612
+ with suppress(Exception):
613
+ execute_script(driver, script)
610
614
  time.sleep(0.0181)
611
615
  script = (
612
616
  """jQuery('%s').css('box-shadow',
613
617
  '0px 0px 6px 6px rgba(0, 255, 0, 1)');"""
614
618
  % selector
615
619
  )
616
- execute_script(driver, script)
620
+ with suppress(Exception):
621
+ execute_script(driver, script)
617
622
  time.sleep(0.0181)
618
623
  script = (
619
624
  """jQuery('%s').css('box-shadow',
620
625
  '0px 0px 6px 6px rgba(128, 128, 0, 1)');"""
621
626
  % selector
622
627
  )
623
- execute_script(driver, script)
628
+ with suppress(Exception):
629
+ execute_script(driver, script)
624
630
  time.sleep(0.0181)
625
631
  script = (
626
632
  """jQuery('%s').css('box-shadow',
627
633
  '0px 0px 6px 6px rgba(128, 0, 128, 1)');"""
628
634
  % selector
629
635
  )
630
- execute_script(driver, script)
636
+ with suppress(Exception):
637
+ execute_script(driver, script)
631
638
  time.sleep(0.0181)
632
639
  script = """jQuery('%s').css('box-shadow', '%s');""" % (selector, o_bs)
633
- execute_script(driver, script)
640
+ with suppress(Exception):
641
+ execute_script(driver, script)
634
642
 
635
643
 
636
644
  def add_css_link(driver, css_link):
@@ -924,9 +932,20 @@ def post_message(driver, message, msg_dur=None, style="info"):
924
932
  """hideAfter: %s, hideOnNavigate: true});"""
925
933
  % (message, style, msg_dur)
926
934
  )
935
+ retry = False
927
936
  try:
928
937
  execute_script(driver, messenger_script)
938
+ except TypeError as e:
939
+ if (
940
+ shared_utils.is_cdp_swap_needed(driver)
941
+ and "cannot unpack non-iterable" in str(e)
942
+ ):
943
+ pass
944
+ else:
945
+ retry = True
929
946
  except Exception:
947
+ retry = True
948
+ if retry:
930
949
  activate_messenger(driver)
931
950
  set_messenger_theme(driver)
932
951
  try:
@@ -1273,7 +1292,10 @@ def slow_scroll_to_element(driver, element, *args, **kwargs):
1273
1292
  scroll_position = execute_script(driver, "return window.scrollY;")
1274
1293
  element_location_y = None
1275
1294
  try:
1276
- element_location_y = element.location["y"]
1295
+ if shared_utils.is_cdp_swap_needed(driver):
1296
+ element.get_position().y
1297
+ else:
1298
+ element_location_y = element.location["y"]
1277
1299
  except Exception:
1278
1300
  element.location_once_scrolled_into_view
1279
1301
  return
@@ -265,6 +265,8 @@ class Browser:
265
265
  :param new_window: Open new window
266
266
  :return: Page
267
267
  """
268
+ if url and ":" not in url:
269
+ url = "https://" + url
268
270
  if new_tab or new_window:
269
271
  # Create new target using the browser session.
270
272
  target_id = await self.connection.send(
@@ -4,6 +4,7 @@ import asyncio
4
4
  import fasteners
5
5
  import logging
6
6
  import os
7
+ import sys
7
8
  import time
8
9
  import types
9
10
  import typing
@@ -11,6 +12,7 @@ from contextlib import suppress
11
12
  from seleniumbase import config as sb_config
12
13
  from seleniumbase.config import settings
13
14
  from seleniumbase.core import detect_b_ver
15
+ from seleniumbase.core import proxy_helper
14
16
  from seleniumbase.fixtures import constants
15
17
  from seleniumbase.fixtures import shared_utils
16
18
  from typing import Optional, List, Union, Callable
@@ -23,6 +25,7 @@ import mycdp as cdp
23
25
 
24
26
  logger = logging.getLogger(__name__)
25
27
  IS_LINUX = shared_utils.is_linux()
28
+ PROXY_DIR_LOCK = proxy_helper.PROXY_DIR_LOCK
26
29
  T = typing.TypeVar("T")
27
30
 
28
31
 
@@ -139,6 +142,85 @@ def __activate_virtual_display_as_needed(
139
142
  __activate_standard_virtual_display()
140
143
 
141
144
 
145
+ def __set_proxy_filenames():
146
+ DOWNLOADS_DIR = constants.Files.DOWNLOADS_FOLDER
147
+ for num in range(1000):
148
+ PROXY_DIR_PATH = os.path.join(DOWNLOADS_DIR, "proxy_ext_dir_%s" % num)
149
+ if os.path.exists(PROXY_DIR_PATH):
150
+ continue
151
+ proxy_helper.PROXY_DIR_PATH = PROXY_DIR_PATH
152
+ return
153
+ # Exceeded upper bound. Use Defaults:
154
+ PROXY_DIR_PATH = os.path.join(DOWNLOADS_DIR, "proxy_ext_dir")
155
+ proxy_helper.PROXY_DIR_PATH = PROXY_DIR_PATH
156
+
157
+
158
+ def __add_chrome_ext_dir(extension_dir, dir_path):
159
+ # Add dir_path to the existing extension_dir
160
+ option_exists = False
161
+ if extension_dir:
162
+ option_exists = True
163
+ extension_dir = "%s,%s" % (
164
+ extension_dir, os.path.realpath(dir_path)
165
+ )
166
+ if not option_exists:
167
+ extension_dir = os.path.realpath(dir_path)
168
+ return extension_dir
169
+
170
+
171
+ def __add_chrome_proxy_extension(
172
+ extension_dir,
173
+ proxy_string,
174
+ proxy_user,
175
+ proxy_pass,
176
+ proxy_bypass_list=None,
177
+ multi_proxy=False,
178
+ ):
179
+ """Implementation of https://stackoverflow.com/a/35293284/7058266
180
+ for https://stackoverflow.com/q/12848327/7058266
181
+ (Run Selenium on a proxy server that requires authentication.)"""
182
+ args = " ".join(sys.argv)
183
+ bypass_list = proxy_bypass_list
184
+ if (
185
+ not ("-n" in sys.argv or " -n=" in args or args == "-c")
186
+ and not multi_proxy
187
+ ):
188
+ # Single-threaded
189
+ proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
190
+ with proxy_dir_lock:
191
+ proxy_helper.create_proxy_ext(
192
+ proxy_string,
193
+ proxy_user,
194
+ proxy_pass,
195
+ bypass_list,
196
+ zip_it=False,
197
+ )
198
+ proxy_dir_path = proxy_helper.PROXY_DIR_PATH
199
+ extension_dir = __add_chrome_ext_dir(
200
+ extension_dir, proxy_dir_path
201
+ )
202
+ else:
203
+ # Multi-threaded
204
+ proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
205
+ with proxy_dir_lock:
206
+ with suppress(Exception):
207
+ shared_utils.make_writable(PROXY_DIR_LOCK)
208
+ if multi_proxy:
209
+ __set_proxy_filenames()
210
+ if not os.path.exists(proxy_helper.PROXY_DIR_PATH):
211
+ proxy_helper.create_proxy_ext(
212
+ proxy_string,
213
+ proxy_user,
214
+ proxy_pass,
215
+ bypass_list,
216
+ zip_it=False,
217
+ )
218
+ extension_dir = __add_chrome_ext_dir(
219
+ extension_dir, proxy_helper.PROXY_DIR_PATH
220
+ )
221
+ return extension_dir
222
+
223
+
142
224
  async def start(
143
225
  config: Optional[Config] = None,
144
226
  *,
@@ -156,6 +238,8 @@ async def start(
156
238
  xvfb: Optional[int] = None, # Use a special virtual display on Linux
157
239
  headed: Optional[bool] = None, # Override default Xvfb mode on Linux
158
240
  expert: Optional[bool] = None, # Open up closed Shadow-root elements
241
+ proxy: Optional[str] = None, # "host:port" or "user:pass@host:port"
242
+ extension_dir: Optional[str] = None, # Chrome extension directory
159
243
  **kwargs: Optional[dict],
160
244
  ) -> Browser:
161
245
  """
@@ -200,6 +284,18 @@ async def start(
200
284
  if IS_LINUX and not headless and not headed and not xvfb:
201
285
  xvfb = True # The default setting on Linux
202
286
  __activate_virtual_display_as_needed(headless, headed, xvfb, xvfb_metrics)
287
+ if proxy and "@" in str(proxy):
288
+ user_with_pass = proxy.split("@")[0]
289
+ if ":" in user_with_pass:
290
+ proxy_user = user_with_pass.split(":")[0]
291
+ proxy_pass = user_with_pass.split(":")[1]
292
+ proxy_string = proxy.split("@")[1]
293
+ extension_dir = __add_chrome_proxy_extension(
294
+ extension_dir,
295
+ proxy_string,
296
+ proxy_user,
297
+ proxy_pass,
298
+ )
203
299
  if not config:
204
300
  config = Config(
205
301
  user_data_dir,
@@ -213,13 +309,19 @@ async def start(
213
309
  host=host,
214
310
  port=port,
215
311
  expert=expert,
312
+ proxy=proxy,
313
+ extension_dir=extension_dir,
216
314
  **kwargs,
217
315
  )
316
+ driver = None
218
317
  try:
219
- return await Browser.create(config)
318
+ driver = await Browser.create(config)
220
319
  except Exception:
221
320
  time.sleep(0.15)
222
- return await Browser.create(config)
321
+ driver = await Browser.create(config)
322
+ if proxy and "@" in str(proxy):
323
+ time.sleep(0.11)
324
+ return driver
223
325
 
224
326
 
225
327
  async def start_async(*args, **kwargs) -> Browser:
@@ -40,6 +40,8 @@ class Config:
40
40
  host: str = AUTO,
41
41
  port: int = AUTO,
42
42
  expert: bool = AUTO,
43
+ proxy: Optional[str] = None,
44
+ extension_dir: Optional[str] = None,
43
45
  **kwargs: dict,
44
46
  ):
45
47
  """
@@ -91,6 +93,8 @@ class Config:
91
93
  self.host = host
92
94
  self.port = port
93
95
  self.expert = expert
96
+ self.proxy = proxy
97
+ self.extension_dir = extension_dir
94
98
  self._extensions = []
95
99
  # When using posix-ish operating system and running as root,
96
100
  # you must use no_sandbox=True
@@ -195,6 +199,12 @@ class Config:
195
199
  "--disable-web-security",
196
200
  "--disable-site-isolation-trials",
197
201
  ]
202
+ if self.proxy:
203
+ args.append("--proxy-server=%s" % self.proxy.split("@")[-1])
204
+ args.append("--ignore-certificate-errors")
205
+ args.append("--ignore-ssl-errors=yes")
206
+ if self.extension_dir:
207
+ args.append("--load-extension=%s" % self.extension_dir)
198
208
  if self._browser_args:
199
209
  args.extend([arg for arg in self._browser_args if arg not in args])
200
210
  if self.headless:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: seleniumbase
3
- Version: 4.34.15
3
+ Version: 4.34.16
4
4
  Summary: A complete web automation framework for end-to-end testing.
5
5
  Home-page: https://github.com/seleniumbase/SeleniumBase
6
6
  Author: Michael Mintz
@@ -68,7 +68,7 @@ Requires-Dist: attrs>=25.1.0
68
68
  Requires-Dist: certifi>=2025.1.31
69
69
  Requires-Dist: exceptiongroup>=1.2.2
70
70
  Requires-Dist: websockets~=13.1; python_version < "3.9"
71
- Requires-Dist: websockets>=14.2; python_version >= "3.9"
71
+ Requires-Dist: websockets>=15.0; python_version >= "3.9"
72
72
  Requires-Dist: filelock~=3.16.1; python_version < "3.9"
73
73
  Requires-Dist: filelock>=3.17.0; python_version >= "3.9"
74
74
  Requires-Dist: fasteners>=0.19
@@ -99,8 +99,8 @@ Requires-Dist: sniffio==1.3.1
99
99
  Requires-Dist: h11==0.14.0
100
100
  Requires-Dist: outcome==1.3.0.post0
101
101
  Requires-Dist: trio==0.27.0; python_version < "3.9"
102
- Requires-Dist: trio==0.28.0; python_version >= "3.9"
103
- Requires-Dist: trio-websocket==0.11.1
102
+ Requires-Dist: trio==0.29.0; python_version >= "3.9"
103
+ Requires-Dist: trio-websocket==0.12.1
104
104
  Requires-Dist: wsproto==1.2.0
105
105
  Requires-Dist: websocket-client==1.8.0
106
106
  Requires-Dist: selenium==4.27.1; python_version < "3.9"
@@ -137,7 +137,7 @@ Requires-Dist: pytest-cov>=5.0.0; python_version < "3.9" and extra == "coverage"
137
137
  Requires-Dist: pytest-cov>=6.0.0; python_version >= "3.9" and extra == "coverage"
138
138
  Provides-Extra: flake8
139
139
  Requires-Dist: flake8==5.0.4; python_version < "3.9" and extra == "flake8"
140
- Requires-Dist: flake8==7.1.1; python_version >= "3.9" and extra == "flake8"
140
+ Requires-Dist: flake8==7.1.2; python_version >= "3.9" and extra == "flake8"
141
141
  Requires-Dist: mccabe==0.7.0; extra == "flake8"
142
142
  Requires-Dist: pyflakes==2.5.0; python_version < "3.9" and extra == "flake8"
143
143
  Requires-Dist: pyflakes==3.2.0; python_version >= "3.9" and extra == "flake8"
@@ -3,7 +3,7 @@ sbase/__main__.py,sha256=G0bVB1-DM4PGwQ1KyOupaWCs4ePbChZNNWuX2htim5U,647
3
3
  sbase/steps.py,sha256=_WvAjydKqZfTdnZW9LPKkRty-g-lfdUPmLqnZj6ulcs,43013
4
4
  seleniumbase/__init__.py,sha256=OtJh8nGKL4xtZpw8KPqmn7Q6R-86t4cWUDyVF5MbMTo,2398
5
5
  seleniumbase/__main__.py,sha256=dn1p6dgCchmcH1zzTzzQvFwwdQQqnTGH6ULV9m4hv24,654
6
- seleniumbase/__version__.py,sha256=HQYREOtXe_KHVKfcnng511WpKG975mX8PLrqOajrkgI,47
6
+ seleniumbase/__version__.py,sha256=yo5-NDclqDbu-vlvPFL7dji8w3SHeRhS6yV5yaJdYxU,47
7
7
  seleniumbase/behave/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  seleniumbase/behave/behave_helper.py,sha256=elkl8P9eLulRAioLstE9baYNM9N_PHBmAOcajX-pH_Y,24198
9
9
  seleniumbase/behave/behave_sb.py,sha256=qQF85LoohJBfrPK5ZcPi50v-pWtOrC9qcN1B3Ki_3tY,59401
@@ -36,7 +36,7 @@ seleniumbase/console_scripts/sb_print.py,sha256=tNy-bMDgwHJO3bZxMpmo9weSE8uhbH0C
36
36
  seleniumbase/console_scripts/sb_recorder.py,sha256=fnHb5-kh11Hit-E9Ha-e4QXzqLcZvtij6mb5qNd4B1Q,11032
37
37
  seleniumbase/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
38
  seleniumbase/core/application_manager.py,sha256=e_0sjtI8cjY5BNyZj1QBR0j6_oCScxGmSXYEpcYwuZE,576
39
- seleniumbase/core/browser_launcher.py,sha256=ahPk232q2gp5H55X5uH1B93IWvFioeuuoGlahgbnNWo,235352
39
+ seleniumbase/core/browser_launcher.py,sha256=zRDRfNGsCHKn5w1R_78l5SnbieQ3bm74NMmJs4O-sQ8,235755
40
40
  seleniumbase/core/capabilities_parser.py,sha256=meIS2uHapTCq2ldfNAToC7r0cKmZDRXuYNKExM1GHDY,6038
41
41
  seleniumbase/core/colored_traceback.py,sha256=DrRWfg7XEnKcgY59Xj7Jdk09H-XqHYBSUpB-DiZt6iY,2020
42
42
  seleniumbase/core/create_db_tables.sql,sha256=VWPtrdiW_HQ6yETHjqTu-VIrTwvd8I8o1NfBeaVSHpU,972
@@ -50,7 +50,7 @@ seleniumbase/core/proxy_helper.py,sha256=4VkpMwavz0fx8wcOqJ_jyBT0HIXwcxmAcpd1gjJ
50
50
  seleniumbase/core/recorder_helper.py,sha256=fNGjbapXmEsht54x1o6Igk198QdnPxDDnjUOzQxNhNQ,25055
51
51
  seleniumbase/core/report_helper.py,sha256=AIl6Qava2yW1uSzbLpJBlPlYDz0KE-rVhogh8hsGWBo,12201
52
52
  seleniumbase/core/s3_manager.py,sha256=bkeI8I4y19ebWuQG1oEZV5qJbotC6eN8vin31OCNWJk,3521
53
- seleniumbase/core/sb_cdp.py,sha256=r9sv8WjwLhS_AFLn4QGLvbK4osfaC6ncRJRj0asrBvI,76754
53
+ seleniumbase/core/sb_cdp.py,sha256=5OLmRH5JSJYwqDomY2O9cx_0lNlAwNYjJbUTGmFUZPQ,79126
54
54
  seleniumbase/core/sb_driver.py,sha256=yvTDRblBzG6bDX7XcLiAA6QcBelSJj_HHL_04lcfeeE,13760
55
55
  seleniumbase/core/session_helper.py,sha256=s9zD3PVZEWVzG2h81cCUskbNWLfdjC_LwwQjKptHCak,558
56
56
  seleniumbase/core/settings_parser.py,sha256=gqVohHVlE_5L5Cqe2L24uYrRzvoK-saX8E_Df7_-_3I,7609
@@ -65,11 +65,11 @@ seleniumbase/extensions/disable_csp.zip,sha256=5RvomXnm2PdivUVcxTV6jfvD8WhTEsQYH
65
65
  seleniumbase/extensions/recorder.zip,sha256=OOyzF-Ize2cSRu1CqhzSAq5vusI9hqLLd2OIApUHesI,11918
66
66
  seleniumbase/extensions/sbase_ext.zip,sha256=3s1N8zrVaMz8RQEOIoBzC3KDjtmHwVZRvVsX25Odr_s,8175
67
67
  seleniumbase/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
- seleniumbase/fixtures/base_case.py,sha256=0bW99a1g8YH5YCXLnsJvZ5uSXvLOmY6QutDEpxDjX9U,721561
68
+ seleniumbase/fixtures/base_case.py,sha256=AJ33rXML0feRLo6WdCcQhxmBwEtQdn_gXuOy_iFPInY,722065
69
69
  seleniumbase/fixtures/constants.py,sha256=WMrItuNyKq3XVJ64NluLIRc4gJCxDw8K8qXED0b9S2w,13752
70
70
  seleniumbase/fixtures/css_to_xpath.py,sha256=9ouDB1xl4MJ2os6JOgTIAyHKOQfuxtxvXC3O5hSnEKA,1954
71
71
  seleniumbase/fixtures/errors.py,sha256=KyxuEVx_e3MPhVrJfNIa_3ltMpbCFxfy_jxK8RFNTns,555
72
- seleniumbase/fixtures/js_utils.py,sha256=asZJ0LDFv2BPQOzUVKK_9ie8ToGcmFousDnGdb3Vabg,51534
72
+ seleniumbase/fixtures/js_utils.py,sha256=QxHC0Wz_rDQ5dB6B1CK_UFs9zzryuElr-fj7MUtf_lA,52204
73
73
  seleniumbase/fixtures/page_actions.py,sha256=LPcFSkUvBkxLrOt4laQHHN-NLmqInT41E2vlPiOlLFY,66753
74
74
  seleniumbase/fixtures/page_utils.py,sha256=H1iV8f9vDyEy87DBntyiBXC_tg8HskcebUOAJVn0hxE,12160
75
75
  seleniumbase/fixtures/shared_utils.py,sha256=G6CsE-Adt-GfuZF-71jXWKSIQW7YZPx8FIM24pVd_yI,8368
@@ -115,9 +115,9 @@ seleniumbase/undetected/reactor.py,sha256=NropaXcO54pzmDq6quR27qPJxab6636H7LRAaq
115
115
  seleniumbase/undetected/webelement.py,sha256=OOpUYbEiOG52KsYYyuDW9tYLdA2SMnukvwQHUdPVn9E,1389
116
116
  seleniumbase/undetected/cdp_driver/__init__.py,sha256=c0TjMwPfVFyoqYOJ7PQ-Jln_L_dpN3ebHyaD-juQoM0,64
117
117
  seleniumbase/undetected/cdp_driver/_contradict.py,sha256=lP4b0h5quAy573ETn_TBbYV889cL1AuPLVInpJ0ZkiU,3183
118
- seleniumbase/undetected/cdp_driver/browser.py,sha256=vwccwrAZDssRmVnf-NVXYvs5Dvv8DtGzeiDHSY3RTfM,30189
119
- seleniumbase/undetected/cdp_driver/cdp_util.py,sha256=B5myq2zi-Gdq_vfZGPOG-LTSwbhK4n2h6SY2Z9dmIzQ,17784
120
- seleniumbase/undetected/cdp_driver/config.py,sha256=oHFJ3UH0OmLmEGgG5S6SZwbyBs9ZYMsbUJ02QCA7iZc,12044
118
+ seleniumbase/undetected/cdp_driver/browser.py,sha256=ruPunmJKwE67Veh1MjSkZAF5W38FMwc32lHgy1YULho,30261
119
+ seleniumbase/undetected/cdp_driver/cdp_util.py,sha256=zlYc_oTqtCSjV_T7lzPbFyJlj_78NHMxKkrpleXcfpQ,21374
120
+ seleniumbase/undetected/cdp_driver/config.py,sha256=t8KV1Vqa5SQRBq3-gjkHHmj9h85AplAM01asO3AWufs,12507
121
121
  seleniumbase/undetected/cdp_driver/connection.py,sha256=sOTUGjbUqKA2hPvDcRCdqw1VQjVGJs7mbgVvzS7ldtE,23360
122
122
  seleniumbase/undetected/cdp_driver/element.py,sha256=FIC6v7OmumLCT-_vIc3H4oju_oBbaLpWJUJIKm2c_q4,40467
123
123
  seleniumbase/undetected/cdp_driver/tab.py,sha256=rcASsWY_mC7L0wXpFSWM5mtojzA47qG9BWEMXe7ZGF8,50906
@@ -135,9 +135,9 @@ seleniumbase/utilities/selenium_grid/start-grid-hub.bat,sha256=Ftq-GrAKRYH2ssDPr
135
135
  seleniumbase/utilities/selenium_grid/start-grid-hub.sh,sha256=KADv0RUHONLL2_I443QFK8PryBpDmKn5Gy0s4o0vDSM,106
136
136
  seleniumbase/utilities/selenium_ide/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
137
137
  seleniumbase/utilities/selenium_ide/convert_ide.py,sha256=pZFnqEJQEKZPyNFjkLD29s2HPQgCrWW9XJWpCPhWOoM,31691
138
- seleniumbase-4.34.15.dist-info/LICENSE,sha256=BRblZsX7HyPUjQmYTiyWr_e9tzWvmR3R4SFclM2R3W0,1085
139
- seleniumbase-4.34.15.dist-info/METADATA,sha256=KFp4laKW_MENuhZdfslrP38qajSDf88F7Yb8HJ9loM8,86522
140
- seleniumbase-4.34.15.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
141
- seleniumbase-4.34.15.dist-info/entry_points.txt,sha256=CNrh2EKNaHYEhO6pP1RJyVLB99LkDDYX7TnUK8xfjqk,623
142
- seleniumbase-4.34.15.dist-info/top_level.txt,sha256=4N97aBOQ8ETCnDnokBsWb07lJfTaq3C1ZzYRxvLMxqU,19
143
- seleniumbase-4.34.15.dist-info/RECORD,,
138
+ seleniumbase-4.34.16.dist-info/LICENSE,sha256=BRblZsX7HyPUjQmYTiyWr_e9tzWvmR3R4SFclM2R3W0,1085
139
+ seleniumbase-4.34.16.dist-info/METADATA,sha256=qF4wtXCAPxi0YzvlBRfk_z-drHDdQH4wTTkhCcWHO3g,86522
140
+ seleniumbase-4.34.16.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
141
+ seleniumbase-4.34.16.dist-info/entry_points.txt,sha256=CNrh2EKNaHYEhO6pP1RJyVLB99LkDDYX7TnUK8xfjqk,623
142
+ seleniumbase-4.34.16.dist-info/top_level.txt,sha256=4N97aBOQ8ETCnDnokBsWb07lJfTaq3C1ZzYRxvLMxqU,19
143
+ seleniumbase-4.34.16.dist-info/RECORD,,