[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1035334: unblock: python-selenium/4.9.0+dfsg-1



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
X-Debbugs-Cc: python-selenium@packages.debian.org
Control: affects -1 + src:python-selenium

Please unblock package python-selenium

[ Reason ]
Upstream released another round of update to address some minor updates
and fixes of the 4.x series.
>From the CHANGES files:
* Remove "shadow_root" assertion in Python bindings for Firefox (#11821)
* Simplify driver binary and driver location selecting (#11864)
* Do not pass desired caps in Safari (#11854)
* Selenium Manager get Browser Version from Options classes
* Selenium Manager use binary from Browser Options
* Adding v112 and removing v109

[ Impact ]
Users of bookworm won't get the latest fixes and adjustments, might
result in non usable functionality.

[ Tests ]
The autopkgtest succeeded and so far no bug report nor private email
communication did need to happen.
Using the new version in some of my personal scripts did not show any
problems or regressions.

[ Risks ]
I'd say the risk of introducing problems is quite zero as basically the
difference to the previous version isn't big and straight forward from
reviewing the code changes.
OTOH python-selenium is marked as key package and requires a manual
unblock due this.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]
A mostly better viewable diff of the upstream changes between 4.8.3 and
4.9.0 can seen on the Salsa upstream tree.

https://salsa.debian.org/sagiru-guest/python-selenium/-/commit/02f43bac627b47adb42fac57011e2e578797cdab

unblock python-selenium/4.9.0+dfsg-1
diff -Nru python-selenium-4.8.3+dfsg/CHANGES python-selenium-4.9.0+dfsg/CHANGES
--- python-selenium-4.8.3+dfsg/CHANGES	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/CHANGES	2023-04-21 01:41:21.000000000 +0200
@@ -1,3 +1,11 @@
+Selenium 4.9.0
+* Remove "shadow_root" assertion in Python bindings for Firefox (#11821)
+* Simplify driver binary and driver location selecting (#11864)
+* Do not pass desired caps in Safari (#11854)
+* Selenium Manager get Browser Version from Options classes
+* Selenium Manager use binary from Browser Options
+* Adding v112 and removing v109
+
 Selenium 4.8.3
 * Add fine grained control for arguments provided to service subprocesses by passing a `popen_kw` mapping for all services.
 * `Options` classes now allow `timeout` to be set partially and no longer raise an exception when all values are not provided. (#11623)
diff -Nru python-selenium-4.8.3+dfsg/debian/changelog python-selenium-4.9.0+dfsg/debian/changelog
--- python-selenium-4.8.3+dfsg/debian/changelog	2023-03-29 12:14:56.000000000 +0200
+++ python-selenium-4.9.0+dfsg/debian/changelog	2023-04-22 13:02:57.000000000 +0200
@@ -1,3 +1,12 @@
+python-selenium (4.9.0+dfsg-1) unstable; urgency=medium
+
+  * [02f43ba] New upstream version 4.9.0+dfsg
+  * [e4e1dcd] Use a not so usual port for chromium test
+    Use port 8088 instead of 8000, the port 8000 might be in use already by
+    some other process.
+
+ -- Carsten Schoenert <c.schoenert@t-online.de>  Sat, 22 Apr 2023 13:02:57 +0200
+
 python-selenium (4.8.3+dfsg-1) unstable; urgency=medium
 
   * [9118276] New upstream version 4.8.3+dfsg
diff -Nru python-selenium-4.8.3+dfsg/debian/tests/test-chromium python-selenium-4.9.0+dfsg/debian/tests/test-chromium
--- python-selenium-4.8.3+dfsg/debian/tests/test-chromium	2023-03-29 12:14:56.000000000 +0200
+++ python-selenium-4.9.0+dfsg/debian/tests/test-chromium	2023-04-22 13:02:57.000000000 +0200
@@ -2,7 +2,7 @@
 
 set -exu
 
-python3 -m http.server 8000 --bind 127.0.0.1 --directory="$(pwd)" &
+python3 -m http.server 8088 --bind 127.0.0.1 --directory="$(pwd)" &
 pid=$!
 trap "kill $pid" EXIT
 
@@ -23,23 +23,26 @@
 
 driver = webdriver.Chrome(options = chrome_options)
 
-print("Getting data from http://127.0.0.1:8000";)
+print("Getting data from http://127.0.0.1:8088";)
 
-if driver.get("http://127.0.0.1:8000";) == None:
+if driver.get("http://127.0.0.1:8088";) == None:
     print("Success.")
 else:
     print("Failed!")
+    sys.exit(1)
 
 print("Looking for a link named 'debian/'")
 link = driver.find_element(By.LINK_TEXT, "debian/")
 
 if link.click() == None:
     print("Success.")
+    print()
+    print("\nTest seems to be successful!\nTest was using the following HTML data to test the Chrome webdriver.\n")
+    print("------------------------------- %< -------------------------------")
+    print(driver.page_source)
+    print("------------------------------- >% -------------------------------")
 else:
     print("Failed!")
+    sys.exit(1)
 
-print("\nTest seems to be successful!\nTest was using the following HTML data to test the Chrome webdriver.\n")
-print("------------------------------- %< -------------------------------")
-print(driver.page_source)
-print("------------------------------- >% -------------------------------")
 END
diff -Nru python-selenium-4.8.3+dfsg/docs/source/conf.py python-selenium-4.9.0+dfsg/docs/source/conf.py
--- python-selenium-4.8.3+dfsg/docs/source/conf.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/docs/source/conf.py	2023-04-21 01:41:21.000000000 +0200
@@ -56,7 +56,7 @@
 # built documents.
 #
 # The short X.Y version.
-version = '4.8'
+version = '4.9'
 # The full version, including alpha/beta/rc tags.
 release = version
 
diff -Nru python-selenium-4.8.3+dfsg/docs/source/index.rst python-selenium-4.9.0+dfsg/docs/source/index.rst
--- python-selenium-4.8.3+dfsg/docs/source/index.rst	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/docs/source/index.rst	2023-04-21 01:41:21.000000000 +0200
@@ -35,7 +35,7 @@
 
     pip install -U selenium
 
-Alternately, you can download the source distribution from `PyPI <https://pypi.org/project/selenium/#files>`_ (e.g. selenium-4.8.3.tar.gz), unarchive it, and run::
+Alternately, you can download the source distribution from `PyPI <https://pypi.org/project/selenium/#files>`_ (e.g. selenium-4.9.0.tar.gz), unarchive it, and run::
 
     python setup.py install
 
diff -Nru python-selenium-4.8.3+dfsg/python.iml python-selenium-4.9.0+dfsg/python.iml
--- python-selenium-4.8.3+dfsg/python.iml	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/python.iml	2023-04-21 01:41:21.000000000 +0200
@@ -5,13 +5,17 @@
       <configuration sdkName="" />
     </facet>
   </component>
+  <component name="ModuleRunConfigurationManager">
+    <shared />
+  </component>
   <component name="NewModuleRootManager" inherit-compiler-output="true">
     <exclude-output />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/selenium" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/venv" />
     </content>
-    <orderEntry type="jdk" jdkName="Python 2.7" jdkType="Python SDK" />
+    <orderEntry type="jdk" jdkName="Python 3.9 (py)" jdkType="Python SDK" />
     <orderEntry type="sourceFolder" forTests="false" />
   </component>
   <component name="sonarModuleSettings">
diff -Nru python-selenium-4.8.3+dfsg/README.md python-selenium-4.9.0+dfsg/README.md
--- python-selenium-4.8.3+dfsg/README.md	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/README.md	2023-04-21 01:41:21.000000000 +0200
@@ -298,6 +298,16 @@
 
 </details>
 
+#### Rust
+<details>
+<summary>Click to see Rust Build Steps</summary>
+
+Targets:
+
+| Command                               | Description                         |
+|---------------------------------------|-------------------------------------|
+| `bazel build //rust:selenium-manager` | Build selenium-manager binary       |
+| `bazel test //rust/...`               | Run both unit and integration tests |
 
 ### Build Details
 
diff -Nru python-selenium-4.8.3+dfsg/README.rst python-selenium-4.9.0+dfsg/README.rst
--- python-selenium-4.8.3+dfsg/README.rst	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/README.rst	2023-04-21 01:41:21.000000000 +0200
@@ -35,7 +35,7 @@
 
     pip install -U selenium
 
-Alternately, you can download the source distribution from `PyPI <https://pypi.org/project/selenium/#files>`_ (e.g. selenium-4.8.3.tar.gz), unarchive it, and run::
+Alternately, you can download the source distribution from `PyPI <https://pypi.org/project/selenium/#files>`_ (e.g. selenium-4.9.0.tar.gz), unarchive it, and run::
 
     python setup.py install
 
diff -Nru python-selenium-4.8.3+dfsg/requirements_lock.txt python-selenium-4.9.0+dfsg/requirements_lock.txt
--- python-selenium-4.8.3+dfsg/requirements_lock.txt	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/requirements_lock.txt	2023-04-21 01:41:21.000000000 +0200
@@ -1,6 +1,6 @@
 #
-# This file is autogenerated by pip-compile with python 3.8
-# To update, run:
+# This file is autogenerated by pip-compile with Python 3.8
+# by the following command:
 #
 #    bazel run //py:requirements.update
 #
@@ -129,6 +129,10 @@
     --hash=sha256:e3513399177dd37af4c1332df52da5da1d0c387e5927dc4c0709e26ee7302e8f \
     --hash=sha256:eb1946efac0c0c3d411cea0b5ac772fbde744109fd9520fb0c5a51979faf05ad
     # via -r py/requirements.txt
+exceptiongroup==1.1.1 \
+    --hash=sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e \
+    --hash=sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785
+    # via pytest
 h11==0.13.0 \
     --hash=sha256:70813c1135087a248a4d38cc0e1a0181ffab2188141a93eaf567940c3957ff06 \
     --hash=sha256:8ddd78563b633ca55346c8cd41ec0af27d3c79931828beffb46ce70a379e7442
@@ -242,9 +246,7 @@
 py==1.11.0 \
     --hash=sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719 \
     --hash=sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378
-    # via
-    #   -r py/requirements.txt
-    #   pytest
+    # via -r py/requirements.txt
 pycparser==2.21 \
     --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \
     --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206
@@ -268,9 +270,9 @@
     # via
     #   -r py/requirements.txt
     #   urllib3
-pytest==6.2.5 \
-    --hash=sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89 \
-    --hash=sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134
+pytest==7.2.0 \
+    --hash=sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71 \
+    --hash=sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59
     # via
     #   -r py/requirements.txt
     #   pytest-instafail
@@ -302,9 +304,11 @@
 toml==0.10.2 \
     --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \
     --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f
-    # via
-    #   -r py/requirements.txt
-    #   pytest
+    # via -r py/requirements.txt
+tomli==2.0.1 \
+    --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
+    --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
+    # via pytest
 trio==0.20.0 \
     --hash=sha256:670a52d3115d0e879e1ac838a4eb999af32f858163e3a704fe4839de2a676070 \
     --hash=sha256:fb2d48e4eab0dfb786a472cd514aaadc71e3445b203bc300bad93daa75d77c1a
diff -Nru python-selenium-4.8.3+dfsg/requirements.txt python-selenium-4.9.0+dfsg/requirements.txt
--- python-selenium-4.8.3+dfsg/requirements.txt	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/requirements.txt	2023-04-21 01:41:21.000000000 +0200
@@ -20,10 +20,10 @@
 pyOpenSSL==22.0.0
 pyparsing==3.0.8
 PySocks==1.7.1
-pytest==6.2.5
+pytest==7.2.0
 pytest-instafail==0.4.2
-pytest-mock==3.7.0
-pytest-trio==0.7.0
+pytest-mock==3.10.0
+pytest-trio==0.8.0
 sniffio==1.2.0
 sortedcontainers==2.4.0
 toml==0.10.2
diff -Nru python-selenium-4.8.3+dfsg/selenium/__init__.py python-selenium-4.9.0+dfsg/selenium/__init__.py
--- python-selenium-4.8.3+dfsg/selenium/__init__.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/__init__.py	2023-04-21 01:41:21.000000000 +0200
@@ -16,4 +16,4 @@
 # under the License.
 
 
-__version__ = "4.8.3"
+__version__ = "4.9.0"
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/chrome/webdriver.py python-selenium-4.9.0+dfsg/selenium/webdriver/chrome/webdriver.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/chrome/webdriver.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/chrome/webdriver.py	2023-04-21 01:41:21.000000000 +0200
@@ -18,6 +18,7 @@
 
 from selenium.webdriver.chromium.webdriver import ChromiumDriver
 from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+from selenium.webdriver.common.driver_finder import DriverFinder
 
 from .options import Options
 from .service import DEFAULT_EXECUTABLE_PATH
@@ -74,8 +75,11 @@
             )
         else:
             keep_alive = True
+        if not options:
+            options = self.create_options()
         if not service:
             service = Service(executable_path, port, service_args, service_log_path)
+        service.path = DriverFinder.get_path(service, options)
 
         super().__init__(
             DesiredCapabilities.CHROME["browserName"],
@@ -88,3 +92,6 @@
             service,
             keep_alive,
         )
+
+    def create_options(self) -> Options:
+        return Options()
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/common/driver_finder.py python-selenium-4.9.0+dfsg/selenium/webdriver/common/driver_finder.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/common/driver_finder.py	1970-01-01 01:00:00.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/common/driver_finder.py	2023-04-21 01:41:21.000000000 +0200
@@ -0,0 +1,45 @@
+# Licensed to the Software Freedom Conservancy (SFC) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The SFC licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import logging
+import shutil
+
+from selenium.common.exceptions import WebDriverException
+from selenium.webdriver.common.options import BaseOptions
+from selenium.webdriver.common.selenium_manager import SeleniumManager
+from selenium.webdriver.common.service import Service
+
+logger = logging.getLogger(__name__)
+
+
+class DriverFinder:
+    """Utility to find if a given file is present and executable.
+
+    This implementation is still in beta, and may change.
+    """
+
+    def __init__(self) -> None:
+        pass
+
+    @staticmethod
+    def get_path(service: Service, options: BaseOptions) -> str:
+        try:
+            path = shutil.which(service.path) or SeleniumManager().driver_location(options)
+        except WebDriverException as err:
+            logger.warning("Unable to obtain driver using Selenium Manager: " + err.msg)
+            raise err
+
+        return path
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/common/options.py python-selenium-4.9.0+dfsg/selenium/webdriver/common/options.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/common/options.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/common/options.py	2023-04-21 01:41:21.000000000 +0200
@@ -44,7 +44,7 @@
         """
         :returns: the version of the browser if set, otherwise None.
         """
-        return self._caps["browserVersion"]
+        return self._caps.get("browserVersion")
 
     @browser_version.setter
     def browser_version(self, version: str) -> None:
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/common/selenium_manager.py python-selenium-4.9.0+dfsg/selenium/webdriver/common/selenium_manager.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/common/selenium_manager.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/common/selenium_manager.py	2023-04-21 01:41:21.000000000 +0200
@@ -19,9 +19,10 @@
 import subprocess
 import sys
 from pathlib import Path
-from typing import Tuple
+from typing import List
 
 from selenium.common.exceptions import SeleniumManagerException
+from selenium.webdriver.common.options import BaseOptions
 
 logger = logging.getLogger(__name__)
 
@@ -41,11 +42,15 @@
 
         :Returns: The Selenium Manager executable location
         """
-        directory = sys.platform
-        if directory == "darwin":
-            directory = "macos"
-        elif directory in ("win32", "cygwin"):
-            directory = "windows"
+        platform = sys.platform
+
+        dirs = {
+            "darwin": "macos",
+            "win32": "windows",
+            "cygwin": "windows",
+        }
+
+        directory = dirs.get(platform) if dirs.get(platform) else platform
 
         file = "selenium-manager.exe" if directory == "windows" else "selenium-manager"
 
@@ -57,34 +62,49 @@
 
         return path
 
-    def driver_location(self, browser: str) -> str:
+    def driver_location(self, options: BaseOptions) -> str:
         """
         Determines the path of the correct driver.
         :Args:
          - browser: which browser to get the driver path for.
         :Returns: The driver path to use
         """
-        allowed = ("chrome", "firefox", "edge", "ie")
-        if browser not in allowed:
-            raise SeleniumManagerException(f"{browser} is not a valid browser.  Choose one of: {allowed}")
-
-        if browser == "ie":
-            browser = "iexplorer"
-
-        binary, browser_flag, browser, output_flag, output = (
-            str(self.get_binary()),
-            "--browser",
-            browser,
-            "--output",
-            "json",
-        )
-        result = self.run((binary, browser_flag, browser, output_flag, output))
+
+        browser = options.capabilities["browserName"]
+
+        allowed_browsers = {
+            "chrome": "chrome",
+            "firefox": "firefox",
+            "edge": "edge",
+            "MicrosoftEdge": "edge",
+            "ie": "iexplorer",
+        }
+
+        if browser not in allowed_browsers.keys():
+            raise SeleniumManagerException(
+                f"{browser} is not a valid browser.  Choose one of: {list(allowed_browsers.keys())}"
+            )
+
+        browser = allowed_browsers[browser]
+
+        args = [str(self.get_binary()), "--browser", browser, "--output", "json"]
+
+        if options.browser_version:
+            args.append("--browser-version")
+            args.append(str(options.browser_version))
+
+        binary_location = getattr(options, "binary_location", None)
+        if binary_location:
+            args.append("--browser-path")
+            args.append(str(binary_location))
+
+        result = self.run(args)
         executable = result.split("\t")[-1].strip()
         logger.debug(f"Using driver at: {executable}")
         return executable
 
     @staticmethod
-    def run(args: Tuple[str, str, str, str, str]) -> str:
+    def run(args: List[str]) -> str:
         """
         Executes the Selenium Manager Binary.
         :Args:
@@ -92,16 +112,16 @@
         :Returns: The log string containing the driver location.
         """
         command = " ".join(args)
-        logger.debug(f"Executing: {command}")
+        logger.info(f"Executing: {command}")
         completed_proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
         stdout = completed_proc.stdout.decode("utf-8").rstrip("\n")
         stderr = completed_proc.stderr.decode("utf-8").rstrip("\n")
         output = json.loads(stdout)
         result = output["result"]["message"]
         if completed_proc.returncode:
-            raise SeleniumManagerException(f"Selenium manager failed for: {command}.\n{result}{stderr}")
+            raise SeleniumManagerException(f"Selenium Manager failed for: {command}.\n{result}{stderr}")
         else:
-            # Selenium Manager exited 0 successfully, return executable path and print warnings (if any)
+            # Selenium Manager exited successfully, return executable path and print warnings
             for item in output["logs"]:
                 if item["level"] == "WARN":
                     logger.warning(item["message"])
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/common/service.py python-selenium-4.9.0+dfsg/selenium/webdriver/common/service.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/common/service.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/common/service.py	2023-04-21 01:41:21.000000000 +0200
@@ -31,7 +31,6 @@
 from selenium.common.exceptions import WebDriverException
 from selenium.types import SubprocessStdAlias
 from selenium.webdriver.common import utils
-from selenium.webdriver.common.selenium_manager import SeleniumManager
 
 logger = logging.getLogger(__name__)
 
@@ -60,7 +59,7 @@
         start_error_message: typing.Optional[str] = None,
         **kwargs,
     ) -> None:
-        self.path = executable
+        self._path = executable
         self.port = port or utils.free_port()
         self.log_file = open(os.devnull, "wb") if not _HAS_NATIVE_DEVNULL and log_file == DEVNULL else log_file
         self.start_error_message = start_error_message or ""
@@ -79,6 +78,14 @@
         """A List of program arguments (excluding the executable)."""
         raise NotImplementedError("This method needs to be implemented in a sub class")
 
+    @property
+    def path(self) -> str:
+        return self._path
+
+    @path.setter
+    def path(self, value: str) -> None:
+        self._path = str(value)
+
     def start(self) -> None:
         """Starts the Service.
 
@@ -86,20 +93,7 @@
          - WebDriverException : Raised either when it can't start the service
            or when it can't connect to the service
         """
-        try:
-            self._start_process(self.path)
-        except WebDriverException as err:
-            if "executable needs to be in PATH" in err.msg:
-                logger.debug("driver not found in PATH, trying Selenium Manager")
-                browser = self.__class__.__module__.split(".")[-2]
-
-                try:
-                    path = SeleniumManager().driver_location(browser)
-                except WebDriverException as new_err:
-                    logger.debug("Unable to obtain driver using Selenium Manager: " + new_err.msg)
-                    raise err
-
-                self._start_process(path)
+        self._start_process(self._path)
 
         count = 0
         while True:
@@ -110,13 +104,13 @@
             count += 1
             sleep(0.5)
             if count == 60:
-                raise WebDriverException(f"Can not connect to the Service {self.path}")
+                raise WebDriverException(f"Can not connect to the Service {self._path}")
 
     def assert_process_still_running(self) -> None:
         """Check if the underlying process is still running."""
         return_code = self.process.poll()
         if return_code:
-            raise WebDriverException(f"Service {self.path} unexpectedly exited. Status code was: {return_code}")
+            raise WebDriverException(f"Service {self._path} unexpectedly exited. Status code was: {return_code}")
 
     def is_connectable(self) -> bool:
         """Establishes a socket connection to determine if the service running
@@ -210,20 +204,20 @@
                 creationflags=self.creation_flags,
                 **self.popen_kw,
             )
-            logger.debug(f"Started executable: `{self.path}` in a child process with pid: {self.process.pid}")
+            logger.debug(f"Started executable: `{self._path}` in a child process with pid: {self.process.pid}")
         except TypeError:
             raise
         except OSError as err:
             if err.errno == errno.ENOENT:
                 raise WebDriverException(
-                    f"'{os.path.basename(self.path)}' executable needs to be in PATH. {self.start_error_message}"
+                    f"'{os.path.basename(self._path)}' executable needs to be in PATH. {self.start_error_message}"
                 )
             if err.errno == errno.EACCES:
                 raise WebDriverException(
-                    f"'{os.path.basename(self.path)}' executable may have wrong permissions. {self.start_error_message}"
+                    f"'{os.path.basename(self._path)}' executable may have wrong permissions. {self.start_error_message}"
                 )
             raise
         except Exception as e:
             raise WebDriverException(
-                f"The executable {os.path.basename(self.path)} needs to be available in the path. {self.start_error_message}\n{str(e)}"
+                f"The executable {os.path.basename(self._path)} needs to be available in the path. {self.start_error_message}\n{str(e)}"
             )
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/edge/webdriver.py python-selenium-4.9.0+dfsg/selenium/webdriver/edge/webdriver.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/edge/webdriver.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/edge/webdriver.py	2023-04-21 01:41:21.000000000 +0200
@@ -18,6 +18,7 @@
 
 from selenium.webdriver.chromium.webdriver import ChromiumDriver
 from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+from selenium.webdriver.common.driver_finder import DriverFinder
 
 from .options import Options
 from .service import DEFAULT_EXECUTABLE_PATH
@@ -66,8 +67,11 @@
                 "executable_path has been deprecated, please pass in a Service object", DeprecationWarning, stacklevel=2
             )
 
+        if not options:
+            options = self.create_options()
         if not service:
             service = Service(executable_path, port, service_args, service_log_path)
+        service.path = DriverFinder.get_path(service, options)
 
         super().__init__(
             DesiredCapabilities.EDGE["browserName"],
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/firefox/webdriver.py python-selenium-4.9.0+dfsg/selenium/webdriver/firefox/webdriver.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/firefox/webdriver.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/firefox/webdriver.py	2023-04-21 01:41:21.000000000 +0200
@@ -24,6 +24,7 @@
 from shutil import rmtree
 
 from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+from selenium.webdriver.common.driver_finder import DriverFinder
 from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
 
 from .firefox_binary import FirefoxBinary
@@ -191,6 +192,7 @@
 
         if not self.service:
             self.service = Service(executable_path, service_args=service_args, log_path=service_log_path)
+        self.service.path = DriverFinder.get_path(self.service, options)
         self.service.start()
 
         executor = FirefoxRemoteConnection(
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/ie/webdriver.py python-selenium-4.9.0+dfsg/selenium/webdriver/ie/webdriver.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/ie/webdriver.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/ie/webdriver.py	2023-04-21 01:41:21.000000000 +0200
@@ -18,6 +18,7 @@
 import warnings
 
 from selenium.webdriver.common import utils
+from selenium.webdriver.common.driver_finder import DriverFinder
 from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
 
 from .options import Options
@@ -123,6 +124,7 @@
                 executable_path, port=self.port, host=self.host, log_level=log_level, log_file=service_log_path
             )
 
+        self.iedriver.path = DriverFinder.get_path(self.iedriver, options)
         self.iedriver.start()
 
         super().__init__(command_executor=self.iedriver.service_url, options=options, keep_alive=keep_alive)
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/__init__.py python-selenium-4.9.0+dfsg/selenium/webdriver/__init__.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/__init__.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/__init__.py	2023-04-21 01:41:21.000000000 +0200
@@ -36,7 +36,7 @@
 from .wpewebkit.options import Options as WPEWebKitOptions  # noqa
 from .wpewebkit.webdriver import WebDriver as WPEWebKit  # noqa
 
-__version__ = "4.8.3"
+__version__ = "4.9.0"
 
 # We need an explicit __all__ because the above won't otherwise be exported.
 __all__ = [
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/remote/webelement.py python-selenium-4.9.0+dfsg/selenium/webdriver/remote/webelement.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/remote/webelement.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/remote/webelement.py	2023-04-21 01:41:21.000000000 +0200
@@ -235,21 +235,20 @@
     @property
     def shadow_root(self) -> ShadowRoot:
         """Returns a shadow root of the element if there is one or an error.
-        Only works from Chromium 96 onwards. Previous versions of Chromium
-        based browsers will throw an assertion exception.
+        Only works from Chromium 96 and Firefox 96 onwards. Previous versions
+        of browsers will throw an assertion exception.
 
         :Returns:
           - ShadowRoot object or
           - NoSuchShadowRoot - if no shadow root was attached to element
         """
         browser_main_version = int(self._parent.caps["browserVersion"].split(".")[0])
-        assert self._parent.caps["browserName"].lower() not in [
-            "firefox",
-            "safari",
-        ], "This only currently works in Chromium based browsers"
+        assert (
+            self._parent.caps["browserName"].lower() != "safari"
+        ), "This only currently works in Firefox and Chromium based browsers"
         assert (
             browser_main_version > 95
-        ), f"Please use Chromium based browsers with version 96 or later. Version used {self._parent.caps['browserVersion']}"
+        ), f"Please use Firefox or Chromium based browsers with version 96 or later. Version used {self._parent.caps['browserVersion']}"
         return self._execute(Command.GET_SHADOW_ROOT)["value"]
 
     # RenderedWebElement Items
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/safari/webdriver.py python-selenium-4.9.0+dfsg/selenium/webdriver/safari/webdriver.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/safari/webdriver.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/safari/webdriver.py	2023-04-21 01:41:21.000000000 +0200
@@ -107,7 +107,7 @@
 
         executor = SafariRemoteConnection(remote_server_addr=self.service.service_url, keep_alive=keep_alive)
 
-        super().__init__(command_executor=executor, options=options, desired_capabilities=desired_capabilities)
+        super().__init__(command_executor=executor, options=options)
 
         self._is_remote = False
 
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/webkitgtk/webdriver.py python-selenium-4.9.0+dfsg/selenium/webdriver/webkitgtk/webdriver.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/webkitgtk/webdriver.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/webkitgtk/webdriver.py	2023-04-21 01:41:21.000000000 +0200
@@ -17,6 +17,7 @@
 
 import http.client as http_client
 
+from selenium.webdriver.common.driver_finder import DriverFinder
 from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
 
 from .options import Options
@@ -49,8 +50,9 @@
          - keep_alive : Whether to configure RemoteConnection to use HTTP keep-alive.
         """
         if not options:
+            options = Options()
             if not desired_capabilities:
-                desired_capabilities = Options().to_capabilities()
+                desired_capabilities = options.to_capabilities()
         else:
             capabilities = options.to_capabilities()
             if desired_capabilities:
@@ -58,6 +60,7 @@
             desired_capabilities = capabilities
 
         self.service = Service(executable_path, port=port, log_path=service_log_path)
+        self.service.path = DriverFinder.get_path(self.service, options)
         self.service.start()
 
         super().__init__(
diff -Nru python-selenium-4.8.3+dfsg/selenium/webdriver/wpewebkit/webdriver.py python-selenium-4.9.0+dfsg/selenium/webdriver/wpewebkit/webdriver.py
--- python-selenium-4.8.3+dfsg/selenium/webdriver/wpewebkit/webdriver.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/selenium/webdriver/wpewebkit/webdriver.py	2023-04-21 01:41:21.000000000 +0200
@@ -18,8 +18,10 @@
 import http.client as http_client
 
 from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+from selenium.webdriver.common.driver_finder import DriverFinder
 from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
 
+from .options import Options
 from .service import DEFAULT_EXECUTABLE_PATH
 from .service import Service
 
@@ -50,8 +52,11 @@
             capabilities = options.to_capabilities()
             capabilities.update(desired_capabilities)
             desired_capabilities = capabilities
+        else:
+            options = Options()
 
         self.service = Service(executable_path, port=port, log_path=service_log_path)
+        self.service.path = DriverFinder.get_path(self.service, options)
         self.service.start()
 
         super().__init__(command_executor=self.service.service_url, desired_capabilities=desired_capabilities)
diff -Nru python-selenium-4.8.3+dfsg/setup.py python-selenium-4.9.0+dfsg/setup.py
--- python-selenium-4.8.3+dfsg/setup.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/setup.py	2023-04-21 01:41:21.000000000 +0200
@@ -27,7 +27,7 @@
 setup_args = {
     'cmdclass': {'install': install},
     'name': 'selenium',
-    'version': "4.8.3",
+    'version': "4.9.0",
     'license': 'Apache 2.0',
     'description': 'Python bindings for Selenium',
     'long_description': open(join(abspath(dirname(__file__)), "README.rst")).read(),
diff -Nru python-selenium-4.8.3+dfsg/test/selenium/webdriver/common/selenium_manager_tests.py python-selenium-4.9.0+dfsg/test/selenium/webdriver/common/selenium_manager_tests.py
--- python-selenium-4.8.3+dfsg/test/selenium/webdriver/common/selenium_manager_tests.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/test/selenium/webdriver/common/selenium_manager_tests.py	2023-04-21 01:41:21.000000000 +0200
@@ -15,21 +15,64 @@
 #  specific language governing permissions and limitations
 #  under the License.
 
+from unittest.mock import Mock
+
 import pytest
 
 from selenium.common.exceptions import SeleniumManagerException
+from selenium.webdriver.chrome.options import Options
 from selenium.webdriver.common.selenium_manager import SeleniumManager
 
 
 def test_non_supported_browser_raises_sme():
-    msg = r"foo is not a valid browser.  Choose one of: \('chrome', 'firefox', 'edge', 'ie'\)"
+    msg = r"foo is not a valid browser.  Choose one of: \['chrome', 'firefox', 'edge', 'MicrosoftEdge', 'ie'\]"
     with pytest.raises(SeleniumManagerException, match=msg):
-        _ = SeleniumManager().driver_location("foo")
+        options = Options()
+        options.capabilities["browserName"] = "foo"
+        _ = SeleniumManager().driver_location(options)
+
+
+def test_browser_version_is_used_for_sm(mocker):
+    import subprocess
+
+    mock_run = mocker.patch("subprocess.run")
+    mocked_result = Mock()
+    mocked_result.configure_mock(
+        **{"stdout.decode.return_value": '{"result": {"message": "driver"}, "logs": []}', "returncode": 0}
+    )
+    mock_run.return_value = mocked_result
+    options = Options()
+    options.capabilities["browserName"] = "chrome"
+    options.browser_version = 110
+
+    _ = SeleniumManager().driver_location(options)
+    args, kwargs = subprocess.run.call_args
+    assert "--browser-version" in args[0]
+    assert "110" in args[0]
+
+
+def test_browser_path_is_used_for_sm(mocker):
+    import subprocess
+
+    mock_run = mocker.patch("subprocess.run")
+    mocked_result = Mock()
+    mocked_result.configure_mock(
+        **{"stdout.decode.return_value": '{"result": {"message": "driver"}, "logs": []}', "returncode": 0}
+    )
+    mock_run.return_value = mocked_result
+    options = Options()
+    options.capabilities["browserName"] = "chrome"
+    options.binary_location = "/opt/bin/browser-bin"
+
+    _ = SeleniumManager().driver_location(options)
+    args, kwargs = subprocess.run.call_args
+    assert "--browser-path" in args[0]
+    assert "/opt/bin/browser-bin" in args[0]
 
 
 def test_stderr_is_propagated_to_exception_messages():
-    msg = r"Selenium manager failed for:.* --browser foo --output json\.\nInvalid browser name: foo\n"
+    msg = r"Selenium Manager failed for:.* --browser foo --output json\.\nInvalid browser name: foo\n"
     with pytest.raises(SeleniumManagerException, match=msg):
         manager = SeleniumManager()
         binary = manager.get_binary()
-        _ = manager.run((str(binary), "--browser", "foo", "--output", "json"))
+        _ = manager.run([str(binary), "--browser", "foo", "--output", "json"])
diff -Nru python-selenium-4.8.3+dfsg/test/selenium/webdriver/common/web_components_tests.py python-selenium-4.9.0+dfsg/test/selenium/webdriver/common/web_components_tests.py
--- python-selenium-4.8.3+dfsg/test/selenium/webdriver/common/web_components_tests.py	2023-03-24 19:05:50.000000000 +0100
+++ python-selenium-4.9.0+dfsg/test/selenium/webdriver/common/web_components_tests.py	2023-04-21 01:41:21.000000000 +0200
@@ -24,7 +24,6 @@
 
 
 @pytest.mark.xfail_safari
-@pytest.mark.xfail_firefox
 @pytest.mark.xfail_remote
 def test_can_get_the_shadow_root_of_an_element(driver, pages):
     pages.load("webComponents.html")
@@ -33,7 +32,6 @@
 
 
 @pytest.mark.xfail_safari
-@pytest.mark.xfail_firefox
 @pytest.mark.xfail_remote
 def test_no_such_shadow_root_thrown_when_no_shadow_root(driver, pages):
     with pytest.raises(NoSuchShadowRootException):
@@ -42,7 +40,6 @@
 
 
 @pytest.mark.xfail_safari
-@pytest.mark.xfail_firefox
 @pytest.mark.xfail_remote
 def test_returns_shadow_root_via_execute_script(driver, pages):
     pages.load("webComponents.html")

Reply to: