zmc
2023-08-08 e792e9a60d958b93aef96050644f369feb25d61b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# 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 json
import logging
import subprocess
import sys
from pathlib import Path
from typing import List
 
from selenium.common.exceptions import SeleniumManagerException
from selenium.webdriver.common.options import BaseOptions
 
logger = logging.getLogger(__name__)
 
 
class SeleniumManager:
    """Wrapper for getting information from the Selenium Manager binaries.
 
    This implementation is still in beta, and may change.
    """
 
    def __init__(self) -> None:
        pass
 
    @staticmethod
    def get_binary() -> Path:
        """Determines the path of the correct Selenium Manager binary.
 
        :Returns: The Selenium Manager executable location
        """
        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"
 
        path = Path(__file__).parent.joinpath(directory, file)
 
        if not path.is_file():
            tracker = "https://github.com/SeleniumHQ/selenium/issues"
            raise SeleniumManagerException(f"{path} is missing.  Please open an issue on {tracker}")
 
        return path
 
    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
        """
 
        logger.info("Applicable driver not found; attempting to install with Selenium Manager (Beta)")
 
        browser = options.capabilities["browserName"]
 
        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))
 
        if logger.getEffectiveLevel() == logging.DEBUG:
            args.append("--debug")
 
        result = self.run(args)
        executable = result.split("\t")[-1].strip()
        logger.debug(f"Using driver at: {executable}")
        return executable
 
    @staticmethod
    def run(args: List[str]) -> str:
        """
        Executes the Selenium Manager Binary.
        :Args:
         - args: the components of the command being executed.
        :Returns: The log string containing the driver location.
        """
        command = " ".join(args)
        logger.debug(f"Executing process: {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}")
        else:
            # Selenium Manager exited successfully, return executable path and print warnings
            for item in output["logs"]:
                if item["level"] == "WARN":
                    logger.warning(item["message"])
                if item["level"] == "DEBUG":
                    logger.debug(item["message"])
            return result