You've already forked SeleniumHQ.selenium.py
Passing options to Selenium Manager (#11866)
NOKEYCHECK=True GitOrigin-RevId: 6be659b6cf99846db616ba8ef17cf4f413692030
This commit is contained in:

committed by
Copybara-Service

parent
69a949951c
commit
8707082014
selenium/webdriver
chrome
common
edge
firefox
ie
webkitgtk
wpewebkit
test/selenium/webdriver/common
@@ -18,6 +18,7 @@ import warnings
|
||||
|
||||
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 @@ class WebDriver(ChromiumDriver):
|
||||
)
|
||||
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 @@ class WebDriver(ChromiumDriver):
|
||||
service,
|
||||
keep_alive,
|
||||
)
|
||||
|
||||
def create_options(self) -> Options:
|
||||
return Options()
|
||||
|
45
selenium/webdriver/common/driver_finder.py
Normal file
45
selenium/webdriver/common/driver_finder.py
Normal file
@@ -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
|
@@ -22,6 +22,7 @@ from pathlib import Path
|
||||
from typing import Tuple
|
||||
|
||||
from selenium.common.exceptions import SeleniumManagerException
|
||||
from selenium.webdriver.common.options import BaseOptions
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -61,7 +62,7 @@ class SeleniumManager:
|
||||
|
||||
return path
|
||||
|
||||
def driver_location(self, browser: str) -> str:
|
||||
def driver_location(self, options: BaseOptions) -> str:
|
||||
"""
|
||||
Determines the path of the correct driver.
|
||||
:Args:
|
||||
@@ -69,10 +70,13 @@ class SeleniumManager:
|
||||
:Returns: The driver path to use
|
||||
"""
|
||||
|
||||
browser = options.capabilities["browserName"]
|
||||
|
||||
allowed_browsers = {
|
||||
"chrome": "chrome",
|
||||
"firefox": "firefox",
|
||||
"edge": "edge",
|
||||
"MicrosoftEdge": "edge",
|
||||
"ie": "iexplorer",
|
||||
}
|
||||
|
||||
|
@@ -31,7 +31,6 @@ from urllib.error import URLError
|
||||
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 @@ class Service(ABC):
|
||||
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 @@ class Service(ABC):
|
||||
"""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 @@ class Service(ABC):
|
||||
- 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 @@ class Service(ABC):
|
||||
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 @@ class Service(ABC):
|
||||
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)}"
|
||||
)
|
||||
|
@@ -18,6 +18,7 @@ import warnings
|
||||
|
||||
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 @@ class WebDriver(ChromiumDriver):
|
||||
"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"],
|
||||
|
@@ -24,6 +24,7 @@ from io import BytesIO
|
||||
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 @@ class WebDriver(RemoteWebDriver):
|
||||
|
||||
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(
|
||||
|
@@ -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 @@ class WebDriver(RemoteWebDriver):
|
||||
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)
|
||||
|
@@ -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 @@ class WebDriver(RemoteWebDriver):
|
||||
- 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 @@ class WebDriver(RemoteWebDriver):
|
||||
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__(
|
||||
|
@@ -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 @@ class WebDriver(RemoteWebDriver):
|
||||
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)
|
||||
|
@@ -18,13 +18,16 @@
|
||||
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_stderr_is_propagated_to_exception_messages():
|
||||
|
Reference in New Issue
Block a user