Skip to content

Commit 8b6dbb3

Browse files
authored
[py]: Propagate stderr to exceptions when selenium manager fails (#11329)
* [py]: Capture `stderr` when selenium manager goes wrong and some tests * [py]: Apply `linting` * [py]: tidy up `selenium manager` unit tests * [py]: Remove unused fixtures and improve naming of manager unit tests * [py] Raise `SeleniumManagerException` when binary cannot be found on disk * Delete py/selenium/webdriver/common/linux directory * [py]: Handle exceptions better in `service` classes from `selenium-manager` * [py]: Remove `selenium-manager` binary * [py]: simplify command and logging in selenium manager `.run()` * [py]: update `.gitignore` to prevent `selenium-manager` in python
1 parent 3584dad commit 8b6dbb3

File tree

6 files changed

+70
-20
lines changed

6 files changed

+70
-20
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ py/selenium/webdriver/remote/isDisplayed.js
7373
py/docs/build/
7474
py/build/
7575
py/LICENSE
76+
py/selenium/webdriver/common/linux/
77+
py/selenium/webdriver/common/windows/
78+
py/selenium/webdriver/common/macos/
7679
selenium.egg-info/
7780
third_party/java/jetty/jetty-repacked.jar
7881
*.user

py/selenium/common/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from .exceptions import NoSuchShadowRootException
4040
from .exceptions import NoSuchWindowException
4141
from .exceptions import ScreenshotException
42+
from .exceptions import SeleniumManagerException
4243
from .exceptions import SessionNotCreatedException
4344
from .exceptions import StaleElementReferenceException
4445
from .exceptions import TimeoutException
@@ -81,4 +82,5 @@
8182
"InvalidSessionIdException",
8283
"SessionNotCreatedException",
8384
"UnknownMethodException",
85+
"SeleniumManagerException",
8486
]

py/selenium/common/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,7 @@ class UnknownMethodException(WebDriverException):
298298
"""
299299
The requested command matched a known URL but did not match any methods for that URL.
300300
"""
301+
302+
303+
class SeleniumManagerException(WebDriverException):
304+
"""Raised when an issue interacting with selenium manager occurs."""

py/selenium/webdriver/common/selenium_manager.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,12 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717
import logging
18-
import re
1918
import subprocess
2019
import sys
2120
from pathlib import Path
2221
from typing import Tuple
2322

24-
from selenium.common.exceptions import WebDriverException
23+
from selenium.common.exceptions import SeleniumManagerException
2524

2625
logger = logging.getLogger(__name__)
2726

@@ -32,6 +31,9 @@ class SeleniumManager:
3231
This implementation is still in beta, and may change.
3332
"""
3433

34+
def __init__(self) -> None:
35+
pass
36+
3537
@staticmethod
3638
def get_binary() -> Path:
3739
"""
@@ -49,26 +51,27 @@ def get_binary() -> Path:
4951
path = Path(__file__).parent.joinpath(directory, file)
5052

5153
if not path.is_file():
52-
raise WebDriverException("Unable to obtain Selenium Manager")
54+
tracker = "https://github.com/SeleniumHQ/selenium/issues"
55+
raise SeleniumManagerException(f"{path} is missing. Please open an issue on {tracker}")
5356

5457
return path
5558

56-
@staticmethod
57-
def driver_location(browser: str) -> str:
59+
def driver_location(self, browser: str) -> str:
5860
"""
5961
Determines the path of the correct driver.
6062
:Args:
6163
- browser: which browser to get the driver path for.
6264
:Returns: The driver path to use
6365
"""
64-
if browser not in ("chrome", "firefox", "edge", "ie"):
65-
raise WebDriverException(f"Unable to locate driver associated with browser name: {browser}")
66+
allowed = ("chrome", "firefox", "edge", "ie")
67+
if browser not in allowed:
68+
raise SeleniumManagerException(f"{browser} is not a valid browser. Choose one of: {allowed}")
6669

67-
args = (str(SeleniumManager.get_binary()), "--browser", browser)
68-
result = SeleniumManager.run(args)
69-
command = result.split("\t")[-1].strip()
70-
logger.debug(f"Using driver at: {command}")
71-
return command
70+
binary, flag, browser = str(self.get_binary()), "--browser", browser
71+
result = self.run((binary, flag, browser))
72+
executable = result.split("\t")[-1].strip()
73+
logger.debug(f"Using driver at: {executable}")
74+
return executable
7275

7376
@staticmethod
7477
def run(args: Tuple[str, str, str]) -> str:
@@ -78,10 +81,13 @@ def run(args: Tuple[str, str, str]) -> str:
7881
- args: the components of the command being executed.
7982
:Returns: The log string containing the driver location.
8083
"""
81-
logger.debug(f"Executing selenium manager with: {args}")
82-
result = subprocess.run(args, stdout=subprocess.PIPE).stdout.decode("utf-8")
83-
84-
if not re.match("^INFO\t", result):
85-
raise WebDriverException(f"Unsuccessful command executed: {args}")
86-
87-
return result
84+
command = " ".join(args)
85+
logger.debug(f"Executing: {command}")
86+
completed_proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
87+
stdout = completed_proc.stdout.decode("utf-8").rstrip("\n")
88+
stderr = completed_proc.stderr.decode("utf-8").rstrip("\n")
89+
if completed_proc.returncode:
90+
raise SeleniumManagerException(f"Selenium manager failed for: {command}. {stderr}")
91+
else:
92+
# selenium manager exited 0 successfully, parse the executable path from stdout.
93+
return stdout.split("\t")[-1].strip()

py/selenium/webdriver/common/service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def start(self) -> None:
9393
logger.debug("driver not found in PATH, trying Selenium Manager")
9494
browser = self.__class__.__module__.split(".")[-2]
9595
try:
96-
path = SeleniumManager.driver_location(browser)
96+
path = SeleniumManager().driver_location(browser)
9797
except WebDriverException as new_err:
9898
logger.debug("Unable to obtain driver using Selenium Manager: " + new_err.msg)
9999
raise err
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Licensed to the Software Freedom Conservancy (SFC) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The SFC licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
import pytest
19+
20+
from selenium.common.exceptions import SeleniumManagerException
21+
from selenium.webdriver.common.selenium_manager import SeleniumManager
22+
23+
24+
def test_non_supported_browser_raises_sme():
25+
msg = r"foo is not a valid browser. Choose one of: \('chrome', 'firefox', 'edge', 'ie'\)"
26+
with pytest.raises(SeleniumManagerException, match=msg):
27+
_ = SeleniumManager().driver_location("foo")
28+
29+
30+
def test_stderr_is_propagated_to_exception_messages():
31+
msg = "Selenium manager failed for.*Error: \"Invalid browser/driver name\""
32+
with pytest.raises(SeleniumManagerException, match=msg):
33+
manager = SeleniumManager()
34+
binary = manager.get_binary()
35+
_ = manager.run((str(binary), "--browser", "foo"))

0 commit comments

Comments
 (0)