diff --git a/README.md b/README.md
index 32d2dbfc54e..a5d717bcfa1 100755
--- a/README.md
+++ b/README.md
@@ -1332,13 +1332,14 @@ pytest --reruns=1 --reruns-delay=1
-
+
-
+
-
+
+
--------
diff --git a/examples/custom_settings.py b/examples/custom_settings.py
index b90e1c09f84..2eccc6bd8eb 100644
--- a/examples/custom_settings.py
+++ b/examples/custom_settings.py
@@ -72,6 +72,12 @@
HEADLESS_START_WIDTH = 1440
HEADLESS_START_HEIGHT = 1880
+# If True, hides messages related to downloading drivers.
+# If False, you'll see details about downloading drivers.
+# (This only affects driver downloads coming from tests.)
+# (Eg. Using "sbase get chromedriver" won't hide output.)
+HIDE_DRIVER_DOWNLOADS = False
+
# Changing the default behavior of MasterQA Mode.
MASTERQA_DEFAULT_VALIDATION_MESSAGE = "Does the page look good?"
MASTERQA_WAIT_TIME_BEFORE_VERIFY = 0.5
diff --git a/help_docs/webdriver_installation.md b/help_docs/webdriver_installation.md
index af97b022fc1..6883dc9a064 100644
--- a/help_docs/webdriver_installation.md
+++ b/help_docs/webdriver_installation.md
@@ -21,6 +21,12 @@ If the necessary driver is not found in this location while running tests, Selen
```bash
sbase get chromedriver 114
sbase get chromedriver 114.0.5735.90
+sbase get chromedriver stable
+sbase get chromedriver beta
+sbase get chromedriver dev
+sbase get chromedriver canary
+sbase get chromedriver previous # One major version before the stable version
+sbase get chromedriver mlatest # Milestone latest version for detected browser
sbase get edgedriver 115.0.1901.183
```
diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt
index bd8fdab7c26..16231170d5b 100644
--- a/mkdocs_build/requirements.txt
+++ b/mkdocs_build/requirements.txt
@@ -20,7 +20,7 @@ paginate==0.5.6
pyquery==2.0.0
readtime==3.0.0
mkdocs==1.5.2
-mkdocs-material==9.2.3
+mkdocs-material==9.2.5
mkdocs-exclude-search==0.6.5
mkdocs-simple-hooks==0.1.5
mkdocs-material-extensions==1.1.1
diff --git a/requirements.txt b/requirements.txt
index 3601a122f82..f2724ab4803 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -11,7 +11,8 @@ attrs==22.1.0;python_version<"3.7"
attrs>=23.1.0;python_version>="3.7"
certifi>=2023.7.22
filelock>=3.4.1;python_version<"3.7"
-filelock>=3.12.2;python_version>="3.7"
+filelock>=3.12.2;python_version>="3.7" and python_version<"3.8"
+filelock>=3.12.3;python_version>="3.8"
platformdirs>=2.4.0;python_version<"3.7"
platformdirs>=3.10.0;python_version>="3.7"
parse>=1.19.1
@@ -47,7 +48,8 @@ execnet==2.0.2;python_version>="3.7"
iniconfig==1.1.1;python_version<"3.7"
iniconfig==2.0.0;python_version>="3.7"
pluggy==1.0.0;python_version<"3.7"
-pluggy==1.2.0;python_version>="3.7"
+pluggy==1.2.0;python_version>="3.7" and python_version<"3.8"
+pluggy==1.3.0;python_version>="3.8"
py==1.11.0
pytest==7.0.1;python_version<"3.7"
pytest==7.4.0;python_version>="3.7"
diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py
index 207949f7c09..8eb03dbe8bc 100755
--- a/seleniumbase/__version__.py
+++ b/seleniumbase/__version__.py
@@ -1,2 +1,2 @@
# seleniumbase package
-__version__ = "4.17.11"
+__version__ = "4.17.12"
diff --git a/seleniumbase/config/settings.py b/seleniumbase/config/settings.py
index cc8cdffdcd5..0b2822100f3 100644
--- a/seleniumbase/config/settings.py
+++ b/seleniumbase/config/settings.py
@@ -118,6 +118,12 @@
HEADLESS_START_WIDTH = 1440
HEADLESS_START_HEIGHT = 1880
+# If True, hides messages related to downloading drivers.
+# If False, you'll see details about downloading drivers.
+# (This only affects driver downloads coming from tests.)
+# (If calling "sbase get chromedriver", then won't hide.)
+HIDE_DRIVER_DOWNLOADS = False
+
# #####>>>>>----- MasterQA SETTINGS -----<<<<<#####
# ##### (Used when importing MasterQA as the parent class)
diff --git a/seleniumbase/console_scripts/ReadMe.md b/seleniumbase/console_scripts/ReadMe.md
index e85f27a044b..0592ba8b0d0 100644
--- a/seleniumbase/console_scripts/ReadMe.md
+++ b/seleniumbase/console_scripts/ReadMe.md
@@ -64,6 +64,8 @@ sbase get geckodriver
sbase get edgedriver
sbase get chromedriver 114
sbase get chromedriver 114.0.5735.90
+sbase get chromedriver stable
+sbase get chromedriver beta
sbase get chromedriver -p
```
diff --git a/seleniumbase/console_scripts/run.py b/seleniumbase/console_scripts/run.py
index e98b688fe16..314c608ef60 100644
--- a/seleniumbase/console_scripts/run.py
+++ b/seleniumbase/console_scripts/run.py
@@ -39,6 +39,7 @@
import colorama
import sys
import time
+from seleniumbase.config import settings
from seleniumbase.fixtures import constants
colorama.init(autoreset=True)
@@ -143,6 +144,8 @@ def show_install_usage():
print(" sbase get edgedriver")
print(" sbase get chromedriver 114")
print(" sbase get chromedriver 114.0.5735.90")
+ print(" sbase get chromedriver stable")
+ print(" sbase get chromedriver beta")
print(" sbase get chromedriver -p")
print(" Output:")
print(" Downloads the webdriver to seleniumbase/drivers/")
@@ -984,6 +987,7 @@ def main():
proxy_helper.validate_proxy_string(proxy_string)
break
try:
+ settings.HIDE_DRIVER_DOWNLOADS = False
sb_install.main()
except Exception as e:
invalid_run_cmd = constants.Warnings.INVALID_RUN_COMMAND
diff --git a/seleniumbase/console_scripts/sb_install.py b/seleniumbase/console_scripts/sb_install.py
index e66e6ddff7b..339556b05ce 100644
--- a/seleniumbase/console_scripts/sb_install.py
+++ b/seleniumbase/console_scripts/sb_install.py
@@ -24,6 +24,7 @@
(edgedriver is required for MS__Edge automation)
"""
import colorama
+import logging
import os
import platform
import requests
@@ -46,7 +47,7 @@
IS_WINDOWS = shared_utils.is_windows()
DRIVER_DIR = os.path.dirname(os.path.realpath(drivers.__file__))
LOCAL_PATH = "/usr/local/bin/" # On Mac and Linux systems
-DEFAULT_CHROMEDRIVER_VERSION = "72.0.3626.69" # (If can't find LATEST_STABLE)
+DEFAULT_CHROMEDRIVER_VERSION = "114.0.5735.90" # (If can't find LATEST_STABLE)
DEFAULT_GECKODRIVER_VERSION = "v0.33.0"
DEFAULT_EDGEDRIVER_VERSION = "115.0.1901.183" # (If can't find LATEST_STABLE)
DEFAULT_OPERADRIVER_VERSION = "v.96.0.4664.45"
@@ -73,6 +74,8 @@ def invalid_run_command():
exp += " sbase get edgedriver\n"
exp += " sbase get chromedriver 114\n"
exp += " sbase get chromedriver 114.0.5735.90\n"
+ exp += " sbase get chromedriver stable\n"
+ exp += " sbase get chromedriver beta\n"
exp += " sbase get chromedriver -p\n"
exp += " Output:\n"
exp += " Downloads the webdriver to seleniumbase/drivers/\n"
@@ -159,7 +162,79 @@ def requests_get_with_retry(url):
return response
-def main(override=None, intel_for_uc=None):
+def get_cft_known_good_versions():
+ if hasattr(sb_config, "cft_kgv_json") and sb_config.cft_kgv_json:
+ return sb_config.cft_kgv_json
+ cft_ngv_url = (
+ "https://googlechromelabs.github.io/"
+ "chrome-for-testing/known-good-versions.json"
+ )
+ sb_config.cft_kgv_json = requests_get(cft_ngv_url)
+ return sb_config.cft_kgv_json
+
+
+def get_cft_latest_versions_per_milestone():
+ if hasattr(sb_config, "cft_lvpm_json") and sb_config.cft_lvpm_json:
+ return sb_config.cft_lvpm_json
+ cft_lvpm_url = (
+ "https://googlechromelabs.github.io/"
+ "chrome-for-testing/latest-versions-per-milestone.json"
+ )
+ sb_config.cft_lvpm_json = requests_get(cft_lvpm_url)
+ return sb_config.cft_lvpm_json
+
+
+def get_cft_latest_version_from_milestone(milestone):
+ url_request = get_cft_latest_versions_per_milestone()
+ return url_request.json()["milestones"][milestone]["version"]
+
+
+def get_latest_chromedriver_version(channel="Stable"):
+ try:
+ if hasattr(sb_config, "cft_lkgv_json") and sb_config.cft_lkgv_json:
+ return sb_config.cft_lkgv_json["channels"][channel]["version"]
+ req = requests_get(
+ "https://googlechromelabs.github.io/"
+ "chrome-for-testing/last-known-good-versions.json"
+ )
+ if req and req.ok:
+ sb_config.cft_lkgv_json = req.json()
+ return req.json()["channels"][channel]["version"]
+ except Exception:
+ pass
+ # If a problem with Chrome-for-Testing JSON API: Fall back
+ return DEFAULT_CHROMEDRIVER_VERSION
+
+
+def get_latest_stable_chromedriver_version():
+ return get_latest_chromedriver_version(channel="Stable")
+
+
+def get_latest_beta_chromedriver_version():
+ return get_latest_chromedriver_version(channel="Beta")
+
+
+def get_latest_dev_chromedriver_version():
+ return get_latest_chromedriver_version(channel="Dev")
+
+
+def get_latest_canary_chromedriver_version():
+ return get_latest_chromedriver_version(channel="Canary")
+
+
+def log_d(message):
+ """If setting sb_config.settings.HIDE_DRIVER_DOWNLOADS to True,
+ output from driver downloads are logged instead of printed."""
+ if (
+ hasattr(sb_config.settings, "HIDE_DRIVER_DOWNLOADS")
+ and sb_config.settings.HIDE_DRIVER_DOWNLOADS
+ ):
+ logging.debug(message)
+ else:
+ print(message)
+
+
+def main(override=None, intel_for_uc=None, force_uc=None):
if override:
found_proxy = None
if hasattr(sb_config, "proxy_driver") and sb_config.proxy_driver:
@@ -201,6 +276,8 @@ def main(override=None, intel_for_uc=None):
else:
invalid_run_command()
name = sys.argv[2].lower()
+ if force_uc:
+ name = "uc_driver"
file_name = None
download_url = None
@@ -222,18 +299,22 @@ def main(override=None, intel_for_uc=None):
c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX
c4 = colorama.Fore.LIGHTRED_EX + colorama.Back.LIGHTWHITE_EX
c5 = colorama.Fore.RED + colorama.Back.LIGHTWHITE_EX
+ c6 = colorama.Fore.LIGHTYELLOW_EX + colorama.Back.CYAN
cr = colorama.Style.RESET_ALL
if IS_LINUX:
c1 = ""
c2 = ""
c3 = ""
+ c4 = ""
+ c5 = ""
+ c6 = ""
cr = ""
if name == "chromedriver" or name == "uc_driver":
if name == "uc_driver" and IS_ARM_MAC:
intel_for_uc = True # uc_driver is generated from chromedriver
last = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE"
- use_version = DEFAULT_CHROMEDRIVER_VERSION
+ use_version = DEFAULT_CHROMEDRIVER_VERSION # Until get correct VER
if (
not override
@@ -260,16 +341,27 @@ def main(override=None, intel_for_uc=None):
get_latest = False
get_v_latest = False
- get_latest_minus_one = False
+ get_previous = False
+ get_beta = False
+ get_canary = False
if num_args == 4 or num_args == 5:
if "-p" not in sys.argv[3].lower():
use_version = sys.argv[3]
uv_low = use_version.lower()
- if uv_low == "latest":
+ if uv_low == "latest" or uv_low == "stable":
+ uv_low = "latest" # If "stable", rename
get_latest = True
- elif uv_low == "latest-1":
- get_latest_minus_one = True
- elif len(uv_low) < 4 and uv_low.isdigit() and int(uv_low) > 69:
+ elif uv_low == "latest-1" or uv_low == "previous":
+ uv_low = "latest-1" # If "previous", rename
+ get_previous = True
+ elif uv_low == "beta":
+ get_beta = True
+ elif uv_low == "dev":
+ use_version = get_latest_dev_chromedriver_version()
+ sys.argv[3] = use_version
+ elif uv_low == "canary":
+ get_canary = True
+ elif uv_low.isdigit() and int(uv_low) > 69:
get_v_latest = True
else:
copy_to_path = True
@@ -280,10 +372,22 @@ def main(override=None, intel_for_uc=None):
invalid_run_command()
if IS_MAC:
if IS_ARM_MAC and not intel_for_uc:
- if use_version == "latest" or use_version == "latest-1":
- use_version = requests_get(last).text
- if use_version == "latest-1":
+ use_version = use_version.lower()
+ if (
+ use_version == "latest"
+ or use_version == "stable"
+ or use_version == "latest-1"
+ or use_version == "previous"
+ or use_version == "beta"
+ or use_version == "canary"
+ ):
+ use_version = get_latest_stable_chromedriver_version()
+ if use_version == "latest-1" or use_version == "previous":
use_version = str(int(use_version.split(".")[0]) - 1)
+ elif use_version == "beta":
+ use_version = str(int(use_version.split(".")[0]) + 1)
+ elif use_version == "canary":
+ use_version = str(int(use_version.split(".")[0]) + 2)
if (
IS_ARM_MAC
and not intel_for_uc
@@ -304,19 +408,36 @@ def main(override=None, intel_for_uc=None):
)
found_chromedriver = False
cft = False
- if get_latest or get_latest_minus_one:
- url_request = requests_get(last)
- if url_request.ok:
- found_chromedriver = True
- use_version = url_request.text
- if get_latest_minus_one:
- get_v_latest = True
- use_version = str(int(use_version.split(".")[0]) - 1)
- if get_v_latest:
- url_req = requests_get(last)
- if url_req.ok:
- latest_version = url_req.text
- if int(use_version) < 115:
+ if get_latest or get_previous or get_beta or get_canary:
+ use_version = get_latest_stable_chromedriver_version()
+ found_chromedriver = True
+ if get_previous and int(use_version.split(".")[0]) >= 115:
+ get_v_latest = True
+ use_version = str(int(use_version.split(".")[0]) - 1)
+ elif get_beta and int(use_version.split(".")[0]) >= 115:
+ get_v_latest = True
+ use_version = get_latest_beta_chromedriver_version()
+ use_version = use_version.split(".")[0]
+ elif get_canary and int(use_version.split(".")[0]) >= 115:
+ get_v_latest = True
+ use_version = get_latest_canary_chromedriver_version()
+ use_version = use_version.split(".")[0]
+ force_cft = False
+ if (
+ use_version.split(".")[0].isnumeric()
+ and int(use_version.split(".")[0]) >= 115
+ ):
+ force_cft = True
+ if (get_v_latest or force_cft):
+ if get_v_latest:
+ if not force_cft:
+ url_req = requests_get(last)
+ if url_req.ok:
+ latest_version = url_req.text
+ else:
+ latest_version = get_latest_stable_chromedriver_version()
+ force_cft = False
+ if not force_cft and int(use_version) < 115:
last = last + "_" + use_version
url_request = requests_get(last)
if url_request.ok:
@@ -325,15 +446,20 @@ def main(override=None, intel_for_uc=None):
if use_version == latest_version:
get_latest = True
else:
+ url_request = None
cft = True
- cft_latest_vpm_url = (
- "https://googlechromelabs.github.io/"
- "chrome-for-testing/latest-versions-per-milestone.json"
- )
- url_request = requests_get(cft_latest_vpm_url)
- if url_request.ok:
- use_ver = use_version
- fver = url_request.json()["milestones"][use_ver]["version"]
+ if force_cft:
+ url_request = get_cft_known_good_versions()
+ if (
+ url_request.ok
+ and '"version":"%s"' % use_version in url_request.text
+ ):
+ fver = use_version
+ found_chromedriver = True
+ else:
+ url_request = get_cft_latest_versions_per_milestone()
+ if not force_cft and url_request.ok:
+ fver = get_cft_latest_version_from_milestone(use_version)
found_chromedriver = True
use_version = str(fver)
if use_version == latest_version:
@@ -380,21 +506,46 @@ def main(override=None, intel_for_uc=None):
if found_chromedriver or url_request.ok:
p_version = use_version
p_version = c3 + use_version + cr
- if get_latest or cft:
- p_version = p_version + " " + c2 + "(Latest)" + cr
+ latest_stable = get_latest_stable_chromedriver_version()
+ latest_beta = get_latest_beta_chromedriver_version()
+ latest_dev = get_latest_dev_chromedriver_version()
+ latest_canary = get_latest_canary_chromedriver_version()
+ vint = True
+ int_use_ver = None
+ int_latest_ver = None
+ try:
+ int_use_ver = int(use_version.split(".")[0])
+ int_latest_ver = int(latest_stable.split(".")[0])
+ except Exception:
+ vint = False
+ on_cft = False
+ if int_latest_ver > 115:
+ on_cft = True
+ if cft and on_cft and use_version == latest_stable:
+ p_version = p_version + " " + c2 + "(Latest Stable)" + cr + " "
+ elif cft and on_cft and use_version == latest_beta:
+ p_version = p_version + " " + c2 + "(Latest Beta)" + cr + " "
+ elif cft and on_cft and use_version == latest_dev:
+ p_version = p_version + " " + c2 + "(Latest Dev)" + cr + " "
+ elif cft and on_cft and use_version == latest_canary:
+ p_version = p_version + " " + c2 + "(Latest Canary)" + cr + " "
+ elif not vint:
+ pass
+ elif vint and cft and on_cft and int_use_ver == int_latest_ver:
+ p_version = p_version + " " + c2 + "(Stable)" + cr
+ elif vint and cft and on_cft and int_use_ver == int_latest_ver + 1:
+ p_version = p_version + " " + c2 + "(Beta)" + cr
+ elif vint and cft and on_cft and int_use_ver == int_latest_ver + 2:
+ p_version = p_version + " " + c2 + "(Dev / Canary)" + cr
+ elif vint and cft and on_cft and int_use_ver == int_latest_ver - 1:
+ p_version = p_version + " " + c6 + "(Previous Version)" + cr
+ elif cft and not on_cft:
+ pass
else:
- n_l_s = "Not Latest"
- try:
- int_use_version = int(use_version.split(".")[0])
- int_latest_version = int(latest_version.split(".")[0])
- if int_use_version > int_latest_version:
- n_l_s = "Not Latest Stable"
- except Exception:
- pass
- not_latest = c5 + "(" + c4 + n_l_s + c5 + ")" + cr
+ not_latest = c5 + "(" + c4 + "Legacy Version" + c5 + ")" + cr
p_version = p_version + " " + not_latest
- msg = c2 + "chromedriver version for download" + cr
- print("\n*** %s = %s" % (msg, p_version))
+ msg = c2 + "chromedriver to download" + cr
+ log_d("\n*** %s = %s" % (msg, p_version))
else:
raise Exception("Could not find chromedriver to download!\n")
if not get_latest:
@@ -445,9 +596,9 @@ def main(override=None, intel_for_uc=None):
if not found_geckodriver:
url_request = requests_get(download_url)
if found_geckodriver or url_request.ok:
- msg = c2 + "geckodriver version for download" + cr
+ msg = c2 + "geckodriver to download" + cr
p_version = c3 + use_version + cr
- print("\n*** %s = %s" % (msg, p_version))
+ log_d("\n*** %s = %s" % (msg, p_version))
else:
raise Exception(
"\nCould not find the specified geckodriver "
@@ -552,9 +703,9 @@ def main(override=None, intel_for_uc=None):
raise Exception(
"Could not find version [%s] of EdgeDriver!" % use_version
)
- msg = c2 + "edgedriver version for download" + cr
+ msg = c2 + "edgedriver to download" + cr
p_version = c3 + use_version + cr
- print("\n*** %s = %s" % (msg, p_version))
+ log_d("\n*** %s = %s" % (msg, p_version))
elif name == "iedriver":
major_version = "3.14"
full_version = "3.14.0"
@@ -582,9 +733,9 @@ def main(override=None, intel_for_uc=None):
url_request = requests_get_with_retry(headless_ie_url)
if url_request.ok:
headless_ie_exists = True
- msg = c2 + "HeadlessIEDriver version for download" + cr
+ msg = c2 + "HeadlessIEDriver to download" + cr
p_version = c3 + headless_ie_version + cr
- print("\n*** %s = %s" % (msg, p_version))
+ log_d("\n*** %s = %s" % (msg, p_version))
elif name == "operadriver" or name == "operachromiumdriver":
name = "operadriver"
use_version = DEFAULT_OPERADRIVER_VERSION
@@ -647,9 +798,9 @@ def main(override=None, intel_for_uc=None):
"releases/download/"
"%s/%s" % (use_version, file_name)
)
- msg = c2 + "operadriver version for download" + cr
+ msg = c2 + "operadriver to download" + cr
p_version = c3 + use_version + cr
- print("\n*** %s = %s" % (msg, p_version))
+ log_d("\n*** %s = %s" % (msg, p_version))
else:
invalid_run_command()
@@ -667,14 +818,14 @@ def main(override=None, intel_for_uc=None):
headless_ie_file_path = os.path.join(
downloads_folder, headless_ie_file_name
)
- print(
+ log_d(
"\nDownloading %s from:\n%s ..."
% (headless_ie_file_name, headless_ie_url)
)
remote_file = requests_get_with_retry(headless_ie_url)
with open(headless_ie_file_path, "wb") as file:
file.write(remote_file.content)
- print("%sDownload Complete!%s\n" % (c1, cr))
+ log_d("%sDownload Complete!%s\n" % (c1, cr))
zip_file_path = headless_ie_file_path
zip_ref = zipfile.ZipFile(zip_file_path, "r")
contents = zip_ref.namelist()
@@ -713,12 +864,12 @@ def main(override=None, intel_for_uc=None):
os.remove(new_file)
if not driver_file or not driver_path or not filename:
raise Exception("headless_ie_selenium.exe missing from Zip file!")
- print("Extracting %s from %s ..." % (filename, headless_ie_file_name))
+ log_d("Extracting %s from %s ..." % (filename, headless_ie_file_name))
zip_ref.extractall(downloads_folder)
zip_ref.close()
os.remove(zip_file_path)
shutil.copyfile(driver_path, os.path.join(downloads_folder, filename))
- print("%sUnzip Complete!%s\n" % (c2, cr))
+ log_d("%sUnzip Complete!%s\n" % (c2, cr))
to_remove = [
"%s/%s/ruby_example/Gemfile" % (downloads_folder, h_ie_fn),
"%s/%s/ruby_example/Gemfile.lock" % (downloads_folder, h_ie_fn),
@@ -737,22 +888,22 @@ def main(override=None, intel_for_uc=None):
# Only works if the directory is empty
os.rmdir(os.path.join(downloads_folder, h_ie_fn))
driver_path = os.path.join(downloads_folder, filename)
- print(
+ log_d(
"The file [%s] was saved to:\n%s%s%s\n"
% (filename, c3, driver_path, cr)
)
- print("Making [%s %s] executable ..." % (driver_file, use_version))
+ log_d("Making [%s %s] executable ..." % (driver_file, use_version))
make_executable(driver_path)
- print(
+ log_d(
"%s[%s %s] is now ready for use!%s"
% (c1, driver_file, use_version, cr)
)
- print("\nDownloading %s from:\n%s ..." % (file_name, download_url))
+ log_d("\nDownloading %s from:\n%s ..." % (file_name, download_url))
remote_file = requests_get_with_retry(download_url)
with open(file_path, "wb") as file:
file.write(remote_file.content)
- print("%sDownload Complete!%s\n" % (c1, cr))
+ log_d("%sDownload Complete!%s\n" % (c1, cr))
if file_name.endswith(".zip"):
zip_file_path = file_path
@@ -774,23 +925,32 @@ def main(override=None, intel_for_uc=None):
driver_contents = [driver_name]
# Remove existing version if exists
new_file = os.path.join(downloads_folder, str(f_name))
- if intel_for_uc and IS_MAC:
+ if name == "uc_driver":
if new_file.endswith("drivers/chromedriver"):
new_file = new_file.replace(
"drivers/chromedriver", "drivers/uc_driver"
)
+ elif new_file.endswith("drivers/chromedriver.exe"):
+ new_file = new_file.replace(
+ "drivers/chromedriver.exe", "drivers/uc_driver.exe"
+ )
elif "drivers/%s/chromedriver" % plat_arch in new_file:
new_file = new_file.replace(
"drivers/%s/chromedriver" % plat_arch,
"drivers/%s/uc_driver" % plat_arch
)
+ elif "drivers/%s/chromedriver.exe" % plat_arch in new_file:
+ new_file = new_file.replace(
+ "drivers/%s/chromedriver.exe" % plat_arch,
+ "drivers/%s/uc_driver.exe" % plat_arch
+ )
if "Driver" in new_file or "driver" in new_file:
if os.path.exists(new_file):
os.remove(new_file) # Technically the old file now
if driver_contents:
contents = driver_contents
- print("Extracting %s from %s ..." % (contents, file_name))
- if intel_for_uc and IS_ARM_MAC:
+ log_d("Extracting %s from %s ..." % (contents, file_name))
+ if name == "uc_driver":
f_name = "uc_driver"
new_file = os.path.join(downloads_folder, f_name)
if os.path.exists(new_file):
@@ -800,6 +960,9 @@ def main(override=None, intel_for_uc=None):
if zipinfo.filename.split("/")[-1] == "chromedriver":
zipinfo.filename = "uc_driver"
zip_ref.extract(zipinfo, downloads_folder)
+ elif zipinfo.filename.split("/")[-1] == "chromedriver.exe":
+ zipinfo.filename = "uc_driver.exe"
+ zip_ref.extract(zipinfo, downloads_folder)
contents = zip_ref.namelist()
if driver_contents:
contents = driver_contents
@@ -826,19 +989,19 @@ def main(override=None, intel_for_uc=None):
zip_ref.extractall(downloads_folder)
zip_ref.close()
os.remove(zip_file_path)
- print("%sUnzip Complete!%s\n" % (c2, cr))
+ log_d("%sUnzip Complete!%s\n" % (c2, cr))
for f_name in contents:
- if intel_for_uc:
+ if name == "uc_driver":
if IS_WINDOWS:
f_name = "uc_driver.exe"
else:
f_name = "uc_driver"
new_file = os.path.join(downloads_folder, str(f_name))
pr_file = c3 + new_file + cr
- print("The file [%s] was saved to:\n%s\n" % (f_name, pr_file))
- print("Making [%s %s] executable ..." % (f_name, use_version))
+ log_d("The file [%s] was saved to:\n%s\n" % (f_name, pr_file))
+ log_d("Making [%s %s] executable ..." % (f_name, use_version))
make_executable(new_file)
- print(
+ log_d(
"%s[%s %s] is now ready for use!%s" %
(c1, f_name, use_version, cr)
)
@@ -846,8 +1009,8 @@ def main(override=None, intel_for_uc=None):
path_file = LOCAL_PATH + f_name
shutil.copyfile(new_file, path_file)
make_executable(path_file)
- print("Also copied to: %s%s%s" % (c3, path_file, cr))
- print("")
+ log_d("Also copied to: %s%s%s" % (c3, path_file, cr))
+ log_d("")
elif name == "edgedriver" or name == "msedgedriver":
if IS_MAC or IS_LINUX:
# Mac / Linux
@@ -899,11 +1062,11 @@ def main(override=None, intel_for_uc=None):
os.remove(new_file)
if not driver_file or not driver_path:
raise Exception("msedgedriver missing from Zip file!")
- print("Extracting %s from %s ..." % (contents, file_name))
+ log_d("Extracting %s from %s ..." % (contents, file_name))
zip_ref.extractall(downloads_folder)
zip_ref.close()
os.remove(zip_file_path)
- print("%sUnzip Complete!%s\n" % (c2, cr))
+ log_d("%sUnzip Complete!%s\n" % (c2, cr))
to_remove = [
"%s/Driver_Notes/credits.html" % downloads_folder,
"%s/Driver_Notes/EULA" % downloads_folder,
@@ -916,13 +1079,13 @@ def main(override=None, intel_for_uc=None):
# Only works if the directory is empty
os.rmdir(os.path.join(downloads_folder, "Driver_Notes/"))
pr_driver_path = c3 + driver_path + cr
- print(
+ log_d(
"The file [%s] was saved to:\n%s\n"
% (driver_file, pr_driver_path)
)
- print("Making [%s %s] executable ..." % (driver_file, use_version))
+ log_d("Making [%s %s] executable ..." % (driver_file, use_version))
make_executable(driver_path)
- print(
+ log_d(
"%s[%s %s] is now ready for use!%s"
% (c1, driver_file, use_version, cr)
)
@@ -930,8 +1093,8 @@ def main(override=None, intel_for_uc=None):
path_file = LOCAL_PATH + f_name
shutil.copyfile(new_file, path_file)
make_executable(path_file)
- print("Also copied to: %s%s%s" % (c3, path_file, cr))
- print("")
+ log_d("Also copied to: %s%s%s" % (c3, path_file, cr))
+ log_d("")
elif name == "operadriver":
if len(contents) > 3:
raise Exception("Unexpected content in OperaDriver Zip file!")
@@ -949,11 +1112,11 @@ def main(override=None, intel_for_uc=None):
os.remove(new_file)
if not driver_file or not driver_path:
raise Exception("Operadriver missing from Zip file!")
- print("Extracting %s from %s ..." % (contents, file_name))
+ log_d("Extracting %s from %s ..." % (contents, file_name))
zip_ref.extractall(downloads_folder)
zip_ref.close()
os.remove(zip_file_path)
- print("%sUnzip Complete!%s\n" % (c2, cr))
+ log_d("%sUnzip Complete!%s\n" % (c2, cr))
inner_driver = os.path.join(
downloads_folder, inner_folder, driver_file
)
@@ -962,13 +1125,13 @@ def main(override=None, intel_for_uc=None):
)
shutil.copyfile(inner_driver, driver_path)
pr_driver_path = c3 + driver_path + cr
- print(
+ log_d(
"The file [%s] was saved to:\n%s\n"
% (driver_file, pr_driver_path)
)
- print("Making [%s %s] executable ..." % (driver_file, use_version))
+ log_d("Making [%s %s] executable ..." % (driver_file, use_version))
make_executable(driver_path)
- print(
+ log_d(
"%s[%s %s] is now ready for use!%s"
% (c1, driver_file, use_version, cr)
)
@@ -976,7 +1139,7 @@ def main(override=None, intel_for_uc=None):
path_file = LOCAL_PATH + driver_file
shutil.copyfile(driver_path, path_file)
make_executable(path_file)
- print("Also copied to: %s%s%s" % (c3, path_file, cr))
+ log_d("Also copied to: %s%s%s" % (c3, path_file, cr))
# Clean up extra files
if os.path.exists(inner_driver):
os.remove(inner_driver)
@@ -985,7 +1148,7 @@ def main(override=None, intel_for_uc=None):
if os.path.exists(os.path.join(downloads_folder, inner_folder)):
# Only works if the directory is empty
os.rmdir(os.path.join(downloads_folder, inner_folder))
- print("")
+ log_d("")
elif len(contents) == 0:
raise Exception("Zip file %s is empty!" % zip_file_path)
else:
@@ -1001,18 +1164,18 @@ def main(override=None, intel_for_uc=None):
if "Driver" in new_file or "driver" in new_file:
if os.path.exists(new_file):
os.remove(new_file) # Technically the old file now
- print("Extracting %s from %s ..." % (contents, file_name))
+ log_d("Extracting %s from %s ..." % (contents, file_name))
tar.extractall(downloads_folder)
tar.close()
os.remove(tar_file_path)
- print("%sUnzip Complete!%s\n" % (c2, cr))
+ log_d("%sUnzip Complete!%s\n" % (c2, cr))
for f_name in contents:
new_file = os.path.join(downloads_folder, str(f_name))
pr_file = c3 + new_file + cr
- print("The file [%s] was saved to:\n%s\n" % (f_name, pr_file))
- print("Making [%s %s] executable ..." % (f_name, use_version))
+ log_d("The file [%s] was saved to:\n%s\n" % (f_name, pr_file))
+ log_d("Making [%s %s] executable ..." % (f_name, use_version))
make_executable(new_file)
- print(
+ log_d(
"%s[%s %s] is now ready for use!%s"
% (c1, f_name, use_version, cr)
)
@@ -1020,8 +1183,8 @@ def main(override=None, intel_for_uc=None):
path_file = LOCAL_PATH + f_name
shutil.copyfile(new_file, path_file)
make_executable(path_file)
- print("Also copied to: %s%s%s" % (c3, path_file, cr))
- print("")
+ log_d("Also copied to: %s%s%s" % (c3, path_file, cr))
+ log_d("")
elif len(contents) == 0:
raise Exception("Tar file %s is empty!" % tar_file_path)
else:
@@ -1029,15 +1192,10 @@ def main(override=None, intel_for_uc=None):
else:
# Not a .zip file or a .tar.gz file. Just a direct download.
if "Driver" in file_name or "driver" in file_name:
- print("Making [%s] executable ..." % file_name)
+ log_d("Making [%s] executable ..." % file_name)
make_executable(file_path)
- print("%s[%s] is now ready for use!%s" % (c1, file_name, cr))
- print("Location of [%s]:\n%s\n" % (file_name, file_path))
- if name == "uc_driver" and not IS_ARM_MAC:
- print(
- "%s[uc_driver] will be created from [chromedriver] at runtime!%s\n"
- % (c5, cr)
- )
+ log_d("%s[%s] is now ready for use!%s" % (c1, file_name, cr))
+ log_d("Location of [%s]:\n%s\n" % (file_name, file_path))
if __name__ == "__main__":
diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py
index 82b1ae2afae..668eeb096e7 100644
--- a/seleniumbase/core/browser_launcher.py
+++ b/seleniumbase/core/browser_launcher.py
@@ -8,13 +8,13 @@
import time
import urllib3
import warnings
-import zipfile
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.common.service import utils as service_utils
from selenium.webdriver.edge.service import Service as EdgeService
from selenium.webdriver.firefox.service import Service as FirefoxService
from selenium.webdriver.safari.service import Service as SafariService
+from seleniumbase import decorators
from seleniumbase import drivers # webdriver storage folder for SeleniumBase
from seleniumbase import extensions # browser extensions storage folder
from seleniumbase.config import settings
@@ -82,6 +82,18 @@
pass # SeleniumBase will use web drivers from the System PATH by default
+def log_d(message):
+ """If setting sb_config.settings.HIDE_DRIVER_DOWNLOADS to True,
+ output from driver downloads are logged instead of printed."""
+ if (
+ hasattr(settings, "HIDE_DRIVER_DOWNLOADS")
+ and settings.HIDE_DRIVER_DOWNLOADS
+ ):
+ logging.debug(message)
+ else:
+ print(message)
+
+
def make_executable(file_path):
# Set permissions to: "If you can read it, you can execute it."
mode = os.stat(file_path).st_mode
@@ -97,6 +109,7 @@ def make_driver_executable_if_not(driver_path):
make_executable(driver_path)
+@decorators.rate_limited(4)
def requests_get(url, proxy_string=None):
import requests
@@ -122,12 +135,8 @@ def requests_get(url, proxy_string=None):
def get_latest_chromedriver_version():
- last = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE"
- url_request = requests_get(last)
- if url_request.ok:
- return url_request.text
- else:
- return None
+ from seleniumbase.console_scripts import sb_install
+ return sb_install.get_latest_stable_chromedriver_version()
def chromedriver_on_path():
@@ -174,6 +183,27 @@ def find_chromedriver_version_to_use(use_version, driver_version):
and int(str(driver_version).split(".")[0]) >= 72
):
use_version = str(driver_version)
+ elif driver_version and not str(driver_version).split(".")[0].isdigit():
+ from seleniumbase.console_scripts import sb_install
+ driver_version = driver_version.lower()
+ if driver_version == "stable" or driver_version == "latest":
+ use_version = sb_install.get_latest_stable_chromedriver_version()
+ elif driver_version == "beta":
+ use_version = sb_install.get_latest_beta_chromedriver_version()
+ elif driver_version == "dev":
+ use_version = sb_install.get_latest_dev_chromedriver_version()
+ elif driver_version == "canary":
+ use_version = sb_install.get_latest_canary_chromedriver_version()
+ elif driver_version == "previous" or driver_version == "latest-1":
+ use_version = sb_install.get_latest_stable_chromedriver_version()
+ use_version = str(int(use_version.split(".")[0]) - 1)
+ elif driver_version == "mlatest":
+ if use_version.split(".")[0].isdigit():
+ major = use_version.split(".")[0]
+ if int(major) >= 115:
+ use_version = (
+ sb_install.get_cft_latest_version_from_milestone(major)
+ )
return use_version
@@ -217,6 +247,8 @@ def uc_special_open_if_cf(driver, url, proxy_string=None):
or has_cf(req_get.text)
):
special = True
+ if status_str == "403":
+ time.sleep(0.06) # Forbidden / Blocked! (Wait first!)
except Exception:
pass
if special:
@@ -367,7 +399,7 @@ def _repair_chromedriver(chrome_options, headless_options, mcv=None):
def _repair_edgedriver(edge_version):
- print(
+ log_d(
"\nWarning: msedgedriver version doesn't match Edge version!"
"\nAttempting to install a matching version of msedgedriver:"
)
@@ -483,6 +515,7 @@ def _unzip_to_new_folder(zip_file, folder):
proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
with proxy_dir_lock:
if not os.path.exists(folder):
+ import zipfile
zip_ref = zipfile.ZipFile(zip_file, "r")
os.makedirs(folder)
zip_ref.extractall(folder)
@@ -1170,13 +1203,13 @@ def get_driver(
)
):
if not os.path.exists(binary_location):
- print(
+ log_d(
"\nWarning: The Chromium binary specified (%s) was NOT found!"
"\n(Will use default settings...)\n" % binary_location
)
binary_location = None
elif binary_location.endswith("/") or binary_location.endswith("\\"):
- print(
+ log_d(
"\nWarning: The Chromium binary path must be a full path "
"that includes the driver filename at the end of it!"
"\n(Will use default settings...)\n" % binary_location
@@ -1188,7 +1221,7 @@ def get_driver(
binary_name = binary_location.split("/")[-1].split("\\")[-1]
valid_names = get_valid_binary_names_for_browser(browser_name)
if binary_name not in valid_names:
- print(
+ log_d(
"\nWarning: The Chromium binary specified is NOT valid!"
'\nExpecting "%s" to be found in %s for the browser / OS!'
"\n(Will use default settings...)\n"
@@ -2070,16 +2103,15 @@ def get_local_driver(
)
elif not geckodriver_on_path():
from seleniumbase.console_scripts import sb_install
-
args = " ".join(sys.argv)
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
# (Not multithreaded)
sys_args = sys.argv # Save a copy of current sys args
- print("\nWarning: geckodriver not found. Getting it now:")
+ log_d("\nWarning: geckodriver not found. Getting it now:")
try:
sb_install.main(override="geckodriver")
except Exception as e:
- print("\nWarning: Could not install geckodriver: %s" % e)
+ log_d("\nWarning: Could not install geckodriver: %s" % e)
sys.argv = sys_args # Put back the original sys args
else:
geckodriver_fixing_lock = fasteners.InterProcessLock(
@@ -2088,7 +2120,7 @@ def get_local_driver(
with geckodriver_fixing_lock:
if not geckodriver_on_path():
sys_args = sys.argv # Save a copy of sys args
- print(
+ log_d(
"\nWarning: geckodriver not found. "
"Getting it now:"
)
@@ -2211,12 +2243,11 @@ def get_local_driver(
)
elif not iedriver_on_path():
from seleniumbase.console_scripts import sb_install
-
args = " ".join(sys.argv)
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
# (Not multithreaded)
sys_args = sys.argv # Save a copy of current sys args
- print("\nWarning: IEDriver not found. Getting it now:")
+ log_d("\nWarning: IEDriver not found. Getting it now:")
sb_install.main(override="iedriver")
sys.argv = sys_args # Put back the original sys args
if LOCAL_HEADLESS_IEDRIVER and os.path.exists(LOCAL_HEADLESS_IEDRIVER):
@@ -2229,12 +2260,11 @@ def get_local_driver(
)
elif not headless_iedriver_on_path():
from seleniumbase.console_scripts import sb_install
-
args = " ".join(sys.argv)
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
# (Not multithreaded)
sys_args = sys.argv # Save a copy of current sys args
- print("\nWarning: HeadlessIEDriver not found. Getting it now:")
+ log_d("\nWarning: HeadlessIEDriver not found. Getting it now:")
sb_install.main(override="iedriver")
sys.argv = sys_args # Put back the original sys args
if not headless:
@@ -2340,7 +2370,6 @@ def get_local_driver(
)
if not local_edgedriver_exists or edgedriver_upgrade_needed:
from seleniumbase.console_scripts import sb_install
-
args = " ".join(sys.argv)
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
# (Not multithreaded)
@@ -2348,7 +2377,7 @@ def get_local_driver(
if edgedriver_upgrade_needed:
msg = "Microsoft Edge Driver update needed."
sys_args = sys.argv # Save a copy of current sys args
- print("\n%s Getting it now:" % msg)
+ log_d("\n%s Getting it now:" % msg)
sb_install.main(override="edgedriver %s" % use_version)
sys.argv = sys_args # Put back the original sys args
else:
@@ -2360,7 +2389,7 @@ def get_local_driver(
if edgedriver_upgrade_needed:
msg = "Microsoft Edge Driver update needed."
sys_args = sys.argv # Save a copy of current sys args
- print("\n%s Getting it now:" % msg)
+ log_d("\n%s Getting it now:" % msg)
sb_install.main(override="edgedriver %s" % use_version)
sys.argv = sys_args # Put back the original sys args
@@ -2902,34 +2931,36 @@ def get_local_driver(
)
use_version = "latest"
major_chrome_version = None
- if selenium4_or_newer:
- try:
- if chrome_options.binary_location:
- try:
- major_chrome_version = (
- detect_b_ver.get_browser_version_from_binary(
- chrome_options.binary_location,
- )
- ).split(".")[0]
- if len(major_chrome_version) < 2:
- major_chrome_version = None
- except Exception:
- major_chrome_version = None
- if not major_chrome_version:
- br_app = "google-chrome"
+ full_ch_version = None
+ full_ch_driver_version = None
+ try:
+ if chrome_options.binary_location:
+ try:
major_chrome_version = (
- detect_b_ver.get_browser_version_from_os(br_app)
+ detect_b_ver.get_browser_version_from_binary(
+ chrome_options.binary_location,
+ )
).split(".")[0]
- if int(major_chrome_version) < 67:
+ if len(major_chrome_version) < 2:
+ major_chrome_version = None
+ except Exception:
major_chrome_version = None
- elif (
- int(major_chrome_version) >= 67
- and int(major_chrome_version) <= 72
- ):
- # chromedrivers 2.41 - 2.46 could be swapped with 72
- major_chrome_version = "72"
- except Exception:
+ if not major_chrome_version:
+ br_app = "google-chrome"
+ full_ch_version = (
+ detect_b_ver.get_browser_version_from_os(br_app)
+ )
+ major_chrome_version = full_ch_version.split(".")[0]
+ if int(major_chrome_version) < 67:
major_chrome_version = None
+ elif (
+ int(major_chrome_version) >= 67
+ and int(major_chrome_version) <= 72
+ ):
+ # chromedrivers 2.41 - 2.46 could be swapped with 72
+ major_chrome_version = "72"
+ except Exception:
+ major_chrome_version = None
if major_chrome_version:
use_version = major_chrome_version
ch_driver_version = None
@@ -2943,12 +2974,20 @@ def get_local_driver(
output = output.decode("latin1")
else:
output = output.decode("utf-8")
- output = output.split(" ")[1].split(".")[0]
+ full_ch_driver_version = output.split(" ")[1]
+ output = full_ch_driver_version.split(".")[0]
if int(output) >= 2:
ch_driver_version = output
except Exception:
pass
elif path_chromedriver:
+ try:
+ make_driver_executable_if_not(path_chromedriver)
+ except Exception as e:
+ logging.debug(
+ "\nWarning: Could not make chromedriver"
+ " executable: %s" % e
+ )
try:
output = subprocess.check_output(
'"%s" --version' % path_chromedriver, shell=True
@@ -2957,7 +2996,8 @@ def get_local_driver(
output = output.decode("latin1")
else:
output = output.decode("utf-8")
- output = output.split(" ")[1].split(".")[0]
+ full_ch_driver_version = output.split(" ")[1]
+ output = full_ch_driver_version.split(".")[0]
if int(output) >= 2:
ch_driver_version = output
except Exception:
@@ -2997,23 +3037,7 @@ def get_local_driver(
use_version = find_chromedriver_version_to_use(
use_version, driver_version
)
- if (
- LOCAL_CHROMEDRIVER
- and os.path.exists(LOCAL_CHROMEDRIVER)
- and (
- use_version == ch_driver_version
- or use_version == "latest"
- )
- and (
- not is_using_uc(undetectable, browser_name)
- or not IS_ARM_MAC
- )
- or (
- is_using_uc(undetectable, browser_name)
- and IS_ARM_MAC
- and uc_driver_version == use_version
- )
- ):
+ if LOCAL_CHROMEDRIVER and os.path.exists(LOCAL_CHROMEDRIVER):
try:
make_driver_executable_if_not(LOCAL_CHROMEDRIVER)
except Exception as e:
@@ -3021,31 +3045,73 @@ def get_local_driver(
"\nWarning: Could not make chromedriver"
" executable: %s" % e
)
+ use_uc = is_using_uc(undetectable, browser_name)
+ make_uc_driver_from_chromedriver = False
+ local_ch_exists = (
+ LOCAL_CHROMEDRIVER and os.path.exists(LOCAL_CHROMEDRIVER)
+ )
+ """If no LOCAL_CHROMEDRIVER, but path_chromedriver, and the
+ browser version nearly matches the driver version, then use
+ the path_chromedriver instead of downloading a new driver.
+ Eg. 116.0.* for both is close, but not 116.0.* and 116.1.*"""
+ browser_driver_close_match = False
+ if (
+ path_chromedriver
+ and full_ch_version
+ and full_ch_driver_version
+ ):
+ full_ch_v_p = full_ch_version.split(".")[0:2]
+ full_ch_driver_v_p = full_ch_driver_version.split(".")[0:2]
+ if full_ch_v_p == full_ch_driver_v_p:
+ browser_driver_close_match = True
+ # If not ARM MAC and need to use uc_driver (and it's missing),
+ # and already have chromedriver with the correct version,
+ # then copy chromedriver to uc_driver (and it'll get patched).
+ if (
+ not IS_ARM_MAC
+ and use_uc
+ and (
+ (
+ (local_ch_exists or path_chromedriver)
+ and use_version == ch_driver_version
+ and (
+ not os.path.exists(LOCAL_UC_DRIVER)
+ or uc_driver_version != use_version
+ )
+ )
+ or (
+ local_ch_exists
+ and use_version == "latest"
+ and not os.path.exists(LOCAL_UC_DRIVER)
+ )
+ )
+ ):
+ make_uc_driver_from_chromedriver = True
elif (
- not path_chromedriver
+ (use_uc and not os.path.exists(LOCAL_UC_DRIVER))
+ or (not use_uc and not path_chromedriver)
or (
- use_version != "latest"
- and ch_driver_version
- and use_version != ch_driver_version
+ not use_uc
+ and use_version != "latest" # Browser version detected
+ and (ch_driver_version or not local_ch_exists)
and (
- selenium4_or_newer
- or ch_driver_version != "72"
+ use_version.split(".")[0] != ch_driver_version
+ or (
+ not local_ch_exists
+ and use_version.isnumeric()
+ and int(use_version) >= 115
+ and not browser_driver_close_match
+ )
)
)
or (
- is_using_uc(undetectable, browser_name)
- and not os.path.exists(LOCAL_UC_DRIVER)
- )
- or (
- is_using_uc(undetectable, browser_name)
- and IS_ARM_MAC
- and use_version != "latest"
+ use_uc
+ and use_version != "latest" # Browser version detected
and uc_driver_version != use_version
)
):
# chromedriver download needed in the seleniumbase/drivers dir
from seleniumbase.console_scripts import sb_install
-
args = " ".join(sys.argv)
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
# (Not multithreaded)
@@ -3053,14 +3119,22 @@ def get_local_driver(
msg = "chromedriver update needed. Getting it now:"
if not path_chromedriver:
msg = "chromedriver not found. Getting it now:"
- print("\nWarning: %s" % msg)
+ if use_uc and not os.path.exists(LOCAL_UC_DRIVER):
+ msg = "uc_driver not found. Getting it now:"
+ if use_uc and os.path.exists(LOCAL_UC_DRIVER):
+ msg = "uc_driver update needed. Getting it now:"
+ log_d("\nWarning: %s" % msg)
+ force_uc = False
intel_for_uc = False
- if IS_ARM_MAC and is_using_uc(undetectable, browser_name):
+ if use_uc:
+ force_uc = True
+ if IS_ARM_MAC and use_uc:
intel_for_uc = True # Use Intel's driver for UC Mode
try:
sb_install.main(
override="chromedriver %s" % use_version,
intel_for_uc=intel_for_uc,
+ force_uc=force_uc,
)
except Exception:
d_latest = get_latest_chromedriver_version()
@@ -3092,28 +3166,60 @@ def get_local_driver(
msg = "chromedriver update needed. Getting it now:"
if not path_chromedriver:
msg = "chromedriver not found. Getting it now:"
+ if use_uc and not os.path.exists(LOCAL_UC_DRIVER):
+ msg = "uc_driver not found. Getting it now:"
+ if use_uc and os.path.exists(LOCAL_UC_DRIVER):
+ msg = "uc_driver update needed. Getting it now:"
+ force_uc = False
intel_for_uc = False
- if (
- IS_ARM_MAC
- and is_using_uc(undetectable, browser_name)
- ):
+ if use_uc:
+ force_uc = True
+ if IS_ARM_MAC and use_uc:
intel_for_uc = True # Use Intel driver for UC Mode
+ if os.path.exists(LOCAL_CHROMEDRIVER):
+ try:
+ output = subprocess.check_output(
+ '"%s" --version' % LOCAL_CHROMEDRIVER,
+ shell=True,
+ )
+ if IS_WINDOWS:
+ output = output.decode("latin1")
+ else:
+ output = output.decode("utf-8")
+ full_ch_driver_version = output.split(" ")[1]
+ output = full_ch_driver_version.split(".")[0]
+ if int(output) >= 2:
+ ch_driver_version = output
+ except Exception:
+ pass
if (
(
- not is_using_uc(undetectable, browser_name)
+ not use_uc
and not os.path.exists(LOCAL_CHROMEDRIVER)
)
+ or (use_uc and not os.path.exists(LOCAL_UC_DRIVER))
or (
- is_using_uc(undetectable, browser_name)
- and not os.path.exists(LOCAL_UC_DRIVER)
+ not use_uc
+ and (
+ use_version.split(".")[0]
+ != ch_driver_version
+ )
+ )
+ or (
+ use_uc
+ and (
+ use_version.split(".")[0]
+ != get_uc_driver_version()
+ )
)
):
- print("\nWarning: %s" % msg)
+ log_d("\nWarning: %s" % msg)
sys_args = sys.argv # Save a copy of sys args
try:
sb_install.main(
override="chromedriver %s" % use_version,
intel_for_uc=intel_for_uc,
+ force_uc=force_uc,
)
except Exception:
d_latest = get_latest_chromedriver_version()
@@ -3150,20 +3256,8 @@ def get_local_driver(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
with uc_lock: # Avoid multithreaded issues
- uc_driver_version = get_uc_driver_version()
- use_version = find_chromedriver_version_to_use(
- use_version, driver_version
- )
- if (
- (
- uc_driver_version != use_version
- and use_version != "latest"
- )
- or not os.path.exists(LOCAL_UC_DRIVER)
- ):
- if IS_ARM_MAC and os.path.exists(LOCAL_UC_DRIVER):
- pass # Already taken care of in sb_install
- elif os.path.exists(LOCAL_CHROMEDRIVER):
+ if make_uc_driver_from_chromedriver:
+ if os.path.exists(LOCAL_CHROMEDRIVER):
shutil.copyfile(
LOCAL_CHROMEDRIVER, LOCAL_UC_DRIVER
)
diff --git a/seleniumbase/core/settings_parser.py b/seleniumbase/core/settings_parser.py
index 80f92430aca..c45a20e748b 100644
--- a/seleniumbase/core/settings_parser.py
+++ b/seleniumbase/core/settings_parser.py
@@ -121,6 +121,8 @@ def set_settings(settings_file):
settings.HEADLESS_START_WIDTH = override_settings[key]
elif key == "HEADLESS_START_HEIGHT":
settings.HEADLESS_START_HEIGHT = override_settings[key]
+ elif key == "HIDE_DRIVER_DOWNLOADS":
+ settings.HIDE_DRIVER_DOWNLOADS = override_settings[key]
elif key == "MASTERQA_DEFAULT_VALIDATION_MESSAGE":
settings.MASTERQA_DEFAULT_VALIDATION_MESSAGE = override_settings[
key
diff --git a/seleniumbase/undetected/__init__.py b/seleniumbase/undetected/__init__.py
index 1ac9d3e746b..dbd6b944ea9 100644
--- a/seleniumbase/undetected/__init__.py
+++ b/seleniumbase/undetected/__init__.py
@@ -13,8 +13,6 @@
from .cdp import PageElement
from .dprocess import start_detached
from .options import ChromeOptions
-from .patcher import IS_POSIX
-from .patcher import Patcher
from .reactor import Reactor
from .webelement import WebElement
@@ -26,6 +24,7 @@
"CDP",
"find_chrome_executable",
)
+IS_POSIX = sys.platform.startswith(("darwin", "cygwin", "linux"))
logger = logging.getLogger("uc")
logger.setLevel(logging.getLogger().getEffectiveLevel())
@@ -114,14 +113,14 @@ def __init__(
"""
self.debug = debug
self.patcher = None
+ import fasteners
+ from seleniumbase.fixtures import constants
if patch_driver:
- import fasteners
- from seleniumbase.fixtures import constants
-
uc_lock = fasteners.InterProcessLock(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
with uc_lock:
+ from .patcher import Patcher
self.patcher = Patcher(
executable_path=driver_executable_path,
force=patcher_force_close,
@@ -268,50 +267,54 @@ def __init__(
if "win32" in sys.platform:
creationflags = subprocess.CREATE_NO_WINDOW
self.options = options
- if not use_subprocess:
- self.browser_pid = start_detached(
- options.binary_location, *options.arguments
- )
- else:
- browser = subprocess.Popen(
- [options.binary_location, *options.arguments],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- close_fds=IS_POSIX,
- creationflags=creationflags,
- )
- self.browser_pid = browser.pid
- service_ = None
- if patch_driver:
- service_ = selenium.webdriver.chrome.service.Service(
- executable_path=self.patcher.executable_path,
- service_args=["--disable-build-check"],
- port=port,
- log_output=os.devnull,
- )
- else:
- service_ = selenium.webdriver.chrome.service.Service(
- executable_path=driver_executable_path,
- service_args=["--disable-build-check"],
- port=port,
- log_output=os.devnull,
- )
- if hasattr(service_, "creationflags"):
- setattr(service_, "creationflags", creationflags)
- if hasattr(service_, "creation_flags"):
- setattr(service_, "creation_flags", creationflags)
- super().__init__(options=options, service=service_)
- self.reactor = None
- if enable_cdp_events:
- if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
- logging.getLogger(
- "selenium.webdriver.remote.remote_connection"
- ).setLevel(20)
- reactor = Reactor(self)
- reactor.start()
- self.reactor = reactor
- self._web_element_cls = WebElement
+ uc_lock = fasteners.InterProcessLock(
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
+ )
+ with uc_lock:
+ if not use_subprocess:
+ self.browser_pid = start_detached(
+ options.binary_location, *options.arguments
+ )
+ else:
+ browser = subprocess.Popen(
+ [options.binary_location, *options.arguments],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ close_fds=IS_POSIX,
+ creationflags=creationflags,
+ )
+ self.browser_pid = browser.pid
+ service_ = None
+ if patch_driver:
+ service_ = selenium.webdriver.chrome.service.Service(
+ executable_path=self.patcher.executable_path,
+ service_args=["--disable-build-check"],
+ port=port,
+ log_output=os.devnull,
+ )
+ else:
+ service_ = selenium.webdriver.chrome.service.Service(
+ executable_path=driver_executable_path,
+ service_args=["--disable-build-check"],
+ port=port,
+ log_output=os.devnull,
+ )
+ if hasattr(service_, "creationflags"):
+ setattr(service_, "creationflags", creationflags)
+ if hasattr(service_, "creation_flags"):
+ setattr(service_, "creation_flags", creationflags)
+ super().__init__(options=options, service=service_)
+ self.reactor = None
+ if enable_cdp_events:
+ if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
+ logging.getLogger(
+ "selenium.webdriver.remote.remote_connection"
+ ).setLevel(20)
+ reactor = Reactor(self)
+ reactor.start()
+ self.reactor = reactor
+ self._web_element_cls = WebElement
def __getattribute__(self, item):
if not super().__getattribute__("debug"):
diff --git a/seleniumbase/undetected/patcher.py b/seleniumbase/undetected/patcher.py
index cc25084d67b..8393d1f4a01 100644
--- a/seleniumbase/undetected/patcher.py
+++ b/seleniumbase/undetected/patcher.py
@@ -35,7 +35,10 @@ class Patcher(object):
# downloads_folder = "~/Library/Application Support/undetected_drivers"
downloads_folder = download_helper.get_downloads_folder()
if not os.path.exists(downloads_folder):
- os.makedirs(downloads_folder)
+ try:
+ os.makedirs(downloads_folder, exist_ok=True)
+ except Exception:
+ pass # Only possible during multithreaded tests
data_path = os.path.abspath(os.path.expanduser(downloads_folder))
def __init__(self, executable_path=None, force=False, version_main=0):
@@ -50,7 +53,10 @@ def __init__(self, executable_path=None, force=False, version_main=0):
self.executable_path = None
prefix = "undetected"
if not os.path.exists(self.data_path):
- os.makedirs(self.data_path, exist_ok=True)
+ try:
+ os.makedirs(self.data_path, exist_ok=True)
+ except Exception:
+ pass
if not executable_path:
self.executable_path = os.path.join(
self.data_path, "_".join([prefix, self.exe_name])
@@ -137,7 +143,10 @@ def unzip_package(self, fp):
os.unlink(self.zip_path)
except (FileNotFoundError, OSError):
pass
- os.makedirs(self.zip_path, mode=0o755, exist_ok=True)
+ try:
+ os.makedirs(self.zip_path, mode=0o755, exist_ok=True)
+ except Exception:
+ pass
with zipfile.ZipFile(fp, mode="r") as zf:
zf.extract(self.exe_name, self.zip_path)
try:
diff --git a/setup.py b/setup.py
index ea7e2e2d5a9..d5165f8b520 100755
--- a/setup.py
+++ b/setup.py
@@ -145,7 +145,8 @@
'attrs>=23.1.0;python_version>="3.7"',
"certifi>=2023.7.22",
'filelock>=3.4.1;python_version<"3.7"',
- 'filelock>=3.12.2;python_version>="3.7"',
+ 'filelock>=3.12.2;python_version>="3.7" and python_version<"3.8"',
+ 'filelock>=3.12.3;python_version>="3.8"',
'platformdirs>=2.4.0;python_version<"3.7"',
'platformdirs>=3.10.0;python_version>="3.7"',
'parse>=1.19.1',
@@ -181,7 +182,8 @@
'iniconfig==1.1.1;python_version<"3.7"',
'iniconfig==2.0.0;python_version>="3.7"',
'pluggy==1.0.0;python_version<"3.7"',
- 'pluggy==1.2.0;python_version>="3.7"',
+ 'pluggy==1.2.0;python_version>="3.7" and python_version<"3.8"',
+ 'pluggy==1.3.0;python_version>="3.8"',
"py==1.11.0",
'pytest==7.0.1;python_version<"3.7"',
'pytest==7.4.0;python_version>="3.7"',