seleniumbase 4.33.4__py3-none-any.whl → 4.34.2__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.
Files changed (33) hide show
  1. seleniumbase/__version__.py +1 -1
  2. seleniumbase/behave/behave_sb.py +10 -2
  3. seleniumbase/console_scripts/run.py +6 -2
  4. seleniumbase/console_scripts/sb_commander.py +5 -5
  5. seleniumbase/console_scripts/sb_install.py +235 -6
  6. seleniumbase/console_scripts/sb_mkdir.py +1 -0
  7. seleniumbase/core/browser_launcher.py +358 -105
  8. seleniumbase/core/log_helper.py +33 -12
  9. seleniumbase/core/proxy_helper.py +35 -30
  10. seleniumbase/core/sb_cdp.py +277 -74
  11. seleniumbase/core/settings_parser.py +2 -0
  12. seleniumbase/core/style_sheet.py +10 -0
  13. seleniumbase/fixtures/base_case.py +216 -127
  14. seleniumbase/fixtures/constants.py +3 -0
  15. seleniumbase/fixtures/js_utils.py +2 -0
  16. seleniumbase/fixtures/page_actions.py +7 -2
  17. seleniumbase/fixtures/shared_utils.py +25 -0
  18. seleniumbase/plugins/driver_manager.py +28 -0
  19. seleniumbase/plugins/pytest_plugin.py +110 -0
  20. seleniumbase/plugins/sb_manager.py +41 -0
  21. seleniumbase/plugins/selenium_plugin.py +9 -0
  22. seleniumbase/undetected/cdp_driver/_contradict.py +3 -3
  23. seleniumbase/undetected/cdp_driver/browser.py +8 -6
  24. seleniumbase/undetected/cdp_driver/cdp_util.py +3 -0
  25. seleniumbase/undetected/cdp_driver/config.py +0 -1
  26. seleniumbase/undetected/cdp_driver/element.py +22 -20
  27. seleniumbase/undetected/patcher.py +20 -5
  28. {seleniumbase-4.33.4.dist-info → seleniumbase-4.34.2.dist-info}/LICENSE +1 -1
  29. {seleniumbase-4.33.4.dist-info → seleniumbase-4.34.2.dist-info}/METADATA +111 -86
  30. {seleniumbase-4.33.4.dist-info → seleniumbase-4.34.2.dist-info}/RECORD +33 -33
  31. {seleniumbase-4.33.4.dist-info → seleniumbase-4.34.2.dist-info}/WHEEL +1 -1
  32. {seleniumbase-4.33.4.dist-info → seleniumbase-4.34.2.dist-info}/entry_points.txt +0 -0
  33. {seleniumbase-4.33.4.dist-info → seleniumbase-4.34.2.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  import fasteners
2
2
  import logging
3
3
  import os
4
+ import platform
4
5
  import re
5
6
  import shutil
6
7
  import subprocess
@@ -64,6 +65,7 @@ LOCAL_EDGEDRIVER = None
64
65
  LOCAL_IEDRIVER = None
65
66
  LOCAL_HEADLESS_IEDRIVER = None
66
67
  LOCAL_UC_DRIVER = None
68
+ ARCH = platform.architecture()[0]
67
69
  IS_ARM_MAC = shared_utils.is_arm_mac()
68
70
  IS_MAC = shared_utils.is_mac()
69
71
  IS_LINUX = shared_utils.is_linux()
@@ -97,26 +99,12 @@ def log_d(message):
97
99
  print(message)
98
100
 
99
101
 
100
- def make_writable(file_path):
101
- # Set permissions to: "If you can read it, you can write it."
102
- mode = os.stat(file_path).st_mode
103
- mode |= (mode & 0o444) >> 1 # copy R bits to W
104
- os.chmod(file_path, mode)
105
-
106
-
107
- def make_executable(file_path):
108
- # Set permissions to: "If you can read it, you can execute it."
109
- mode = os.stat(file_path).st_mode
110
- mode |= (mode & 0o444) >> 2 # copy R bits to X
111
- os.chmod(file_path, mode)
112
-
113
-
114
102
  def make_driver_executable_if_not(driver_path):
115
103
  # Verify driver has executable permissions. If not, add them.
116
104
  permissions = oct(os.stat(driver_path)[0])[-3:]
117
105
  if "4" in permissions or "6" in permissions:
118
106
  # We want at least a '5' or '7' to make sure it's executable
119
- make_executable(driver_path)
107
+ shared_utils.make_executable(driver_path)
120
108
 
121
109
 
122
110
  def extend_driver(driver):
@@ -547,10 +535,26 @@ def uc_open_with_cdp_mode(driver, url=None):
547
535
  if url_protocol not in ["about", "data", "chrome"]:
548
536
  safe_url = False
549
537
 
538
+ headless = False
539
+ headed = None
540
+ xvfb = None
541
+ if hasattr(sb_config, "headless"):
542
+ headless = sb_config.headless
543
+ if hasattr(sb_config, "headed"):
544
+ headed = sb_config.headed
545
+ if hasattr(sb_config, "xvfb"):
546
+ xvfb = sb_config.xvfb
547
+
550
548
  loop = asyncio.new_event_loop()
551
549
  asyncio.set_event_loop(loop)
552
550
  driver.cdp_base = loop.run_until_complete(
553
- cdp_util.start(host=cdp_host, port=cdp_port)
551
+ cdp_util.start(
552
+ host=cdp_host,
553
+ port=cdp_port,
554
+ headless=headless,
555
+ headed=headed,
556
+ xvfb=xvfb,
557
+ )
554
558
  )
555
559
  loop.run_until_complete(driver.cdp_base.wait(0))
556
560
 
@@ -566,6 +570,10 @@ def uc_open_with_cdp_mode(driver, url=None):
566
570
  for tab in driver.cdp_base.tabs[-1::-1]:
567
571
  if "chrome-extension://" not in str(tab):
568
572
  with gui_lock:
573
+ with suppress(Exception):
574
+ shared_utils.make_writable(
575
+ constants.MultiBrowser.PYAUTOGUILOCK
576
+ )
569
577
  loop.run_until_complete(tab.activate())
570
578
  break
571
579
 
@@ -580,11 +588,17 @@ def uc_open_with_cdp_mode(driver, url=None):
580
588
  if page_tab:
581
589
  loop.run_until_complete(page_tab.aopen())
582
590
  with gui_lock:
591
+ with suppress(Exception):
592
+ shared_utils.make_writable(
593
+ constants.MultiBrowser.PYAUTOGUILOCK
594
+ )
583
595
  loop.run_until_complete(page_tab.activate())
584
596
 
585
597
  loop.run_until_complete(driver.cdp_base.update_targets())
586
598
  page = loop.run_until_complete(driver.cdp_base.get(url))
587
599
  with gui_lock:
600
+ with suppress(Exception):
601
+ shared_utils.make_writable(constants.MultiBrowser.PYAUTOGUILOCK)
588
602
  loop.run_until_complete(page.activate())
589
603
  loop.run_until_complete(page.wait())
590
604
  if not safe_url:
@@ -604,6 +618,7 @@ def uc_open_with_cdp_mode(driver, url=None):
604
618
  cdp.find_element = CDPM.find_element
605
619
  cdp.find = CDPM.find_element
606
620
  cdp.locator = CDPM.find_element
621
+ cdp.find_element_by_text = CDPM.find_element_by_text
607
622
  cdp.find_all = CDPM.find_all
608
623
  cdp.find_elements_by_text = CDPM.find_elements_by_text
609
624
  cdp.select = CDPM.select
@@ -662,6 +677,7 @@ def uc_open_with_cdp_mode(driver, url=None):
662
677
  cdp.get_window = CDPM.get_window
663
678
  cdp.get_element_attributes = CDPM.get_element_attributes
664
679
  cdp.get_element_attribute = CDPM.get_element_attribute
680
+ cdp.get_attribute = CDPM.get_attribute
665
681
  cdp.get_element_html = CDPM.get_element_html
666
682
  cdp.get_element_rect = CDPM.get_element_rect
667
683
  cdp.get_element_size = CDPM.get_element_size
@@ -817,6 +833,72 @@ def verify_pyautogui_has_a_headed_browser(driver):
817
833
  )
818
834
 
819
835
 
836
+ def __install_pyautogui_if_missing():
837
+ try:
838
+ import pyautogui
839
+ with suppress(Exception):
840
+ use_pyautogui_ver = constants.PyAutoGUI.VER
841
+ if pyautogui.__version__ != use_pyautogui_ver:
842
+ del pyautogui
843
+ shared_utils.pip_install(
844
+ "pyautogui", version=use_pyautogui_ver
845
+ )
846
+ import pyautogui
847
+ except Exception:
848
+ print("\nPyAutoGUI required! Installing now...")
849
+ shared_utils.pip_install(
850
+ "pyautogui", version=constants.PyAutoGUI.VER
851
+ )
852
+ try:
853
+ import pyautogui
854
+ except Exception:
855
+ if (
856
+ IS_LINUX
857
+ and hasattr(sb_config, "xvfb")
858
+ and hasattr(sb_config, "headed")
859
+ and hasattr(sb_config, "headless")
860
+ and hasattr(sb_config, "headless2")
861
+ and (not sb_config.headed or sb_config.xvfb)
862
+ and not (sb_config.headless or sb_config.headless2)
863
+ ):
864
+ from sbvirtualdisplay import Display
865
+ xvfb_width = 1366
866
+ xvfb_height = 768
867
+ if (
868
+ hasattr(sb_config, "_xvfb_width")
869
+ and sb_config._xvfb_width
870
+ and isinstance(sb_config._xvfb_width, int)
871
+ and hasattr(sb_config, "_xvfb_height")
872
+ and sb_config._xvfb_height
873
+ and isinstance(sb_config._xvfb_height, int)
874
+ ):
875
+ xvfb_width = sb_config._xvfb_width
876
+ xvfb_height = sb_config._xvfb_height
877
+ if xvfb_width < 1024:
878
+ xvfb_width = 1024
879
+ sb_config._xvfb_width = xvfb_width
880
+ if xvfb_height < 768:
881
+ xvfb_height = 768
882
+ sb_config._xvfb_height = xvfb_height
883
+ with suppress(Exception):
884
+ _xvfb_display = Display(
885
+ visible=True,
886
+ size=(xvfb_width, xvfb_height),
887
+ backend="xvfb",
888
+ use_xauth=True,
889
+ )
890
+ _xvfb_display.start()
891
+ sb_config._virtual_display = _xvfb_display
892
+ sb_config.headless_active = True
893
+ if (
894
+ hasattr(sb_config, "reuse_session")
895
+ and sb_config.reuse_session
896
+ and hasattr(sb_config, "_vd_list")
897
+ and isinstance(sb_config._vd_list, list)
898
+ ):
899
+ sb_config._vd_list.append(_xvfb_display)
900
+
901
+
820
902
  def install_pyautogui_if_missing(driver):
821
903
  verify_pyautogui_has_a_headed_browser(driver)
822
904
  pip_find_lock = fasteners.InterProcessLock(
@@ -826,64 +908,13 @@ def install_pyautogui_if_missing(driver):
826
908
  with pip_find_lock:
827
909
  pass
828
910
  except Exception:
829
- # Need write permissions
830
- with suppress(Exception):
831
- make_writable(constants.PipInstall.FINDLOCK)
911
+ # Since missing permissions, skip the locks
912
+ __install_pyautogui_if_missing()
913
+ return
832
914
  with pip_find_lock: # Prevent issues with multiple processes
833
- try:
834
- import pyautogui
835
- with suppress(Exception):
836
- use_pyautogui_ver = constants.PyAutoGUI.VER
837
- if pyautogui.__version__ != use_pyautogui_ver:
838
- del pyautogui
839
- shared_utils.pip_install(
840
- "pyautogui", version=use_pyautogui_ver
841
- )
842
- import pyautogui
843
- except Exception:
844
- print("\nPyAutoGUI required! Installing now...")
845
- shared_utils.pip_install(
846
- "pyautogui", version=constants.PyAutoGUI.VER
847
- )
848
- try:
849
- import pyautogui
850
- except Exception:
851
- if (
852
- IS_LINUX
853
- and hasattr(sb_config, "xvfb")
854
- and hasattr(sb_config, "headed")
855
- and hasattr(sb_config, "headless")
856
- and hasattr(sb_config, "headless2")
857
- and (not sb_config.headed or sb_config.xvfb)
858
- and not (sb_config.headless or sb_config.headless2)
859
- ):
860
- from sbvirtualdisplay import Display
861
- xvfb_width = 1366
862
- xvfb_height = 768
863
- if (
864
- hasattr(sb_config, "_xvfb_width")
865
- and sb_config._xvfb_width
866
- and isinstance(sb_config._xvfb_width, int)
867
- and hasattr(sb_config, "_xvfb_height")
868
- and sb_config._xvfb_height
869
- and isinstance(sb_config._xvfb_height, int)
870
- ):
871
- xvfb_width = sb_config._xvfb_width
872
- xvfb_height = sb_config._xvfb_height
873
- if xvfb_width < 1024:
874
- xvfb_width = 1024
875
- sb_config._xvfb_width = xvfb_width
876
- if xvfb_height < 768:
877
- xvfb_height = 768
878
- sb_config._xvfb_height = xvfb_height
879
- with suppress(Exception):
880
- xvfb_display = Display(
881
- visible=True,
882
- size=(xvfb_width, xvfb_height),
883
- backend="xvfb",
884
- use_xauth=True,
885
- )
886
- xvfb_display.start()
915
+ with suppress(Exception):
916
+ shared_utils.make_writable(constants.PipInstall.FINDLOCK)
917
+ __install_pyautogui_if_missing()
887
918
 
888
919
 
889
920
  def get_configured_pyautogui(pyautogui_copy):
@@ -1195,6 +1226,13 @@ def _uc_gui_click_captcha(
1195
1226
  and driver.is_element_present("#challenge-form div > div")
1196
1227
  ):
1197
1228
  frame = "#challenge-form div > div"
1229
+ elif (
1230
+ driver.is_element_present('[name*="cf-turnstile-"]')
1231
+ and driver.is_element_present(
1232
+ '[style="display: grid;"] div div'
1233
+ )
1234
+ ):
1235
+ frame = '[style="display: grid;"] div div'
1198
1236
  elif (
1199
1237
  driver.is_element_present('[name*="cf-turnstile-"]')
1200
1238
  and driver.is_element_present("[class*=spacer] + div div")
@@ -1411,7 +1449,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1411
1449
  ctype = "cf_t"
1412
1450
  else:
1413
1451
  return
1414
- if not driver.is_connected():
1452
+ if not driver.is_connected() and not __is_cdp_swap_needed(driver):
1415
1453
  driver.connect()
1416
1454
  time.sleep(2)
1417
1455
  install_pyautogui_if_missing(driver)
@@ -1423,7 +1461,10 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1423
1461
  )
1424
1462
  with gui_lock: # Prevent issues with multiple processes
1425
1463
  needs_switch = False
1426
- is_in_frame = js_utils.is_in_frame(driver)
1464
+ if not __is_cdp_swap_needed(driver):
1465
+ is_in_frame = js_utils.is_in_frame(driver)
1466
+ else:
1467
+ is_in_frame = False
1427
1468
  selector = "#challenge-stage"
1428
1469
  if ctype == "g_rc":
1429
1470
  selector = "#recaptcha-token"
@@ -1431,7 +1472,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1431
1472
  driver.switch_to.parent_frame()
1432
1473
  needs_switch = True
1433
1474
  is_in_frame = js_utils.is_in_frame(driver)
1434
- if not is_in_frame:
1475
+ if not is_in_frame and not __is_cdp_swap_needed(driver):
1435
1476
  # Make sure the window is on top
1436
1477
  page_actions.switch_to_window(
1437
1478
  driver, driver.current_window_handle, 2, uc_lock=False
@@ -1498,17 +1539,18 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1498
1539
  and frame == "iframe"
1499
1540
  ):
1500
1541
  frame = 'iframe[title="reCAPTCHA"]'
1501
- if not is_in_frame or needs_switch:
1502
- # Currently not in frame (or nested frame outside CF one)
1503
- try:
1504
- if visible_iframe or ctype == "g_rc":
1505
- driver.switch_to_frame(frame)
1506
- except Exception:
1507
- if visible_iframe or ctype == "g_rc":
1508
- if driver.is_element_present("iframe"):
1509
- driver.switch_to_frame("iframe")
1510
- else:
1511
- return
1542
+ if not __is_cdp_swap_needed(driver):
1543
+ if not is_in_frame or needs_switch:
1544
+ # Currently not in frame (or nested frame outside CF one)
1545
+ try:
1546
+ if visible_iframe or ctype == "g_rc":
1547
+ driver.switch_to_frame(frame)
1548
+ except Exception:
1549
+ if visible_iframe or ctype == "g_rc":
1550
+ if driver.is_element_present("iframe"):
1551
+ driver.switch_to_frame("iframe")
1552
+ else:
1553
+ return
1512
1554
  try:
1513
1555
  selector = "div.cf-turnstile"
1514
1556
  if ctype == "g_rc":
@@ -1518,17 +1560,20 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1518
1560
  for i in range(10):
1519
1561
  pyautogui.hotkey("shift", "tab")
1520
1562
  time.sleep(0.027)
1563
+ if ctype == "g_rc":
1564
+ if js_utils.get_active_element_css(driver) == "body":
1565
+ break
1521
1566
  tab_count = 0
1522
1567
  for i in range(34):
1523
1568
  pyautogui.press("\t")
1524
1569
  tab_count += 1
1525
1570
  time.sleep(0.027)
1526
1571
  active_element_css = js_utils.get_active_element_css(driver)
1527
- print(active_element_css)
1528
1572
  if (
1529
1573
  active_element_css.startswith(selector)
1530
1574
  or active_element_css.endswith(" > div" * 2)
1531
1575
  or (special_form and active_element_css.endswith(" div"))
1576
+ or (ctype == "g_rc" and "frame[name" in active_element_css)
1532
1577
  ):
1533
1578
  found_checkbox = True
1534
1579
  sb_config._saved_cf_tab_count = tab_count
@@ -1548,6 +1593,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1548
1593
  )
1549
1594
  and hasattr(sb_config, "_saved_cf_tab_count")
1550
1595
  and sb_config._saved_cf_tab_count
1596
+ and not __is_cdp_swap_needed(driver)
1551
1597
  ):
1552
1598
  driver.uc_open_with_disconnect(driver.current_url, 3.8)
1553
1599
  with suppress(Exception):
@@ -1762,22 +1808,34 @@ def _add_chrome_proxy_extension(
1762
1808
  ):
1763
1809
  # Single-threaded
1764
1810
  if zip_it:
1765
- proxy_helper.create_proxy_ext(
1766
- proxy_string, proxy_user, proxy_pass, bypass_list
1767
- )
1768
- proxy_zip = proxy_helper.PROXY_ZIP_PATH
1769
- chrome_options.add_extension(proxy_zip)
1811
+ proxy_zip_lock = fasteners.InterProcessLock(PROXY_ZIP_LOCK)
1812
+ with proxy_zip_lock:
1813
+ proxy_helper.create_proxy_ext(
1814
+ proxy_string, proxy_user, proxy_pass, bypass_list
1815
+ )
1816
+ proxy_zip = proxy_helper.PROXY_ZIP_PATH
1817
+ chrome_options.add_extension(proxy_zip)
1770
1818
  else:
1771
- proxy_helper.create_proxy_ext(
1772
- proxy_string, proxy_user, proxy_pass, bypass_list, zip_it=False
1773
- )
1774
- proxy_dir_path = proxy_helper.PROXY_DIR_PATH
1775
- chrome_options = add_chrome_ext_dir(chrome_options, proxy_dir_path)
1819
+ proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
1820
+ with proxy_dir_lock:
1821
+ proxy_helper.create_proxy_ext(
1822
+ proxy_string,
1823
+ proxy_user,
1824
+ proxy_pass,
1825
+ bypass_list,
1826
+ zip_it=False,
1827
+ )
1828
+ proxy_dir_path = proxy_helper.PROXY_DIR_PATH
1829
+ chrome_options = add_chrome_ext_dir(
1830
+ chrome_options, proxy_dir_path
1831
+ )
1776
1832
  else:
1777
1833
  # Multi-threaded
1778
1834
  if zip_it:
1779
1835
  proxy_zip_lock = fasteners.InterProcessLock(PROXY_ZIP_LOCK)
1780
1836
  with proxy_zip_lock:
1837
+ with suppress(Exception):
1838
+ shared_utils.make_writable(PROXY_ZIP_LOCK)
1781
1839
  if multi_proxy:
1782
1840
  _set_proxy_filenames()
1783
1841
  if not os.path.exists(proxy_helper.PROXY_ZIP_PATH):
@@ -1789,6 +1847,8 @@ def _add_chrome_proxy_extension(
1789
1847
  else:
1790
1848
  proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
1791
1849
  with proxy_dir_lock:
1850
+ with suppress(Exception):
1851
+ shared_utils.make_writable(PROXY_DIR_LOCK)
1792
1852
  if multi_proxy:
1793
1853
  _set_proxy_filenames()
1794
1854
  if not os.path.exists(proxy_helper.PROXY_DIR_PATH):
@@ -1797,7 +1857,7 @@ def _add_chrome_proxy_extension(
1797
1857
  proxy_user,
1798
1858
  proxy_pass,
1799
1859
  bypass_list,
1800
- False,
1860
+ zip_it=False,
1801
1861
  )
1802
1862
  chrome_options = add_chrome_ext_dir(
1803
1863
  chrome_options, proxy_helper.PROXY_DIR_PATH
@@ -1814,6 +1874,8 @@ def is_using_uc(undetectable, browser_name):
1814
1874
  def _unzip_to_new_folder(zip_file, folder):
1815
1875
  proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
1816
1876
  with proxy_dir_lock:
1877
+ with suppress(Exception):
1878
+ shared_utils.make_writable(PROXY_DIR_LOCK)
1817
1879
  if not os.path.exists(folder):
1818
1880
  import zipfile
1819
1881
  zip_ref = zipfile.ZipFile(zip_file, "r")
@@ -2298,7 +2360,6 @@ def _set_chrome_options(
2298
2360
  chrome_options.add_argument("--disable-ipc-flooding-protection")
2299
2361
  chrome_options.add_argument("--disable-password-generation")
2300
2362
  chrome_options.add_argument("--disable-domain-reliability")
2301
- chrome_options.add_argument("--disable-component-update")
2302
2363
  chrome_options.add_argument("--disable-breakpad")
2303
2364
  included_disabled_features = []
2304
2365
  included_disabled_features.append("OptimizationHints")
@@ -2383,8 +2444,6 @@ def _set_firefox_options(
2383
2444
  options.set_preference("dom.webnotifications.enabled", False)
2384
2445
  options.set_preference("dom.disable_beforeunload", True)
2385
2446
  options.set_preference("browser.contentblocking.database.enabled", True)
2386
- options.set_preference("extensions.allowPrivateBrowsingByDefault", True)
2387
- options.set_preference("extensions.PrivateBrowsing.notification", False)
2388
2447
  options.set_preference("extensions.systemAddon.update.enabled", False)
2389
2448
  options.set_preference("extensions.update.autoUpdateDefault", False)
2390
2449
  options.set_preference("extensions.update.enabled", False)
@@ -2600,6 +2659,118 @@ def get_driver(
2600
2659
  or browser_name == constants.Browser.EDGE
2601
2660
  )
2602
2661
  ):
2662
+ if (
2663
+ binary_location.lower() == "cft"
2664
+ and browser_name == constants.Browser.GOOGLE_CHROME
2665
+ ):
2666
+ binary_folder = None
2667
+ if IS_MAC:
2668
+ if IS_ARM_MAC:
2669
+ binary_folder = "chrome-mac-arm64"
2670
+ else:
2671
+ binary_folder = "chrome-mac-x64"
2672
+ elif IS_LINUX:
2673
+ binary_folder = "chrome-linux64"
2674
+ elif IS_WINDOWS:
2675
+ if "64" in ARCH:
2676
+ binary_folder = "chrome-win64"
2677
+ else:
2678
+ binary_folder = "chrome-win32"
2679
+ if binary_folder:
2680
+ binary_location = os.path.join(DRIVER_DIR, binary_folder)
2681
+ if not os.path.exists(binary_location):
2682
+ from seleniumbase.console_scripts import sb_install
2683
+ args = " ".join(sys.argv)
2684
+ if not (
2685
+ "-n" in sys.argv or " -n=" in args or args == "-c"
2686
+ ):
2687
+ # (Not multithreaded)
2688
+ sys_args = sys.argv # Save a copy of current sys args
2689
+ log_d(
2690
+ "\nWarning: Chrome for Testing binary not found..."
2691
+ )
2692
+ try:
2693
+ sb_install.main(override="cft")
2694
+ except Exception as e:
2695
+ log_d("\nWarning: Chrome download failed: %s" % e)
2696
+ sys.argv = sys_args # Put back the original sys args
2697
+ else:
2698
+ chrome_fixing_lock = fasteners.InterProcessLock(
2699
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
2700
+ )
2701
+ with chrome_fixing_lock:
2702
+ with suppress(Exception):
2703
+ shared_utils.make_writable(
2704
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
2705
+ )
2706
+ if not os.path.exists(binary_location):
2707
+ sys_args = sys.argv # Save a copy of sys args
2708
+ log_d(
2709
+ "\nWarning: "
2710
+ "Chrome for Testing binary not found..."
2711
+ )
2712
+ sb_install.main(override="cft")
2713
+ sys.argv = sys_args # Put back original args
2714
+ else:
2715
+ binary_location = None
2716
+ if (
2717
+ binary_location.lower() == "chs"
2718
+ and browser_name == constants.Browser.GOOGLE_CHROME
2719
+ ):
2720
+ binary_folder = None
2721
+ if IS_MAC:
2722
+ if IS_ARM_MAC:
2723
+ binary_folder = "chrome-headless-shell-mac-arm64"
2724
+ else:
2725
+ binary_folder = "chrome-headless-shell-mac-x64"
2726
+ elif IS_LINUX:
2727
+ binary_folder = "chrome-headless-shell-linux64"
2728
+ elif IS_WINDOWS:
2729
+ if "64" in ARCH:
2730
+ binary_folder = "chrome-headless-shell-win64"
2731
+ else:
2732
+ binary_folder = "chrome-headless-shell-win32"
2733
+ if binary_folder:
2734
+ binary_location = os.path.join(DRIVER_DIR, binary_folder)
2735
+ if not os.path.exists(binary_location):
2736
+ from seleniumbase.console_scripts import sb_install
2737
+ args = " ".join(sys.argv)
2738
+ if not (
2739
+ "-n" in sys.argv or " -n=" in args or args == "-c"
2740
+ ):
2741
+ # (Not multithreaded)
2742
+ sys_args = sys.argv # Save a copy of current sys args
2743
+ log_d(
2744
+ "\nWarning: "
2745
+ "Chrome-Headless-Shell binary not found..."
2746
+ )
2747
+ try:
2748
+ sb_install.main(override="chs")
2749
+ except Exception as e:
2750
+ log_d(
2751
+ "\nWarning: "
2752
+ "Chrome-Headless-Shell download failed: %s" % e
2753
+ )
2754
+ sys.argv = sys_args # Put back the original sys args
2755
+ else:
2756
+ chrome_fixing_lock = fasteners.InterProcessLock(
2757
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
2758
+ )
2759
+ with chrome_fixing_lock:
2760
+ with suppress(Exception):
2761
+ shared_utils.make_writable(
2762
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
2763
+ )
2764
+ if not os.path.exists(binary_location):
2765
+ sys_args = sys.argv # Save a copy of sys args
2766
+ log_d(
2767
+ "\nWarning: "
2768
+ "Chrome-Headless-Shell binary not found..."
2769
+ )
2770
+ sb_install.main(override="chs")
2771
+ sys.argv = sys_args # Put back original args
2772
+ else:
2773
+ binary_location = None
2603
2774
  if not os.path.exists(binary_location):
2604
2775
  log_d(
2605
2776
  "\nWarning: The Chromium binary specified (%s) was NOT found!"
@@ -2609,7 +2780,7 @@ def get_driver(
2609
2780
  elif binary_location.endswith("/") or binary_location.endswith("\\"):
2610
2781
  log_d(
2611
2782
  "\nWarning: The Chromium binary path must be a full path "
2612
- "that includes the driver filename at the end of it!"
2783
+ "that includes the browser filename at the end of it!"
2613
2784
  "\n(Will use default settings...)\n" % binary_location
2614
2785
  )
2615
2786
  # Example of a valid binary location path - MacOS:
@@ -2618,6 +2789,34 @@ def get_driver(
2618
2789
  else:
2619
2790
  binary_name = binary_location.split("/")[-1].split("\\")[-1]
2620
2791
  valid_names = get_valid_binary_names_for_browser(browser_name)
2792
+ if binary_name == "Google Chrome for Testing.app":
2793
+ binary_name = "Google Chrome for Testing"
2794
+ binary_location += "/Contents/MacOS/Google Chrome for Testing"
2795
+ elif binary_name in ["chrome-mac-arm64", "chrome-mac-x64"]:
2796
+ binary_name = "Google Chrome for Testing"
2797
+ binary_location += "/Google Chrome for Testing.app"
2798
+ binary_location += "/Contents/MacOS/Google Chrome for Testing"
2799
+ elif binary_name == "chrome-linux64":
2800
+ binary_name = "chrome"
2801
+ binary_location += "/chrome"
2802
+ elif binary_name in ["chrome-win32", "chrome-win64"]:
2803
+ binary_name = "chrome.exe"
2804
+ binary_location += "\\chrome.exe"
2805
+ elif binary_name in [
2806
+ "chrome-headless-shell-mac-arm64",
2807
+ "chrome-headless-shell-mac-x64",
2808
+ ]:
2809
+ binary_name = "chrome-headless-shell"
2810
+ binary_location += "/chrome-headless-shell"
2811
+ elif binary_name == "chrome-headless-shell-linux64":
2812
+ binary_name = "chrome-headless-shell"
2813
+ binary_location += "/chrome-headless-shell"
2814
+ elif binary_name in [
2815
+ "chrome-headless-shell-win32",
2816
+ "chrome-headless-shell-win64",
2817
+ ]:
2818
+ binary_name = "chrome-headless-shell.exe"
2819
+ binary_location += "\\chrome-headless-shell.exe"
2621
2820
  if binary_name not in valid_names:
2622
2821
  log_d(
2623
2822
  "\nWarning: The Chromium binary specified is NOT valid!"
@@ -2626,6 +2825,8 @@ def get_driver(
2626
2825
  "" % (binary_name, valid_names)
2627
2826
  )
2628
2827
  binary_location = None
2828
+ elif binary_location.lower() == "chs":
2829
+ headless = True
2629
2830
  if (uc_cdp_events or uc_subprocess) and not undetectable:
2630
2831
  undetectable = True
2631
2832
  if mobile_emulator and not user_agent:
@@ -2923,6 +3124,8 @@ def get_remote_driver(
2923
3124
  constants.PipInstall.FINDLOCK
2924
3125
  )
2925
3126
  with pip_find_lock: # Prevent issues with multiple processes
3127
+ with suppress(Exception):
3128
+ shared_utils.make_writable(constants.PipInstall.FINDLOCK)
2926
3129
  try:
2927
3130
  from seleniumwire import webdriver
2928
3131
  import blinker
@@ -3360,6 +3563,8 @@ def get_local_driver(
3360
3563
  constants.PipInstall.FINDLOCK
3361
3564
  )
3362
3565
  with pip_find_lock: # Prevent issues with multiple processes
3566
+ with suppress(Exception):
3567
+ shared_utils.make_writable(constants.PipInstall.FINDLOCK)
3363
3568
  try:
3364
3569
  from seleniumwire import webdriver
3365
3570
  import blinker
@@ -3423,6 +3628,10 @@ def get_local_driver(
3423
3628
  constants.MultiBrowser.DRIVER_FIXING_LOCK
3424
3629
  )
3425
3630
  with geckodriver_fixing_lock:
3631
+ with suppress(Exception):
3632
+ shared_utils.make_writable(
3633
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
3634
+ )
3426
3635
  if not geckodriver_on_path():
3427
3636
  sys_args = sys.argv # Save a copy of sys args
3428
3637
  log_d(
@@ -3725,6 +3934,10 @@ def get_local_driver(
3725
3934
  constants.MultiBrowser.DRIVER_FIXING_LOCK
3726
3935
  )
3727
3936
  with edgedriver_fixing_lock:
3937
+ with suppress(Exception):
3938
+ shared_utils.make_writable(
3939
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
3940
+ )
3728
3941
  msg = "Microsoft Edge Driver not found."
3729
3942
  if edgedriver_upgrade_needed:
3730
3943
  msg = "Microsoft Edge Driver update needed."
@@ -3783,6 +3996,12 @@ def get_local_driver(
3783
3996
  edge_options.add_argument("--guest")
3784
3997
  if dark_mode:
3785
3998
  edge_options.add_argument("--enable-features=WebContentsForceDark")
3999
+ if headless1:
4000
+ # developer.chrome.com/blog/removing-headless-old-from-chrome
4001
+ with suppress(Exception):
4002
+ if int(str(use_version).split(".")[0]) >= 132:
4003
+ headless1 = False
4004
+ headless2 = True
3786
4005
  if headless2:
3787
4006
  try:
3788
4007
  if use_version == "latest" or int(use_version) >= 109:
@@ -4036,7 +4255,6 @@ def get_local_driver(
4036
4255
  edge_options.add_argument("--disable-ipc-flooding-protection")
4037
4256
  edge_options.add_argument("--disable-password-generation")
4038
4257
  edge_options.add_argument("--disable-domain-reliability")
4039
- edge_options.add_argument("--disable-component-update")
4040
4258
  edge_options.add_argument("--disable-breakpad")
4041
4259
  included_disabled_features = []
4042
4260
  included_disabled_features.append("OptimizationHints")
@@ -4108,6 +4326,10 @@ def get_local_driver(
4108
4326
  constants.MultiBrowser.DRIVER_FIXING_LOCK
4109
4327
  )
4110
4328
  with edgedriver_fixing_lock:
4329
+ with suppress(Exception):
4330
+ shared_utils.make_writable(
4331
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
4332
+ )
4111
4333
  with suppress(Exception):
4112
4334
  if not _was_driver_repaired():
4113
4335
  _repair_edgedriver(edge_version)
@@ -4321,6 +4543,12 @@ def get_local_driver(
4321
4543
  use_version = find_chromedriver_version_to_use(
4322
4544
  use_version, driver_version
4323
4545
  )
4546
+ if headless1:
4547
+ # developer.chrome.com/blog/removing-headless-old-from-chrome
4548
+ with suppress(Exception):
4549
+ if int(str(use_version).split(".")[0]) >= 132:
4550
+ headless1 = False
4551
+ headless2 = True
4324
4552
  if headless2:
4325
4553
  try:
4326
4554
  if (
@@ -4490,6 +4718,10 @@ def get_local_driver(
4490
4718
  constants.MultiBrowser.DRIVER_FIXING_LOCK
4491
4719
  )
4492
4720
  with chromedriver_fixing_lock:
4721
+ with suppress(Exception):
4722
+ shared_utils.make_writable(
4723
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
4724
+ )
4493
4725
  msg = "chromedriver update needed. Getting it now:"
4494
4726
  if not path_chromedriver:
4495
4727
  msg = "chromedriver not found. Getting it now:"
@@ -4581,6 +4813,10 @@ def get_local_driver(
4581
4813
  constants.MultiBrowser.DRIVER_FIXING_LOCK
4582
4814
  )
4583
4815
  with uc_lock: # Avoid multithreaded issues
4816
+ with suppress(Exception):
4817
+ shared_utils.make_writable(
4818
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
4819
+ )
4584
4820
  if make_uc_driver_from_chromedriver:
4585
4821
  if os.path.exists(LOCAL_CHROMEDRIVER):
4586
4822
  with suppress(Exception):
@@ -4813,7 +5049,13 @@ def get_local_driver(
4813
5049
  )
4814
5050
  uc_activated = True
4815
5051
  except URLError as e:
4816
- if cert in e.args[0] and IS_MAC:
5052
+ if (
5053
+ IS_MAC
5054
+ and hasattr(e, "args")
5055
+ and isinstance(e.args, (list, tuple))
5056
+ and len(e.args) > 0
5057
+ and cert in e.args[0]
5058
+ ):
4817
5059
  mac_certificate_error = True
4818
5060
  else:
4819
5061
  raise
@@ -4840,6 +5082,10 @@ def get_local_driver(
4840
5082
  if not os.path.exists(cf_lock_path):
4841
5083
  # Avoid multithreaded issues
4842
5084
  with cf_lock:
5085
+ with suppress(Exception):
5086
+ shared_utils.make_writable(
5087
+ cf_lock_path
5088
+ )
4843
5089
  # Install Python Certificates (MAC)
4844
5090
  os.system(
4845
5091
  r"bash /Applications/Python*/"
@@ -4983,6 +5229,10 @@ def get_local_driver(
4983
5229
  constants.MultiBrowser.DRIVER_FIXING_LOCK
4984
5230
  )
4985
5231
  with chromedriver_fixing_lock:
5232
+ with suppress(Exception):
5233
+ shared_utils.make_writable(
5234
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
5235
+ )
4986
5236
  if not _was_driver_repaired():
4987
5237
  _repair_chromedriver(
4988
5238
  chrome_options, headless_options, mcv
@@ -5181,7 +5431,10 @@ def get_local_driver(
5181
5431
  chromedr_fixing_lock = fasteners.InterProcessLock(
5182
5432
  constants.MultiBrowser.DRIVER_FIXING_LOCK
5183
5433
  )
5434
+ D_F_L = constants.MultiBrowser.DRIVER_FIXING_LOCK
5184
5435
  with chromedr_fixing_lock:
5436
+ with suppress(Exception):
5437
+ shared_utils.make_writable(D_F_L)
5185
5438
  if not _was_driver_repaired():
5186
5439
  with suppress(Exception):
5187
5440
  _repair_chromedriver(