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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,,