webdriver-manager 4.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ __version__ = "4.1.1"
@@ -0,0 +1,55 @@
1
+ import os
2
+ from typing import Optional
3
+
4
+ from webdriver_manager.core.download_manager import DownloadManager
5
+ from webdriver_manager.core.driver_cache import DriverCacheManager
6
+ from webdriver_manager.core.manager import DriverManager
7
+ from webdriver_manager.core.os_manager import OperationSystemManager, ChromeType
8
+ from webdriver_manager.drivers.chrome import ChromeDriver
9
+
10
+
11
+ class ChromeDriverManager(DriverManager):
12
+ def __init__(
13
+ self,
14
+ driver_version: Optional[str] = None,
15
+ name: str = "chromedriver",
16
+ url: str = "https://storage.googleapis.com/chrome-for-testing-public/",
17
+ latest_release_url: str = "https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_STABLE",
18
+ chrome_type: str = ChromeType.GOOGLE,
19
+ download_manager: Optional[DownloadManager] = None,
20
+ cache_manager: Optional[DriverCacheManager] = None,
21
+ os_system_manager: Optional[OperationSystemManager] = None
22
+ ):
23
+ super().__init__(
24
+ download_manager=download_manager,
25
+ cache_manager=cache_manager,
26
+ os_system_manager=os_system_manager
27
+ )
28
+
29
+ self.driver = ChromeDriver(
30
+ name=name,
31
+ driver_version=driver_version,
32
+ url=url,
33
+ latest_release_url=latest_release_url,
34
+ chrome_type=chrome_type,
35
+ http_client=self.http_client,
36
+ os_system_manager=os_system_manager
37
+ )
38
+
39
+ def install(self) -> str:
40
+ driver_path = self._get_driver_binary_path(self.driver)
41
+ os.chmod(driver_path, 0o755)
42
+ return driver_path
43
+
44
+ def get_os_type(self):
45
+ os_type = super().get_os_type()
46
+ if "win" in os_type:
47
+ return "win64" if self._os_system_manager.get_os_architecture() == 64 else "win32"
48
+
49
+ if not self._os_system_manager.is_mac_os(os_type):
50
+ return os_type
51
+
52
+ if self._os_system_manager.is_arch():
53
+ return "mac_arm64"
54
+
55
+ return os_type
File without changes
@@ -0,0 +1,23 @@
1
+ import os
2
+ import zipfile
3
+
4
+
5
+ class LinuxZipFileWithPermissions(zipfile.ZipFile):
6
+ """Class for extract files in linux with right permissions"""
7
+
8
+ def extract(self, member, path=None, pwd=None):
9
+ if not isinstance(member, zipfile.ZipInfo):
10
+ member = self.getinfo(member)
11
+
12
+ if path is None:
13
+ path = os.getcwd()
14
+
15
+ ret_val = self._extract_member(member, path, pwd) # noqa
16
+ attr = member.external_attr >> 16
17
+ os.chmod(ret_val, attr)
18
+ return ret_val
19
+
20
+
21
+ class Archive(object):
22
+ def __init__(self, path: str):
23
+ self.file_path = path
@@ -0,0 +1,41 @@
1
+ import os
2
+ from dotenv import load_dotenv
3
+
4
+
5
+ def str2bool(value):
6
+ return value.lower() in ['true', '1']
7
+
8
+
9
+ load_dotenv()
10
+
11
+
12
+ def ssl_verify():
13
+ return str2bool(os.getenv("WDM_SSL_VERIFY", "true"))
14
+
15
+
16
+ def gh_token():
17
+ return os.getenv("GH_TOKEN", None)
18
+
19
+
20
+ def wdm_local():
21
+ return str2bool(os.getenv("WDM_LOCAL", "false"))
22
+
23
+
24
+ def wdm_log_level():
25
+ default_level = 20
26
+ try:
27
+ return int(os.getenv("WDM_LOG", default_level))
28
+ except Exception:
29
+ return default_level
30
+
31
+
32
+ def wdm_progress_bar():
33
+ default_level = 1
34
+ try:
35
+ return int(os.getenv("WDM_PROGRESS_BAR", default_level))
36
+ except Exception:
37
+ return default_level
38
+
39
+
40
+ def get_xdist_worker_id():
41
+ return os.getenv("PYTEST_XDIST_WORKER", '')
@@ -0,0 +1,25 @@
1
+ import os
2
+ import sys
3
+ import tempfile
4
+
5
+ ROOT_FOLDER_NAME = ".wdm"
6
+
7
+
8
+ def get_default_project_root_cache_path():
9
+ base_path = sys.path[0]
10
+ if getattr(sys, "frozen", False) or base_path.endswith(".zip"):
11
+ base_path = os.getcwd()
12
+ return os.path.join(base_path, ROOT_FOLDER_NAME)
13
+
14
+
15
+ DEFAULT_PROJECT_ROOT_CACHE_PATH = get_default_project_root_cache_path()
16
+
17
+
18
+ def get_default_user_home_cache_path():
19
+ home = os.path.expanduser("~")
20
+ if not home or home in (os.path.sep, "/") or home.startswith("~"):
21
+ home = tempfile.gettempdir()
22
+ return os.path.join(home, ROOT_FOLDER_NAME)
23
+
24
+
25
+ DEFAULT_USER_HOME_CACHE_PATH = get_default_user_home_cache_path()
@@ -0,0 +1,42 @@
1
+ import os
2
+ from abc import ABC
3
+
4
+ from webdriver_manager.core.file_manager import File
5
+ from webdriver_manager.core.http import WDMHttpClient
6
+ from webdriver_manager.core.logger import log
7
+
8
+
9
+ class DownloadManager(ABC):
10
+ def __init__(self, http_client):
11
+ self._http_client = http_client
12
+
13
+ def download_file(self, url: str) -> File:
14
+ raise NotImplementedError
15
+
16
+ @property
17
+ def http_client(self):
18
+ return self._http_client
19
+
20
+
21
+ class WDMDownloadManager(DownloadManager):
22
+ def __init__(self, http_client=None):
23
+ if http_client is None:
24
+ http_client = WDMHttpClient()
25
+ super().__init__(http_client)
26
+
27
+ def download_file(self, url: str) -> File:
28
+ log(f"About to download new driver from {url}")
29
+ response = self._http_client.get(url)
30
+ log(f"Driver downloading response is {response.status_code}")
31
+ file_name = self.extract_filename_from_url(url)
32
+ return File(response, file_name)
33
+
34
+ @staticmethod
35
+ def extract_filename_from_url(url):
36
+ # Split the URL by '/'
37
+ url_parts = url.split('/')
38
+ # Get the last part of the URL, which should be the filename
39
+ filename = url_parts[-1]
40
+ # Decode the URL-encoded filename
41
+ filename = os.path.basename(filename)
42
+ return filename
@@ -0,0 +1,77 @@
1
+ from webdriver_manager.core.logger import log
2
+ from webdriver_manager.core.config import gh_token
3
+ from webdriver_manager.core.os_manager import OperationSystemManager
4
+
5
+
6
+ class Driver(object):
7
+ def __init__(
8
+ self,
9
+ name,
10
+ driver_version_to_download,
11
+ url,
12
+ latest_release_url,
13
+ http_client,
14
+ os_system_manager):
15
+ self._name = name
16
+ self._url = url
17
+ self._latest_release_url = latest_release_url
18
+ self._http_client = http_client
19
+ self._browser_version = None
20
+ self._driver_version_to_download = driver_version_to_download
21
+ self._os_system_manager = os_system_manager
22
+ if not self._os_system_manager:
23
+ self._os_system_manager = OperationSystemManager()
24
+
25
+ @property
26
+ def auth_header(self):
27
+ token = gh_token()
28
+ if token:
29
+ log("GH_TOKEN will be used to perform requests")
30
+ return {"Authorization": f"token {token}"}
31
+ return None
32
+
33
+ def get_name(self):
34
+ return self._name
35
+
36
+ def get_driver_download_url(self, os_type):
37
+ return f"{self._url}/{self.get_driver_version_to_download()}/{self._name}_{os_type}.zip"
38
+
39
+ def get_driver_version_to_download(self):
40
+ """
41
+ Downloads version from parameter if version not None or "latest".
42
+ Downloads latest, if version is "latest" or browser could not been determined.
43
+ Downloads determined browser version driver in all other ways as a bonus fallback for lazy users.
44
+ """
45
+ if self._driver_version_to_download:
46
+ return self._driver_version_to_download
47
+
48
+ return self.get_latest_release_version()
49
+
50
+ def get_latest_release_version(self):
51
+ # type: () -> str
52
+ raise NotImplementedError("Please implement this method")
53
+
54
+ def get_browser_version_from_os(self):
55
+ """
56
+ Use-cases:
57
+ - for key in metadata;
58
+ - for printing nice logs;
59
+ - for fallback if version was not set at all.
60
+ Note: the fallback may have collisions in user cases when previous browser was not uninstalled properly.
61
+ """
62
+ if self._browser_version is None:
63
+ self._browser_version = self._os_system_manager.get_browser_version_from_os(self.get_browser_type())
64
+ return self._browser_version
65
+
66
+ def get_browser_type(self):
67
+ raise NotImplementedError("Please implement this method")
68
+
69
+ def get_binary_name(self, os_type):
70
+ driver_name = self.get_name()
71
+ driver_binary_name = (
72
+ "msedgedriver" if driver_name == "edgedriver" else driver_name
73
+ )
74
+ driver_binary_name = (
75
+ f"{driver_binary_name}.exe" if "win" in os_type else driver_binary_name
76
+ )
77
+ return driver_binary_name
@@ -0,0 +1,212 @@
1
+ import datetime
2
+ import json
3
+ import os
4
+
5
+ from webdriver_manager.core.config import wdm_local, get_xdist_worker_id
6
+ from webdriver_manager.core.constants import (
7
+ DEFAULT_PROJECT_ROOT_CACHE_PATH,
8
+ DEFAULT_USER_HOME_CACHE_PATH, ROOT_FOLDER_NAME,
9
+ )
10
+ from webdriver_manager.core.driver import Driver
11
+ from webdriver_manager.core.file_manager import FileManager, File
12
+ from webdriver_manager.core.logger import log
13
+ from webdriver_manager.core.os_manager import OperationSystemManager
14
+ from webdriver_manager.core.utils import get_date_diff
15
+
16
+
17
+ class DriverCacheManager(object):
18
+ def __init__(self, root_dir=None, valid_range=1, file_manager=None, os_system_manager=None):
19
+ self._root_dir = DEFAULT_USER_HOME_CACHE_PATH
20
+ is_wdm_local = wdm_local()
21
+ xdist_worker_id = get_xdist_worker_id()
22
+ if xdist_worker_id:
23
+ log(f"xdist worker is: {xdist_worker_id}")
24
+ self._root_dir = os.path.join(self._root_dir, xdist_worker_id)
25
+
26
+ if root_dir:
27
+ self._root_dir = os.path.join(root_dir, ROOT_FOLDER_NAME, xdist_worker_id)
28
+
29
+ if is_wdm_local:
30
+ self._root_dir = os.path.join(DEFAULT_PROJECT_ROOT_CACHE_PATH, xdist_worker_id)
31
+
32
+ self._drivers_root = "drivers"
33
+ self._drivers_json_path = os.path.join(self._root_dir, "drivers.json")
34
+ self._date_format = "%d/%m/%Y"
35
+ self._drivers_directory = os.path.join(self._root_dir, self._drivers_root)
36
+ self._cache_valid_days_range = valid_range
37
+ self._cache_key_driver_version = None
38
+ self._metadata_key = None
39
+ self._driver_binary_path = None
40
+ self._os_system_manager = os_system_manager
41
+ if not os_system_manager:
42
+ self._os_system_manager = OperationSystemManager()
43
+ self._file_manager = file_manager
44
+ if not self._file_manager:
45
+ self._file_manager = FileManager(self._os_system_manager)
46
+
47
+ def get_driver_lock_path(self, driver_name: str, os_type: str) -> str:
48
+ os.makedirs(self._root_dir, exist_ok=True)
49
+ return os.path.join(self._root_dir, f".wdm-lock-{driver_name}-{os_type}")
50
+
51
+ def save_archive_file(self, file: File, path):
52
+ return self._file_manager.save_archive_file(file, path)
53
+
54
+ def unpack_archive(self, archive, path):
55
+ return self._file_manager.unpack_archive(archive, path)
56
+
57
+ def save_file_to_cache(self, driver: Driver, file: File):
58
+ path = self.__get_path(driver)
59
+ archive = self.save_archive_file(file, path)
60
+ files = self.unpack_archive(archive, path)
61
+ binary = self.__get_binary(files, driver.get_name())
62
+ binary_path = os.path.join(path, binary)
63
+ self.__save_metadata(driver, binary_path)
64
+ log(f"Driver has been saved in cache [{path}]")
65
+ return binary_path
66
+
67
+ def __get_binary(self, files, driver_name):
68
+ if not files:
69
+ raise Exception(f"Can't find binary for {driver_name} among {files}")
70
+
71
+ if len(files) == 1:
72
+ return files[0]
73
+
74
+ for f in files:
75
+ if 'LICENSE' in f:
76
+ continue
77
+ if 'THIRD_PARTY' in f:
78
+ continue
79
+ if driver_name in f:
80
+ return f
81
+
82
+ raise Exception(f"Can't find binary for {driver_name} among {files}")
83
+
84
+ def __save_metadata(self, driver: Driver, binary_path, date=None):
85
+ if date is None:
86
+ date = datetime.date.today()
87
+
88
+ metadata = self.load_metadata_content()
89
+ key = self.__get_metadata_key(driver)
90
+ data = {
91
+ key: {
92
+ "timestamp": date.strftime(self._date_format),
93
+ "binary_path": binary_path,
94
+ }
95
+ }
96
+
97
+ metadata.update(data)
98
+ with open(self._drivers_json_path, "w+") as outfile:
99
+ json.dump(metadata, outfile, indent=4)
100
+
101
+ def get_os_type(self):
102
+ return self._os_system_manager.get_os_type()
103
+
104
+ def find_driver(self, driver: Driver):
105
+ """Find driver by '{os_type}_{driver_name}_{driver_version}_{browser_version}'."""
106
+ os_type = self.get_os_type()
107
+ driver_name = driver.get_name()
108
+ browser_type = driver.get_browser_type()
109
+ browser_version = self._os_system_manager.get_browser_version_from_os(browser_type)
110
+ if not browser_version:
111
+ return None
112
+
113
+ metadata = self.load_metadata_content()
114
+ requested_driver_version = getattr(driver, "_driver_version_to_download", None)
115
+ if not requested_driver_version:
116
+ cached_path = self.__find_driver_by_browser_version(
117
+ metadata=metadata,
118
+ os_type=os_type,
119
+ driver_name=driver_name,
120
+ browser_version=browser_version,
121
+ )
122
+ if cached_path:
123
+ return cached_path
124
+
125
+ driver_version = self.get_cache_key_driver_version(driver)
126
+
127
+ key = self.__get_metadata_key(driver)
128
+ if key not in metadata:
129
+ log(f'There is no [{os_type}] {driver_name} "{driver_version}" for browser {browser_type} '
130
+ f'"{browser_version}" in cache')
131
+ return None
132
+
133
+ driver_info = metadata[key]
134
+ path = driver_info["binary_path"]
135
+ if not os.path.exists(path):
136
+ return None
137
+
138
+ if not self.__is_valid(driver_info):
139
+ return None
140
+
141
+ path = driver_info["binary_path"]
142
+ log(f"Driver [{path}] found in cache")
143
+ return path
144
+
145
+ def __find_driver_by_browser_version(self, metadata, os_type, driver_name, browser_version):
146
+ prefix = f"{os_type}_{driver_name}_"
147
+ suffix = f"_for_{browser_version}"
148
+ candidates = []
149
+
150
+ for key, info in metadata.items():
151
+ if not key.startswith(prefix) or not key.endswith(suffix):
152
+ continue
153
+ path = info.get("binary_path")
154
+ timestamp = info.get("timestamp")
155
+ if not path or not os.path.exists(path):
156
+ continue
157
+ if not timestamp:
158
+ continue
159
+ if not self.__is_valid(info):
160
+ continue
161
+ try:
162
+ datetime.datetime.strptime(timestamp, self._date_format)
163
+ except ValueError:
164
+ continue
165
+ candidates.append((timestamp, path))
166
+
167
+ if not candidates:
168
+ return None
169
+
170
+ candidates.sort(key=lambda item: datetime.datetime.strptime(item[0], self._date_format), reverse=True)
171
+ path = candidates[0][1]
172
+ log(f"Driver [{path}] found in cache by browser version")
173
+ return path
174
+
175
+ def __is_valid(self, driver_info):
176
+ dates_diff = get_date_diff(
177
+ driver_info["timestamp"], datetime.date.today(), self._date_format
178
+ )
179
+ return dates_diff < self._cache_valid_days_range
180
+
181
+ def load_metadata_content(self):
182
+ if os.path.exists(self._drivers_json_path):
183
+ with open(self._drivers_json_path, "r") as outfile:
184
+ return json.load(outfile)
185
+ return {}
186
+
187
+ def __get_metadata_key(self, driver: Driver):
188
+ if self._metadata_key:
189
+ return self._metadata_key
190
+
191
+ driver_version = self.get_cache_key_driver_version(driver)
192
+ browser_version = driver.get_browser_version_from_os()
193
+ browser_version = browser_version if browser_version else ""
194
+ self._metadata_key = f"{self.get_os_type()}_{driver.get_name()}_{driver_version}" \
195
+ f"_for_{browser_version}"
196
+ return self._metadata_key
197
+
198
+ def get_cache_key_driver_version(self, driver: Driver):
199
+ if self._cache_key_driver_version:
200
+ return self._cache_key_driver_version
201
+ self._cache_key_driver_version = driver.get_driver_version_to_download()
202
+ return self._cache_key_driver_version
203
+
204
+ def __get_path(self, driver: Driver):
205
+ if self._driver_binary_path is None:
206
+ self._driver_binary_path = os.path.join(
207
+ self._drivers_directory,
208
+ driver.get_name(),
209
+ self.get_os_type(),
210
+ self.get_cache_key_driver_version(driver),
211
+ )
212
+ return self._driver_binary_path
@@ -0,0 +1,117 @@
1
+ import os
2
+ import re
3
+ import sys
4
+ import tarfile
5
+ import zipfile
6
+
7
+ from webdriver_manager.core.archive import Archive, LinuxZipFileWithPermissions
8
+ from webdriver_manager.core.os_manager import OperationSystemManager
9
+
10
+
11
+ class File(object):
12
+ def __init__(self, stream, file_name):
13
+ self.content = stream.content
14
+ self.__stream = stream
15
+ self.file_name = file_name
16
+ self.__temp_name = "driver"
17
+ self.__regex_filename = r"""filename.+"(.+)"|filename.+''(.+)|filename=([\w.-]+)"""
18
+
19
+ @property
20
+ def filename(self) -> str:
21
+ if self.file_name:
22
+ return self.file_name
23
+ try:
24
+ content = self.__stream.headers["content-disposition"]
25
+
26
+ content_disposition_list = re.split(";", content)
27
+ filenames = [re.findall(self.__regex_filename, element) for element in content_disposition_list]
28
+ filename = next(filter(None, next(filter(None, next(filter(None, filenames))))))
29
+ except KeyError:
30
+ filename = f"{self.__temp_name}.zip"
31
+ except (IndexError, StopIteration):
32
+ filename = f"{self.__temp_name}.exe"
33
+
34
+ if '"' in filename:
35
+ filename = filename.replace('"', "")
36
+
37
+ return filename
38
+
39
+
40
+ class FileManager(object):
41
+
42
+ def __init__(self, os_system_manager: OperationSystemManager):
43
+ self._os_system_manager = os_system_manager
44
+
45
+ def save_archive_file(self, file: File, directory: str):
46
+ os.makedirs(directory, exist_ok=True)
47
+
48
+ archive_path = f"{directory}{os.sep}{file.filename}"
49
+ with open(archive_path, "wb") as code:
50
+ code.write(file.content)
51
+ if not os.path.exists(archive_path):
52
+ raise FileExistsError(f"No file has been saved on such path {archive_path}")
53
+ return Archive(archive_path)
54
+
55
+ def unpack_archive(self, archive_file: Archive, target_dir):
56
+ file_path = archive_file.file_path
57
+ if file_path.endswith(".zip"):
58
+ return self.__extract_zip(archive_file, target_dir)
59
+ elif file_path.endswith(".tar.gz"):
60
+ return self.__extract_tar_file(archive_file, target_dir)
61
+
62
+ def __extract_zip(self, archive_file, to_directory):
63
+ zip_class = (LinuxZipFileWithPermissions if self._os_system_manager.get_os_name() == "linux" else zipfile.ZipFile)
64
+ archive = zip_class(archive_file.file_path)
65
+ self.__remove_file_dir_conflicts(archive.namelist(), to_directory)
66
+ try:
67
+ archive.extractall(to_directory)
68
+ except Exception as e:
69
+ if e.args[0] not in [26, 13] and e.args[1] not in [
70
+ "Text file busy",
71
+ "Permission denied",
72
+ ]:
73
+ raise e
74
+ file_names = []
75
+ for n in archive.namelist():
76
+ if "/" not in n:
77
+ file_names.append(n)
78
+ else:
79
+ file_path, file_name = n.rsplit("/", 1)
80
+ if not file_name:
81
+ continue
82
+ full_file_path = os.path.join(to_directory, file_path)
83
+ source = os.path.join(full_file_path, file_name)
84
+ destination = os.path.join(to_directory, file_name)
85
+ if not os.path.exists(source):
86
+ continue
87
+ os.replace(source, destination)
88
+ file_names.append(file_name)
89
+ return sorted(file_names, key=lambda x: x.lower())
90
+ return archive.namelist()
91
+
92
+ def __remove_file_dir_conflicts(self, names, to_directory):
93
+ """Remove stale files that conflict with directory entries in archive.
94
+
95
+ Example: if cache contains `<to_directory>/operadriver` as a file, and
96
+ archive now contains `operadriver/<binary>`, extraction would fail with
97
+ NotADirectoryError.
98
+ """
99
+ top_level_dirs = {name.split("/", 1)[0] for name in names if "/" in name}
100
+ for dir_name in top_level_dirs:
101
+ path = os.path.join(to_directory, dir_name)
102
+ if os.path.isfile(path):
103
+ os.remove(path)
104
+
105
+ def __extract_tar_file(self, archive_file, to_directory):
106
+ try:
107
+ tar = tarfile.open(archive_file.file_path, mode="r:gz")
108
+ except tarfile.ReadError:
109
+ tar = tarfile.open(archive_file.file_path, mode="r:bz2")
110
+ members = tar.getmembers()
111
+ # Python 3.13+ requires 'filter' to avoid deprecation warning
112
+ if sys.version_info >= (3, 13):
113
+ tar.extractall(to_directory, filter="fully_trusted")
114
+ else:
115
+ tar.extractall(to_directory)
116
+ tar.close()
117
+ return [x.name for x in members]
@@ -0,0 +1,38 @@
1
+ import requests
2
+ from requests import Response, exceptions
3
+
4
+ from webdriver_manager.core.config import ssl_verify
5
+
6
+
7
+ class HttpClient:
8
+ def get(self, url, params=None, **kwargs) -> Response:
9
+ raise NotImplementedError
10
+
11
+ @staticmethod
12
+ def validate_response(resp: requests.Response):
13
+ status_code = resp.status_code
14
+ if status_code == 404:
15
+ raise ValueError(f"There is no such driver by url {resp.url}")
16
+ elif status_code == 401:
17
+ raise ValueError(f"API Rate limit exceeded. You have to add GH_TOKEN!!!")
18
+ elif resp.status_code != 200:
19
+ raise ValueError(
20
+ f"response body:\n{resp.text}\n"
21
+ f"request url:\n{resp.request.url}\n"
22
+ f"response headers:\n{dict(resp.headers)}\n"
23
+ )
24
+
25
+
26
+ class WDMHttpClient(HttpClient):
27
+ def __init__(self):
28
+ self._ssl_verify = ssl_verify()
29
+
30
+ def get(self, url, **kwargs) -> Response:
31
+ try:
32
+ resp = requests.get(
33
+ url=url, verify=self._ssl_verify, stream=True, **kwargs)
34
+ except exceptions.ConnectionError:
35
+ raise exceptions.ConnectionError(f"Could not reach host. Are you offline?")
36
+ self.validate_response(resp)
37
+ return resp
38
+
@@ -0,0 +1,31 @@
1
+ import logging
2
+
3
+ from webdriver_manager.core.config import wdm_log_level
4
+
5
+ __logger = logging.getLogger("WDM")
6
+ __logger.addHandler(logging.NullHandler())
7
+
8
+
9
+ def log(text):
10
+ """Emitting the log message."""
11
+ __logger.log(wdm_log_level(), text)
12
+
13
+
14
+ def set_logger(logger):
15
+ """
16
+ Set the global logger.
17
+
18
+ Parameters
19
+ ----------
20
+ logger : object
21
+ Any logger-like object that provides a callable ``log(level, message)`` method.
22
+
23
+ Returns None
24
+ """
25
+
26
+ if not callable(getattr(logger, "log", None)):
27
+ raise ValueError("The logger must provide a callable log(level, message) method")
28
+
29
+ # Bind the logger input to the global logger
30
+ global __logger
31
+ __logger = logger