Updated script that can be controled by Nodejs web app

This commit is contained in:
mac OS
2024-11-25 12:24:18 +07:00
parent c440eda1f4
commit 8b0ab2bd3a
8662 changed files with 1803808 additions and 34 deletions

View File

@ -0,0 +1 @@
__version__ = "4.0.2"

View File

@ -0,0 +1,55 @@
import os
from typing import Optional
from webdriver_manager.core.download_manager import DownloadManager
from webdriver_manager.core.driver_cache import DriverCacheManager
from webdriver_manager.core.manager import DriverManager
from webdriver_manager.core.os_manager import OperationSystemManager, ChromeType
from webdriver_manager.drivers.chrome import ChromeDriver
class ChromeDriverManager(DriverManager):
def __init__(
self,
driver_version: Optional[str] = None,
name: str = "chromedriver",
url: str = "https://chromedriver.storage.googleapis.com",
latest_release_url: str = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE",
chrome_type: str = ChromeType.GOOGLE,
download_manager: Optional[DownloadManager] = None,
cache_manager: Optional[DriverCacheManager] = None,
os_system_manager: Optional[OperationSystemManager] = None
):
super().__init__(
download_manager=download_manager,
cache_manager=cache_manager,
os_system_manager=os_system_manager
)
self.driver = ChromeDriver(
name=name,
driver_version=driver_version,
url=url,
latest_release_url=latest_release_url,
chrome_type=chrome_type,
http_client=self.http_client,
os_system_manager=os_system_manager
)
def install(self) -> str:
driver_path = self._get_driver_binary_path(self.driver)
os.chmod(driver_path, 0o755)
return driver_path
def get_os_type(self):
os_type = super().get_os_type()
if "win" in os_type:
return "win32"
if not self._os_system_manager.is_mac_os(os_type):
return os_type
if self._os_system_manager.is_arch(os_type):
return "mac_arm64"
return os_type

View File

@ -0,0 +1,23 @@
import os
import zipfile
class LinuxZipFileWithPermissions(zipfile.ZipFile):
"""Class for extract files in linux with right permissions"""
def extract(self, member, path=None, pwd=None):
if not isinstance(member, zipfile.ZipInfo):
member = self.getinfo(member)
if path is None:
path = os.getcwd()
ret_val = self._extract_member(member, path, pwd) # noqa
attr = member.external_attr >> 16
os.chmod(ret_val, attr)
return ret_val
class Archive(object):
def __init__(self, path: str):
self.file_path = path

View File

@ -0,0 +1,41 @@
import os
from dotenv import load_dotenv
def str2bool(value):
return value.lower() in ['true', '1']
load_dotenv()
def ssl_verify():
return str2bool(os.getenv("WDM_SSL_VERIFY", "true"))
def gh_token():
return os.getenv("GH_TOKEN", None)
def wdm_local():
return str2bool(os.getenv("WDM_LOCAL", "false"))
def wdm_log_level():
default_level = 20
try:
return int(os.getenv("WDM_LOG", default_level))
except Exception:
return default_level
def wdm_progress_bar():
default_level = 1
try:
return int(os.getenv("WDM_PROGRESS_BAR", default_level))
except Exception:
return default_level
def get_xdist_worker_id():
return os.getenv("PYTEST_XDIST_WORKER", '')

View File

@ -0,0 +1,7 @@
import os
import sys
ROOT_FOLDER_NAME = ".wdm"
DEFAULT_PROJECT_ROOT_CACHE_PATH = os.path.join(sys.path[0], ROOT_FOLDER_NAME)
DEFAULT_USER_HOME_CACHE_PATH = os.path.join(
os.path.expanduser("~"), ROOT_FOLDER_NAME)

View File

@ -0,0 +1,42 @@
import os
from abc import ABC
from webdriver_manager.core.file_manager import File
from webdriver_manager.core.http import WDMHttpClient
from webdriver_manager.core.logger import log
class DownloadManager(ABC):
def __init__(self, http_client):
self._http_client = http_client
def download_file(self, url: str) -> File:
raise NotImplementedError
@property
def http_client(self):
return self._http_client
class WDMDownloadManager(DownloadManager):
def __init__(self, http_client=None):
if http_client is None:
http_client = WDMHttpClient()
super().__init__(http_client)
def download_file(self, url: str) -> File:
log(f"About to download new driver from {url}")
response = self._http_client.get(url)
log(f"Driver downloading response is {response.status_code}")
file_name = self.extract_filename_from_url(url)
return File(response, file_name)
@staticmethod
def extract_filename_from_url(url):
# Split the URL by '/'
url_parts = url.split('/')
# Get the last part of the URL, which should be the filename
filename = url_parts[-1]
# Decode the URL-encoded filename
filename = os.path.basename(filename)
return filename

View File

@ -0,0 +1,77 @@
from webdriver_manager.core.logger import log
from webdriver_manager.core.config import gh_token
from webdriver_manager.core.os_manager import OperationSystemManager
class Driver(object):
def __init__(
self,
name,
driver_version_to_download,
url,
latest_release_url,
http_client,
os_system_manager):
self._name = name
self._url = url
self._latest_release_url = latest_release_url
self._http_client = http_client
self._browser_version = None
self._driver_version_to_download = driver_version_to_download
self._os_system_manager = os_system_manager
if not self._os_system_manager:
self._os_system_manager = OperationSystemManager()
@property
def auth_header(self):
token = gh_token()
if token:
log("GH_TOKEN will be used to perform requests")
return {"Authorization": f"token {token}"}
return None
def get_name(self):
return self._name
def get_driver_download_url(self, os_type):
return f"{self._url}/{self.get_driver_version_to_download()}/{self._name}_{os_type}.zip"
def get_driver_version_to_download(self):
"""
Downloads version from parameter if version not None or "latest".
Downloads latest, if version is "latest" or browser could not been determined.
Downloads determined browser version driver in all other ways as a bonus fallback for lazy users.
"""
if self._driver_version_to_download:
return self._driver_version_to_download
return self.get_latest_release_version()
def get_latest_release_version(self):
# type: () -> str
raise NotImplementedError("Please implement this method")
def get_browser_version_from_os(self):
"""
Use-cases:
- for key in metadata;
- for printing nice logs;
- for fallback if version was not set at all.
Note: the fallback may have collisions in user cases when previous browser was not uninstalled properly.
"""
if self._browser_version is None:
self._browser_version = self._os_system_manager.get_browser_version_from_os(self.get_browser_type())
return self._browser_version
def get_browser_type(self):
raise NotImplementedError("Please implement this method")
def get_binary_name(self, os_type):
driver_name = self.get_name()
driver_binary_name = (
"msedgedriver" if driver_name == "edgedriver" else driver_name
)
driver_binary_name = (
f"{driver_binary_name}.exe" if "win" in os_type else driver_binary_name
)
return driver_binary_name

View File

@ -0,0 +1,164 @@
import datetime
import json
import os
from webdriver_manager.core.config import wdm_local, get_xdist_worker_id
from webdriver_manager.core.constants import (
DEFAULT_PROJECT_ROOT_CACHE_PATH,
DEFAULT_USER_HOME_CACHE_PATH, ROOT_FOLDER_NAME,
)
from webdriver_manager.core.driver import Driver
from webdriver_manager.core.file_manager import FileManager, File
from webdriver_manager.core.logger import log
from webdriver_manager.core.os_manager import OperationSystemManager
from webdriver_manager.core.utils import get_date_diff
class DriverCacheManager(object):
def __init__(self, root_dir=None, valid_range=1, file_manager=None):
self._root_dir = DEFAULT_USER_HOME_CACHE_PATH
is_wdm_local = wdm_local()
xdist_worker_id = get_xdist_worker_id()
if xdist_worker_id:
log(f"xdist worker is: {xdist_worker_id}")
self._root_dir = os.path.join(self._root_dir, xdist_worker_id)
if root_dir:
self._root_dir = os.path.join(root_dir, ROOT_FOLDER_NAME, xdist_worker_id)
if is_wdm_local:
self._root_dir = os.path.join(DEFAULT_PROJECT_ROOT_CACHE_PATH, xdist_worker_id)
self._drivers_root = "drivers"
self._drivers_json_path = os.path.join(self._root_dir, "drivers.json")
self._date_format = "%d/%m/%Y"
self._drivers_directory = os.path.join(self._root_dir, self._drivers_root)
self._cache_valid_days_range = valid_range
self._cache_key_driver_version = None
self._metadata_key = None
self._driver_binary_path = None
self._file_manager = file_manager
self._os_system_manager = OperationSystemManager()
if not self._file_manager:
self._file_manager = FileManager(self._os_system_manager)
def save_archive_file(self, file: File, path):
return self._file_manager.save_archive_file(file, path)
def unpack_archive(self, archive, path):
return self._file_manager.unpack_archive(archive, path)
def save_file_to_cache(self, driver: Driver, file: File):
path = self.__get_path(driver)
archive = self.save_archive_file(file, path)
files = self.unpack_archive(archive, path)
binary = self.__get_binary(files, driver.get_name())
binary_path = os.path.join(path, binary)
self.__save_metadata(driver, binary_path)
log(f"Driver has been saved in cache [{path}]")
return binary_path
def __get_binary(self, files, driver_name):
if not files:
raise Exception(f"Can't find binary for {driver_name} among {files}")
if len(files) == 1:
return files[0]
for f in files:
if 'LICENSE' in f:
continue
if 'THIRD_PARTY' in f:
continue
if driver_name in f:
return f
raise Exception(f"Can't find binary for {driver_name} among {files}")
def __save_metadata(self, driver: Driver, binary_path, date=None):
if date is None:
date = datetime.date.today()
metadata = self.load_metadata_content()
key = self.__get_metadata_key(driver)
data = {
key: {
"timestamp": date.strftime(self._date_format),
"binary_path": binary_path,
}
}
metadata.update(data)
with open(self._drivers_json_path, "w+") as outfile:
json.dump(metadata, outfile, indent=4)
def get_os_type(self):
return self._os_system_manager.get_os_type()
def find_driver(self, driver: Driver):
"""Find driver by '{os_type}_{driver_name}_{driver_version}_{browser_version}'."""
os_type = self.get_os_type()
driver_name = driver.get_name()
browser_type = driver.get_browser_type()
browser_version = self._os_system_manager.get_browser_version_from_os(browser_type)
if not browser_version:
return None
driver_version = self.get_cache_key_driver_version(driver)
metadata = self.load_metadata_content()
key = self.__get_metadata_key(driver)
if key not in metadata:
log(f'There is no [{os_type}] {driver_name} "{driver_version}" for browser {browser_type} '
f'"{browser_version}" in cache')
return None
driver_info = metadata[key]
path = driver_info["binary_path"]
if not os.path.exists(path):
return None
if not self.__is_valid(driver_info):
return None
path = driver_info["binary_path"]
log(f"Driver [{path}] found in cache")
return path
def __is_valid(self, driver_info):
dates_diff = get_date_diff(
driver_info["timestamp"], datetime.date.today(), self._date_format
)
return dates_diff < self._cache_valid_days_range
def load_metadata_content(self):
if os.path.exists(self._drivers_json_path):
with open(self._drivers_json_path, "r") as outfile:
return json.load(outfile)
return {}
def __get_metadata_key(self, driver: Driver):
if self._metadata_key:
return self._metadata_key
driver_version = self.get_cache_key_driver_version(driver)
browser_version = driver.get_browser_version_from_os()
browser_version = browser_version if browser_version else ""
self._metadata_key = f"{self.get_os_type()}_{driver.get_name()}_{driver_version}" \
f"_for_{browser_version}"
return self._metadata_key
def get_cache_key_driver_version(self, driver: Driver):
if self._cache_key_driver_version:
return self._cache_key_driver_version
return driver.get_driver_version_to_download()
def __get_path(self, driver: Driver):
if self._driver_binary_path is None:
self._driver_binary_path = os.path.join(
self._drivers_directory,
driver.get_name(),
self.get_os_type(),
driver.get_driver_version_to_download(),
)
return self._driver_binary_path

View File

@ -0,0 +1,94 @@
import os
import re
import tarfile
import zipfile
from webdriver_manager.core.archive import Archive, LinuxZipFileWithPermissions
from webdriver_manager.core.os_manager import OperationSystemManager
class File(object):
def __init__(self, stream, file_name):
self.content = stream.content
self.__stream = stream
self.file_name = file_name
self.__temp_name = "driver"
self.__regex_filename = r"""filename.+"(.+)"|filename.+''(.+)|filename=([\w.-]+)"""
@property
def filename(self) -> str:
if self.file_name:
return self.file_name
try:
content = self.__stream.headers["content-disposition"]
content_disposition_list = re.split(";", content)
filenames = [re.findall(self.__regex_filename, element) for element in content_disposition_list]
filename = next(filter(None, next(filter(None, next(filter(None, filenames))))))
except KeyError:
filename = f"{self.__temp_name}.zip"
except (IndexError, StopIteration):
filename = f"{self.__temp_name}.exe"
if '"' in filename:
filename = filename.replace('"', "")
return filename
class FileManager(object):
def __init__(self, os_system_manager: OperationSystemManager):
self._os_system_manager = os_system_manager
def save_archive_file(self, file: File, directory: str):
os.makedirs(directory, exist_ok=True)
archive_path = f"{directory}{os.sep}{file.filename}"
with open(archive_path, "wb") as code:
code.write(file.content)
if not os.path.exists(archive_path):
raise FileExistsError(f"No file has been saved on such path {archive_path}")
return Archive(archive_path)
def unpack_archive(self, archive_file: Archive, target_dir):
file_path = archive_file.file_path
if file_path.endswith(".zip"):
return self.__extract_zip(archive_file, target_dir)
elif file_path.endswith(".tar.gz"):
return self.__extract_tar_file(archive_file, target_dir)
def __extract_zip(self, archive_file, to_directory):
zip_class = (LinuxZipFileWithPermissions if self._os_system_manager.get_os_name() == "linux" else zipfile.ZipFile)
archive = zip_class(archive_file.file_path)
try:
archive.extractall(to_directory)
except Exception as e:
if e.args[0] not in [26, 13] and e.args[1] not in [
"Text file busy",
"Permission denied",
]:
raise e
file_names = []
for n in archive.namelist():
if "/" not in n:
file_names.append(n)
else:
file_path, file_name = n.split("/")
full_file_path = os.path.join(to_directory, file_path)
source = os.path.join(full_file_path, file_name)
destination = os.path.join(to_directory, file_name)
os.replace(source, destination)
file_names.append(file_name)
return sorted(file_names, key=lambda x: x.lower())
return archive.namelist()
def __extract_tar_file(self, archive_file, to_directory):
try:
tar = tarfile.open(archive_file.file_path, mode="r:gz")
except tarfile.ReadError:
tar = tarfile.open(archive_file.file_path, mode="r:bz2")
members = tar.getmembers()
tar.extractall(to_directory)
tar.close()
return [x.name for x in members]

View File

@ -0,0 +1,38 @@
import requests
from requests import Response, exceptions
from webdriver_manager.core.config import ssl_verify
class HttpClient:
def get(self, url, params=None, **kwargs) -> Response:
raise NotImplementedError
@staticmethod
def validate_response(resp: requests.Response):
status_code = resp.status_code
if status_code == 404:
raise ValueError(f"There is no such driver by url {resp.url}")
elif status_code == 401:
raise ValueError(f"API Rate limit exceeded. You have to add GH_TOKEN!!!")
elif resp.status_code != 200:
raise ValueError(
f"response body:\n{resp.text}\n"
f"request url:\n{resp.request.url}\n"
f"response headers:\n{dict(resp.headers)}\n"
)
class WDMHttpClient(HttpClient):
def __init__(self):
self._ssl_verify = ssl_verify()
def get(self, url, **kwargs) -> Response:
try:
resp = requests.get(
url=url, verify=self._ssl_verify, stream=True, **kwargs)
except exceptions.ConnectionError:
raise exceptions.ConnectionError(f"Could not reach host. Are you offline?")
self.validate_response(resp)
return resp

View File

@ -0,0 +1,32 @@
import logging
from webdriver_manager.core.config import wdm_log_level
__logger = logging.getLogger("WDM")
__logger.addHandler(logging.NullHandler())
def log(text):
"""Emitting the log message."""
__logger.log(wdm_log_level(), text)
def set_logger(logger):
"""
Set the global logger.
Parameters
----------
logger : logging.Logger
The custom logger to use.
Returns None
"""
# Check if the logger is a valid logger
if not isinstance(logger, logging.Logger):
raise ValueError("The logger must be an instance of logging.Logger")
# Bind the logger input to the global logger
global __logger
__logger = logger

View File

@ -0,0 +1,45 @@
from webdriver_manager.core.download_manager import WDMDownloadManager
from webdriver_manager.core.driver_cache import DriverCacheManager
from webdriver_manager.core.logger import log
from webdriver_manager.core.os_manager import OperationSystemManager
class DriverManager(object):
def __init__(
self,
download_manager=None,
cache_manager=None,
os_system_manager=None
):
self._cache_manager = cache_manager
if not self._cache_manager:
self._cache_manager = DriverCacheManager()
self._download_manager = download_manager
if self._download_manager is None:
self._download_manager = WDMDownloadManager()
self._os_system_manager = os_system_manager
if not self._os_system_manager:
self._os_system_manager = OperationSystemManager()
log("====== WebDriver manager ======")
@property
def http_client(self):
return self._download_manager.http_client
def install(self) -> str:
raise NotImplementedError("Please Implement this method")
def _get_driver_binary_path(self, driver):
binary_path = self._cache_manager.find_driver(driver)
if binary_path:
return binary_path
os_type = self.get_os_type()
file = self._download_manager.download_file(driver.get_driver_download_url(os_type))
binary_path = self._cache_manager.save_file_to_cache(driver, file)
return binary_path
def get_os_type(self):
return self._os_system_manager.get_os_type()

View File

@ -0,0 +1,163 @@
import platform
import sys
from webdriver_manager.core.utils import linux_browser_apps_to_cmd, windows_browser_apps_to_cmd, \
read_version_from_cmd
class ChromeType(object):
GOOGLE = "google-chrome"
CHROMIUM = "chromium"
BRAVE = "brave-browser"
MSEDGE = "edge"
class OSType(object):
LINUX = "linux"
MAC = "mac"
WIN = "win"
PATTERN = {
ChromeType.CHROMIUM: r"\d+\.\d+\.\d+",
ChromeType.GOOGLE: r"\d+\.\d+\.\d+",
ChromeType.MSEDGE: r"\d+\.\d+\.\d+",
"brave-browser": r"\d+\.\d+\.\d+(\.\d+)?",
"firefox": r"(\d+.\d+)",
}
class OperationSystemManager(object):
def __init__(self, os_type=None):
self._os_type = os_type
@staticmethod
def get_os_name():
pl = sys.platform
if pl == "linux" or pl == "linux2":
return OSType.LINUX
elif pl == "darwin":
return OSType.MAC
elif pl == "win32" or pl == "cygwin":
return OSType.WIN
@staticmethod
def get_os_architecture():
if platform.machine().endswith("64"):
return 64
else:
return 32
def get_os_type(self):
if self._os_type:
return self._os_type
return f"{self.get_os_name()}{self.get_os_architecture()}"
@staticmethod
def is_arch(os_sys_type):
if '_m1' in os_sys_type:
return True
return platform.processor() != 'i386'
@staticmethod
def is_mac_os(os_sys_type):
return OSType.MAC in os_sys_type
def get_browser_version_from_os(self, browser_type=None):
"""Return installed browser version."""
cmd_mapping = {
ChromeType.GOOGLE: {
OSType.LINUX: linux_browser_apps_to_cmd(
"google-chrome",
"google-chrome-stable",
"google-chrome-beta",
"google-chrome-dev",
),
OSType.MAC: r"/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version",
OSType.WIN: windows_browser_apps_to_cmd(
r'(Get-Item -Path "$env:PROGRAMFILES\Google\Chrome\Application\chrome.exe").VersionInfo.FileVersion',
r'(Get-Item -Path "$env:PROGRAMFILES (x86)\Google\Chrome\Application\chrome.exe").VersionInfo.FileVersion',
r'(Get-Item -Path "$env:LOCALAPPDATA\Google\Chrome\Application\chrome.exe").VersionInfo.FileVersion',
r'(Get-ItemProperty -Path Registry::"HKCU\SOFTWARE\Google\Chrome\BLBeacon").version',
r'(Get-ItemProperty -Path Registry::"HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome").version',
),
},
ChromeType.CHROMIUM: {
OSType.LINUX: linux_browser_apps_to_cmd("chromium", "chromium-browser"),
OSType.MAC: r"/Applications/Chromium.app/Contents/MacOS/Chromium --version",
OSType.WIN: windows_browser_apps_to_cmd(
r'(Get-Item -Path "$env:PROGRAMFILES\Chromium\Application\chrome.exe").VersionInfo.FileVersion',
r'(Get-Item -Path "$env:PROGRAMFILES (x86)\Chromium\Application\chrome.exe").VersionInfo.FileVersion',
r'(Get-Item -Path "$env:LOCALAPPDATA\Chromium\Application\chrome.exe").VersionInfo.FileVersion',
r'(Get-ItemProperty -Path Registry::"HKCU\SOFTWARE\Chromium\BLBeacon").version',
r'(Get-ItemProperty -Path Registry::"HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Chromium").version',
),
},
ChromeType.BRAVE: {
OSType.LINUX: linux_browser_apps_to_cmd(
"brave-browser", "brave-browser-beta", "brave-browser-nightly"
),
OSType.MAC: r"/Applications/Brave\ Browser.app/Contents/MacOS/Brave\ Browser --version",
OSType.WIN: windows_browser_apps_to_cmd(
r'(Get-Item -Path "$env:PROGRAMFILES\BraveSoftware\Brave-Browser\Application\brave.exe").VersionInfo.FileVersion',
r'(Get-Item -Path "$env:PROGRAMFILES (x86)\BraveSoftware\Brave-Browser\Application\brave.exe").VersionInfo.FileVersion',
r'(Get-Item -Path "$env:LOCALAPPDATA\BraveSoftware\Brave-Browser\Application\brave.exe").VersionInfo.FileVersion',
r'(Get-ItemProperty -Path Registry::"HKCU\SOFTWARE\BraveSoftware\Brave-Browser\BLBeacon").version',
r'(Get-ItemProperty -Path Registry::"HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\BraveSoftware Brave-Browser").version',
),
},
ChromeType.MSEDGE: {
OSType.LINUX: linux_browser_apps_to_cmd(
"microsoft-edge",
"microsoft-edge-stable",
"microsoft-edge-beta",
"microsoft-edge-dev",
),
OSType.MAC: r"/Applications/Microsoft\ Edge.app/Contents/MacOS/Microsoft\ Edge --version",
OSType.WIN: windows_browser_apps_to_cmd(
# stable edge
r'(Get-Item -Path "$env:PROGRAMFILES\Microsoft\Edge\Application\msedge.exe").VersionInfo.FileVersion',
r'(Get-Item -Path "$env:PROGRAMFILES (x86)\Microsoft\Edge\Application\msedge.exe").VersionInfo.FileVersion',
r'(Get-ItemProperty -Path Registry::"HKCU\SOFTWARE\Microsoft\Edge\BLBeacon").version',
r'(Get-ItemProperty -Path Registry::"HKLM\SOFTWARE\Microsoft\EdgeUpdate\Clients\{56EB18F8-8008-4CBD-B6D2-8C97FE7E9062}").pv',
# beta edge
r'(Get-Item -Path "$env:LOCALAPPDATA\Microsoft\Edge Beta\Application\msedge.exe").VersionInfo.FileVersion',
r'(Get-Item -Path "$env:PROGRAMFILES\Microsoft\Edge Beta\Application\msedge.exe").VersionInfo.FileVersion',
r'(Get-Item -Path "$env:PROGRAMFILES (x86)\Microsoft\Edge Beta\Application\msedge.exe").VersionInfo.FileVersion',
r'(Get-ItemProperty -Path Registry::"HKCU\SOFTWARE\Microsoft\Edge Beta\BLBeacon").version',
# dev edge
r'(Get-Item -Path "$env:LOCALAPPDATA\Microsoft\Edge Dev\Application\msedge.exe").VersionInfo.FileVersion',
r'(Get-Item -Path "$env:PROGRAMFILES\Microsoft\Edge Dev\Application\msedge.exe").VersionInfo.FileVersion',
r'(Get-Item -Path "$env:PROGRAMFILES (x86)\Microsoft\Edge Dev\Application\msedge.exe").VersionInfo.FileVersion',
r'(Get-ItemProperty -Path Registry::"HKCU\SOFTWARE\Microsoft\Edge Dev\BLBeacon").version',
# canary edge
r'(Get-Item -Path "$env:LOCALAPPDATA\Microsoft\Edge SxS\Application\msedge.exe").VersionInfo.FileVersion',
r'(Get-ItemProperty -Path Registry::"HKCU\SOFTWARE\Microsoft\Edge SxS\BLBeacon").version',
# highest edge
r"(Get-Item (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\msedge.exe').'(Default)').VersionInfo.ProductVersion",
r"[System.Diagnostics.FileVersionInfo]::GetVersionInfo((Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\msedge.exe').'(Default)').ProductVersion",
r"Get-AppxPackage -Name *MicrosoftEdge.* | Foreach Version",
r'(Get-ItemProperty -Path Registry::"HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge").version',
),
},
"firefox": {
OSType.LINUX: linux_browser_apps_to_cmd("firefox"),
OSType.MAC: r"/Applications/Firefox.app/Contents/MacOS/firefox --version",
OSType.WIN: windows_browser_apps_to_cmd(
r'(Get-Item -Path "$env:PROGRAMFILES\Mozilla Firefox\firefox.exe").VersionInfo.FileVersion',
r'(Get-Item -Path "$env:PROGRAMFILES (x86)\Mozilla Firefox\firefox.exe").VersionInfo.FileVersion',
r"(Get-Item (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\firefox.exe').'(Default)').VersionInfo.ProductVersion",
r'(Get-ItemProperty -Path Registry::"HKLM\SOFTWARE\Mozilla\Mozilla Firefox").CurrentVersion',
),
},
}
try:
cmd_mapping = cmd_mapping[browser_type][OperationSystemManager.get_os_name()]
pattern = PATTERN[browser_type]
version = read_version_from_cmd(cmd_mapping, pattern)
return version
except Exception:
return None
# raise Exception("Can not get browser version from OS")

View File

@ -0,0 +1,63 @@
import datetime
import os
import re
import subprocess
def get_date_diff(date1, date2, date_format):
a = datetime.datetime.strptime(date1, date_format)
b = datetime.datetime.strptime(
str(date2.strftime(date_format)), date_format)
return (b - a).days
def linux_browser_apps_to_cmd(*apps: str) -> str:
"""Create 'browser --version' command from browser app names.
Result command example:
chromium --version || chromium-browser --version
"""
ignore_errors_cmd_part = " 2>/dev/null" if os.getenv(
"WDM_LOG_LEVEL") == "0" else ""
return " || ".join(f"{i} --version{ignore_errors_cmd_part}" for i in apps)
def windows_browser_apps_to_cmd(*apps: str) -> str:
"""Create analogue of browser --version command for windows."""
powershell = determine_powershell()
first_hit_template = """$tmp = {expression}; if ($tmp) {{echo $tmp; Exit;}};"""
script = "$ErrorActionPreference='silentlycontinue'; " + " ".join(
first_hit_template.format(expression=e) for e in apps
)
return f'{powershell} -NoProfile "{script}"'
def read_version_from_cmd(cmd, pattern):
with subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stdin=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
shell=True,
) as stream:
stdout = stream.communicate()[0].decode()
version = re.search(pattern, stdout)
version = version.group(0) if version else None
return version
def determine_powershell():
"""Returns "True" if runs in Powershell and "False" if another console."""
cmd = "(dir 2>&1 *`|echo CMD);&<# rem #>echo powershell"
with subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
stdin=subprocess.DEVNULL,
shell=True,
) as stream:
stdout = stream.communicate()[0].decode()
return "" if stdout == "powershell" else "powershell"

View File

@ -0,0 +1,97 @@
from packaging import version
from webdriver_manager.core.driver import Driver
from webdriver_manager.core.logger import log
from webdriver_manager.core.os_manager import ChromeType
import json
class ChromeDriver(Driver):
def __init__(
self,
name,
driver_version,
url,
latest_release_url,
http_client,
os_system_manager,
chrome_type=ChromeType.GOOGLE
):
super(ChromeDriver, self).__init__(
name,
driver_version,
url,
latest_release_url,
http_client,
os_system_manager
)
self._browser_type = chrome_type
def get_driver_download_url(self, os_type):
driver_version_to_download = self.get_driver_version_to_download()
# For Mac ARM CPUs after version 106.0.5249.61 the format of OS type changed
# to more unified "mac_arm64". For newer versions, it'll be "mac_arm64"
# by default, for lower versions we replace "mac_arm64" to old format - "mac64_m1".
if version.parse(driver_version_to_download) < version.parse("106.0.5249.61"):
os_type = os_type.replace("mac_arm64", "mac64_m1")
if version.parse(driver_version_to_download) >= version.parse("115"):
if os_type == "mac64":
os_type = "mac-x64"
if os_type in ["mac_64", "mac64_m1", "mac_arm64"]:
os_type = "mac-arm64"
modern_version_url = self.get_url_for_version_and_platform(driver_version_to_download, os_type)
log(f"Modern chrome version {modern_version_url}")
return modern_version_url
return f"{self._url}/{driver_version_to_download}/{self.get_name()}_{os_type}.zip"
def get_browser_type(self):
return self._browser_type
def get_latest_release_version(self):
determined_browser_version = self.get_browser_version_from_os()
log(f"Get LATEST {self._name} version for {self._browser_type}")
if determined_browser_version is not None and version.parse(determined_browser_version) >= version.parse("115"):
url = "https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build.json"
response = self._http_client.get(url)
response_dict = json.loads(response.text)
determined_browser_version = response_dict.get("builds").get(determined_browser_version).get("version")
return determined_browser_version
elif determined_browser_version is not None:
# Remove the build version (the last segment) from determined_browser_version for version < 113
determined_browser_version = ".".join(determined_browser_version.split(".")[:3])
latest_release_url = f"{self._latest_release_url}_{determined_browser_version}"
else:
latest_release_url = self._latest_release_url
resp = self._http_client.get(url=latest_release_url)
return resp.text.rstrip()
def get_url_for_version_and_platform(self, browser_version, platform):
url = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
response = self._http_client.get(url)
data = response.json()
versions = data["versions"]
if version.parse(browser_version) >= version.parse("115"):
short_version = ".".join(browser_version.split(".")[:3])
compatible_versions = [v for v in versions if short_version in v["version"]]
if compatible_versions:
latest_version = compatible_versions[-1]
log(f"WebDriver version {latest_version['version']} selected")
downloads = latest_version["downloads"]["chromedriver"]
for d in downloads:
if d["platform"] == platform:
return d["url"]
else:
for v in versions:
if v["version"] == browser_version:
downloads = v["downloads"]["chromedriver"]
for d in downloads:
if d["platform"] == platform:
return d["url"]
raise Exception(f"No such driver version {browser_version} for {platform}")

View File

@ -0,0 +1,55 @@
from webdriver_manager.core.driver import Driver
from webdriver_manager.core.logger import log
from webdriver_manager.core.os_manager import OSType, ChromeType
class EdgeChromiumDriver(Driver):
def __init__(
self,
name,
driver_version,
url,
latest_release_url,
http_client,
os_system_manager
):
super(EdgeChromiumDriver, self).__init__(
name,
driver_version,
url,
latest_release_url,
http_client,
os_system_manager
)
def get_stable_release_version(self):
"""Stable driver version when browser version was not determined."""
stable_url = self._latest_release_url.replace("LATEST_RELEASE", "LATEST_STABLE")
resp = self._http_client.get(url=stable_url)
return resp.text.rstrip()
def get_latest_release_version(self) -> str:
determined_browser_version = self.get_browser_version_from_os()
log(f"Get LATEST {self._name} version for Edge {determined_browser_version}")
edge_driver_version_to_download = (
self.get_stable_release_version()
if (determined_browser_version is None)
else determined_browser_version
)
major_edge_version = edge_driver_version_to_download.split(".")[0]
os_type = self._os_system_manager.get_os_type()
latest_release_url = {
OSType.WIN
in os_type: f"{self._latest_release_url}_{major_edge_version}_WINDOWS",
OSType.MAC
in os_type: f"{self._latest_release_url}_{major_edge_version}_MACOS",
OSType.LINUX
in os_type: f"{self._latest_release_url}_{major_edge_version}_LINUX",
}[True]
resp = self._http_client.get(url=latest_release_url)
return resp.text.rstrip()
def get_browser_type(self):
return ChromeType.MSEDGE

View File

@ -0,0 +1,57 @@
from webdriver_manager.core.driver import Driver
from webdriver_manager.core.logger import log
class GeckoDriver(Driver):
def __init__(
self,
name,
driver_version,
url,
latest_release_url,
mozila_release_tag,
http_client,
os_system_manager
):
super(GeckoDriver, self).__init__(
name,
driver_version,
url,
latest_release_url,
http_client,
os_system_manager,
)
self._mozila_release_tag = mozila_release_tag
def get_latest_release_version(self) -> str:
determined_browser_version = self.get_browser_version_from_os()
log(f"Get LATEST {self._name} version for {determined_browser_version} firefox")
resp = self._http_client.get(
url=self.latest_release_url,
headers=self.auth_header
)
return resp.json()["tag_name"]
def get_driver_download_url(self, os_type):
"""Like https://github.com/mozilla/geckodriver/releases/download/v0.11.1/geckodriver-v0.11.1-linux64.tar.gz"""
driver_version_to_download = self.get_driver_version_to_download()
log(f"Getting latest mozilla release info for {driver_version_to_download}")
resp = self._http_client.get(
url=self.tagged_release_url(driver_version_to_download),
headers=self.auth_header
)
assets = resp.json()["assets"]
name = f"{self.get_name()}-{driver_version_to_download}-{os_type}."
output_dict = [
asset for asset in assets if asset["name"].startswith(name)]
return output_dict[0]["browser_download_url"]
@property
def latest_release_url(self):
return self._latest_release_url
def tagged_release_url(self, version):
return self._mozila_release_tag.format(version)
def get_browser_type(self):
return "firefox"

View File

@ -0,0 +1,82 @@
from webdriver_manager.core.driver import Driver
from webdriver_manager.core.logger import log
class IEDriver(Driver):
def __init__(
self,
name,
driver_version,
url,
latest_release_url,
ie_release_tag,
http_client,
os_system_manager
):
super(IEDriver, self).__init__(
name,
driver_version,
url,
latest_release_url,
http_client,
os_system_manager
)
self._ie_release_tag = ie_release_tag
# todo: for 'browser_version' implement installed IE version detection
# like chrome or firefox
def get_latest_release_version(self) -> str:
log(f"Get LATEST driver version for Internet Explorer")
resp = self._http_client.get(
url=self.latest_release_url,
headers=self.auth_header
)
releases = resp.json()
release = next(
release
for release in releases
for asset in release["assets"]
if asset["name"].startswith(self.get_name())
)
return release["tag_name"].replace("selenium-", "")
def get_driver_download_url(self, os_type):
"""Like https://github.com/seleniumhq/selenium/releases/download/3.141.59/IEDriverServer_Win32_3.141.59.zip"""
driver_version_to_download = self.get_driver_version_to_download()
log(f"Getting latest ie release info for {driver_version_to_download}")
resp = self._http_client.get(
url=self.tagged_release_url(driver_version_to_download),
headers=self.auth_header
)
assets = resp.json()["assets"]
name = f"{self._name}_{os_type}_{driver_version_to_download}" + "."
output_dict = [
asset for asset in assets if asset["name"].startswith(name)]
return output_dict[0]["browser_download_url"]
@property
def latest_release_url(self):
return self._latest_release_url
def tagged_release_url(self, version):
version = self.__get_divided_version(version)
return self._ie_release_tag.format(version)
def __get_divided_version(self, version):
divided_version = version.split(".")
if len(divided_version) == 2:
return f"{version}.0"
elif len(divided_version) == 3:
return version
else:
raise ValueError(
"Version must consist of major, minor and/or patch, "
"but given was: '{version}'".format(version=version)
)
def get_browser_type(self):
return "msie"

View File

@ -0,0 +1,55 @@
from webdriver_manager.core.driver import Driver
from webdriver_manager.core.logger import log
class OperaDriver(Driver):
def __init__(
self,
name,
driver_version,
url,
latest_release_url,
opera_release_tag,
http_client,
os_system_manager
):
super(OperaDriver, self).__init__(
name,
driver_version,
url,
latest_release_url,
http_client,
os_system_manager
)
self.opera_release_tag = opera_release_tag
def get_latest_release_version(self) -> str:
resp = self._http_client.get(
url=self.latest_release_url,
headers=self.auth_header
)
return resp.json()["tag_name"]
def get_driver_download_url(self, os_type) -> str:
"""Like https://github.com/operasoftware/operachromiumdriver/releases/download/v.2.45/operadriver_linux64.zip"""
driver_version_to_download = self.get_driver_version_to_download()
log(f"Getting latest opera release info for {driver_version_to_download}")
resp = self._http_client.get(
url=self.tagged_release_url(driver_version_to_download),
headers=self.auth_header
)
assets = resp.json()["assets"]
name = "{0}_{1}".format(self.get_name(), os_type)
output_dict = [
asset for asset in assets if asset["name"].startswith(name)]
return output_dict[0]["browser_download_url"]
@property
def latest_release_url(self):
return self._latest_release_url
def tagged_release_url(self, version):
return self.opera_release_tag.format(version)
def get_browser_type(self):
return "opera"

View File

@ -0,0 +1,51 @@
import os
from typing import Optional
from webdriver_manager.core.download_manager import DownloadManager
from webdriver_manager.core.driver_cache import DriverCacheManager
from webdriver_manager.core.manager import DriverManager
from webdriver_manager.core.os_manager import OperationSystemManager
from webdriver_manager.drivers.firefox import GeckoDriver
class GeckoDriverManager(DriverManager):
def __init__(
self,
version: Optional[str] = None,
name: str = "geckodriver",
url: str = "https://github.com/mozilla/geckodriver/releases/download",
latest_release_url: str = "https://api.github.com/repos/mozilla/geckodriver/releases/latest",
mozila_release_tag: str = "https://api.github.com/repos/mozilla/geckodriver/releases/tags/{0}",
download_manager: Optional[DownloadManager] = None,
cache_manager: Optional[DriverCacheManager] = None,
os_system_manager: Optional[OperationSystemManager] = None
):
super(GeckoDriverManager, self).__init__(
download_manager=download_manager,
cache_manager=cache_manager
)
self.driver = GeckoDriver(
driver_version=version,
name=name,
url=url,
latest_release_url=latest_release_url,
mozila_release_tag=mozila_release_tag,
http_client=self.http_client,
os_system_manager=os_system_manager
)
def install(self) -> str:
driver_path = self._get_driver_binary_path(self.driver)
os.chmod(driver_path, 0o755)
return driver_path
def get_os_type(self):
os_type = super().get_os_type()
if not self._os_system_manager.is_mac_os(os_type):
return os_type
macos = 'macos'
if self._os_system_manager.is_arch(os_type):
return f"{macos}-aarch64"
return macos

View File

@ -0,0 +1,75 @@
import os
from typing import Optional
from webdriver_manager.core.download_manager import DownloadManager
from webdriver_manager.core.driver_cache import DriverCacheManager
from webdriver_manager.core.os_manager import OperationSystemManager
from webdriver_manager.drivers.edge import EdgeChromiumDriver
from webdriver_manager.drivers.ie import IEDriver
from webdriver_manager.core.manager import DriverManager
class IEDriverManager(DriverManager):
def __init__(
self,
version: Optional[str] = None,
name: str = "IEDriverServer",
url: str = "https://github.com/seleniumhq/selenium/releases/download",
latest_release_url: str = "https://api.github.com/repos/seleniumhq/selenium/releases",
ie_release_tag: str = "https://api.github.com/repos/seleniumhq/selenium/releases/tags/selenium-{0}",
download_manager: Optional[DownloadManager] = None,
cache_manager: Optional[DriverCacheManager] = None,
os_system_manager: Optional[OperationSystemManager] = None
):
super().__init__(
download_manager=download_manager,
cache_manager=cache_manager
)
self.driver = IEDriver(
driver_version=version,
name=name,
url=url,
latest_release_url=latest_release_url,
ie_release_tag=ie_release_tag,
http_client=self.http_client,
os_system_manager=os_system_manager
)
def install(self) -> str:
return self._get_driver_binary_path(self.driver)
def get_os_type(self):
return "x64" if self._os_system_manager.get_os_type() == "win64" else "Win32"
class EdgeChromiumDriverManager(DriverManager):
def __init__(
self,
version: Optional[str] = None,
name: str = "edgedriver",
url: str = "https://msedgedriver.azureedge.net",
latest_release_url: str = "https://msedgedriver.azureedge.net/LATEST_RELEASE",
download_manager: Optional[DownloadManager] = None,
cache_manager: Optional[DriverCacheManager] = None,
os_system_manager: Optional[OperationSystemManager] = None
):
super().__init__(
download_manager=download_manager,
cache_manager=cache_manager,
os_system_manager=os_system_manager
)
self.driver = EdgeChromiumDriver(
driver_version=version,
name=name,
url=url,
latest_release_url=latest_release_url,
http_client=self.http_client,
os_system_manager=os_system_manager
)
def install(self) -> str:
driver_path = self._get_driver_binary_path(self.driver)
os.chmod(driver_path, 0o755)
return driver_path

View File

@ -0,0 +1,50 @@
import os
from typing import Optional
from webdriver_manager.core.download_manager import DownloadManager
from webdriver_manager.core.driver_cache import DriverCacheManager
from webdriver_manager.core.manager import DriverManager
from webdriver_manager.core.os_manager import OperationSystemManager
from webdriver_manager.drivers.opera import OperaDriver
class OperaDriverManager(DriverManager):
def __init__(
self,
version: Optional[str] = None,
name: str = "operadriver",
url: str = "https://github.com/operasoftware/operachromiumdriver/"
"releases/",
latest_release_url: str = "https://api.github.com/repos/"
"operasoftware/operachromiumdriver/releases/latest",
opera_release_tag: str = "https://api.github.com/repos/"
"operasoftware/operachromiumdriver/releases/tags/{0}",
download_manager: Optional[DownloadManager] = None,
cache_manager: Optional[DriverCacheManager] = None,
os_system_manager: Optional[OperationSystemManager] = None
):
super().__init__(
download_manager=download_manager,
cache_manager=cache_manager
)
self.driver = OperaDriver(
name=name,
driver_version=version,
url=url,
latest_release_url=latest_release_url,
opera_release_tag=opera_release_tag,
http_client=self.http_client,
os_system_manager=os_system_manager
)
def install(self) -> str:
driver_path = self._get_driver_binary_path(self.driver)
if not os.path.isfile(driver_path):
for name in os.listdir(driver_path):
if "sha512_sum" in name:
os.remove(os.path.join(driver_path, name))
break
driver_path = os.path.join(driver_path, os.listdir(driver_path)[0])
os.chmod(driver_path, 0o755)
return driver_path