seleniumbase 4.24.11__py3-none-any.whl → 4.33.15__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.
- sbase/__init__.py +1 -0
- sbase/steps.py +7 -0
- seleniumbase/__init__.py +16 -7
- seleniumbase/__version__.py +1 -1
- seleniumbase/behave/behave_sb.py +97 -32
- seleniumbase/common/decorators.py +16 -7
- seleniumbase/config/proxy_list.py +3 -3
- seleniumbase/config/settings.py +4 -0
- seleniumbase/console_scripts/logo_helper.py +47 -8
- seleniumbase/console_scripts/run.py +345 -335
- seleniumbase/console_scripts/sb_behave_gui.py +5 -12
- seleniumbase/console_scripts/sb_caseplans.py +6 -13
- seleniumbase/console_scripts/sb_commander.py +5 -12
- seleniumbase/console_scripts/sb_install.py +62 -54
- seleniumbase/console_scripts/sb_mkchart.py +13 -20
- seleniumbase/console_scripts/sb_mkdir.py +11 -17
- seleniumbase/console_scripts/sb_mkfile.py +69 -43
- seleniumbase/console_scripts/sb_mkpres.py +13 -20
- seleniumbase/console_scripts/sb_mkrec.py +88 -21
- seleniumbase/console_scripts/sb_objectify.py +30 -30
- seleniumbase/console_scripts/sb_print.py +5 -12
- seleniumbase/console_scripts/sb_recorder.py +16 -11
- seleniumbase/core/browser_launcher.py +1658 -221
- seleniumbase/core/log_helper.py +42 -27
- seleniumbase/core/mysql.py +1 -4
- seleniumbase/core/proxy_helper.py +35 -30
- seleniumbase/core/recorder_helper.py +24 -5
- seleniumbase/core/sb_cdp.py +1951 -0
- seleniumbase/core/sb_driver.py +162 -8
- seleniumbase/core/settings_parser.py +6 -0
- seleniumbase/core/style_sheet.py +10 -0
- seleniumbase/extensions/recorder.zip +0 -0
- seleniumbase/fixtures/base_case.py +1225 -614
- seleniumbase/fixtures/constants.py +10 -1
- seleniumbase/fixtures/js_utils.py +171 -144
- seleniumbase/fixtures/page_actions.py +177 -13
- seleniumbase/fixtures/page_utils.py +25 -53
- seleniumbase/fixtures/shared_utils.py +97 -11
- seleniumbase/js_code/active_css_js.py +1 -1
- seleniumbase/js_code/recorder_js.py +1 -1
- seleniumbase/plugins/base_plugin.py +2 -3
- seleniumbase/plugins/driver_manager.py +340 -65
- seleniumbase/plugins/pytest_plugin.py +276 -47
- seleniumbase/plugins/sb_manager.py +412 -99
- seleniumbase/plugins/selenium_plugin.py +122 -17
- seleniumbase/translate/translator.py +0 -7
- seleniumbase/undetected/__init__.py +59 -52
- seleniumbase/undetected/cdp.py +0 -1
- seleniumbase/undetected/cdp_driver/__init__.py +1 -0
- seleniumbase/undetected/cdp_driver/_contradict.py +110 -0
- seleniumbase/undetected/cdp_driver/browser.py +829 -0
- seleniumbase/undetected/cdp_driver/cdp_util.py +458 -0
- seleniumbase/undetected/cdp_driver/config.py +334 -0
- seleniumbase/undetected/cdp_driver/connection.py +639 -0
- seleniumbase/undetected/cdp_driver/element.py +1168 -0
- seleniumbase/undetected/cdp_driver/tab.py +1323 -0
- seleniumbase/undetected/dprocess.py +4 -7
- seleniumbase/undetected/options.py +6 -8
- seleniumbase/undetected/patcher.py +11 -13
- seleniumbase/undetected/reactor.py +0 -1
- seleniumbase/undetected/webelement.py +16 -3
- {seleniumbase-4.24.11.dist-info → seleniumbase-4.33.15.dist-info}/LICENSE +1 -1
- {seleniumbase-4.24.11.dist-info → seleniumbase-4.33.15.dist-info}/METADATA +299 -252
- {seleniumbase-4.24.11.dist-info → seleniumbase-4.33.15.dist-info}/RECORD +67 -69
- {seleniumbase-4.24.11.dist-info → seleniumbase-4.33.15.dist-info}/WHEEL +1 -1
- sbase/ReadMe.txt +0 -2
- seleniumbase/ReadMe.md +0 -25
- seleniumbase/common/ReadMe.md +0 -71
- seleniumbase/console_scripts/ReadMe.md +0 -731
- seleniumbase/drivers/ReadMe.md +0 -27
- seleniumbase/extensions/ReadMe.md +0 -12
- seleniumbase/masterqa/ReadMe.md +0 -61
- seleniumbase/resources/ReadMe.md +0 -31
- seleniumbase/resources/favicon.ico +0 -0
- seleniumbase/utilities/selenium_grid/ReadMe.md +0 -84
- seleniumbase/utilities/selenium_ide/ReadMe.md +0 -111
- {seleniumbase-4.24.11.dist-info → seleniumbase-4.33.15.dist-info}/entry_points.txt +0 -0
- {seleniumbase-4.24.11.dist-info → seleniumbase-4.33.15.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,334 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
import pathlib
|
4
|
+
import secrets
|
5
|
+
import sys
|
6
|
+
import tempfile
|
7
|
+
import zipfile
|
8
|
+
from seleniumbase.config import settings
|
9
|
+
from typing import Union, List, Optional
|
10
|
+
|
11
|
+
__all__ = [
|
12
|
+
"Config",
|
13
|
+
"find_chrome_executable",
|
14
|
+
"temp_profile_dir",
|
15
|
+
"is_root",
|
16
|
+
"is_posix",
|
17
|
+
"PathLike",
|
18
|
+
]
|
19
|
+
|
20
|
+
logger = logging.getLogger(__name__)
|
21
|
+
is_posix = sys.platform.startswith(("darwin", "cygwin", "linux", "linux2"))
|
22
|
+
|
23
|
+
PathLike = Union[str, pathlib.Path]
|
24
|
+
AUTO = None
|
25
|
+
|
26
|
+
|
27
|
+
class Config:
|
28
|
+
"""Config object"""
|
29
|
+
|
30
|
+
def __init__(
|
31
|
+
self,
|
32
|
+
user_data_dir: Optional[PathLike] = AUTO,
|
33
|
+
headless: Optional[bool] = False,
|
34
|
+
incognito: Optional[bool] = False,
|
35
|
+
guest: Optional[bool] = False,
|
36
|
+
browser_executable_path: Optional[PathLike] = AUTO,
|
37
|
+
browser_args: Optional[List[str]] = AUTO,
|
38
|
+
sandbox: Optional[bool] = True,
|
39
|
+
lang: Optional[str] = "en-US",
|
40
|
+
host: str = AUTO,
|
41
|
+
port: int = AUTO,
|
42
|
+
expert: bool = AUTO,
|
43
|
+
**kwargs: dict,
|
44
|
+
):
|
45
|
+
"""
|
46
|
+
Creates a config object.
|
47
|
+
Can be called without any arguments to generate a best-practice config,
|
48
|
+
which is recommended.
|
49
|
+
Calling the object, eg: myconfig(), returns the list of arguments which
|
50
|
+
are provided to the browser.
|
51
|
+
Additional args can be added using the :py:obj:`~add_argument method`.
|
52
|
+
Instances of this class are usually not instantiated by end users.
|
53
|
+
:param user_data_dir: the data directory to use
|
54
|
+
:param headless: set to True for headless mode
|
55
|
+
:param browser_executable_path:
|
56
|
+
Specify browser executable, instead of using autodetect.
|
57
|
+
:param browser_args: Forwarded to browser executable.
|
58
|
+
Eg: ["--some-chromeparam=somevalue", "some-other-param=someval"]
|
59
|
+
:param sandbox: disables sandbox
|
60
|
+
:param autodiscover_targets: use autodiscovery of targets
|
61
|
+
:param lang:
|
62
|
+
Language string to use other than the default "en-US,en;q=0.9"
|
63
|
+
:param expert: When set to True, "expert" mode is enabled.
|
64
|
+
This adds: --disable-web-security --disable-site-isolation-trials,
|
65
|
+
as well as some scripts and patching useful for debugging.
|
66
|
+
(For example, ensuring shadow-root is always in "open" mode.)
|
67
|
+
:param kwargs:
|
68
|
+
:type user_data_dir: PathLike
|
69
|
+
:type headless: bool
|
70
|
+
:type browser_executable_path: PathLike
|
71
|
+
:type browser_args: list[str]
|
72
|
+
:type sandbox: bool
|
73
|
+
:type lang: str
|
74
|
+
:type kwargs: dict
|
75
|
+
"""
|
76
|
+
if not browser_args:
|
77
|
+
browser_args = []
|
78
|
+
if not user_data_dir:
|
79
|
+
self._user_data_dir = temp_profile_dir()
|
80
|
+
self._custom_data_dir = False
|
81
|
+
else:
|
82
|
+
self.user_data_dir = user_data_dir
|
83
|
+
if not browser_executable_path:
|
84
|
+
browser_executable_path = find_chrome_executable()
|
85
|
+
self._browser_args = browser_args
|
86
|
+
self.browser_executable_path = browser_executable_path
|
87
|
+
self.headless = headless
|
88
|
+
self.incognito = incognito
|
89
|
+
self.guest = guest
|
90
|
+
self.sandbox = sandbox
|
91
|
+
self.host = host
|
92
|
+
self.port = port
|
93
|
+
self.expert = expert
|
94
|
+
self._extensions = []
|
95
|
+
# When using posix-ish operating system and running as root,
|
96
|
+
# you must use no_sandbox=True
|
97
|
+
if is_posix and is_root() and sandbox:
|
98
|
+
logger.info("Detected root usage, auto-disabling sandbox mode.")
|
99
|
+
self.sandbox = False
|
100
|
+
self.autodiscover_targets = True
|
101
|
+
self.lang = lang
|
102
|
+
# Other keyword args will be accessible by attribute
|
103
|
+
self.__dict__.update(kwargs)
|
104
|
+
super().__init__()
|
105
|
+
start_width = settings.CHROME_START_WIDTH
|
106
|
+
start_height = settings.CHROME_START_HEIGHT
|
107
|
+
start_x = settings.WINDOW_START_X
|
108
|
+
start_y = settings.WINDOW_START_Y
|
109
|
+
self._default_browser_args = [
|
110
|
+
"--window-size=%s,%s" % (start_width, start_height),
|
111
|
+
"--window-position=%s,%s" % (start_x, start_y),
|
112
|
+
"--remote-allow-origins=*",
|
113
|
+
"--no-first-run",
|
114
|
+
"--no-service-autorun",
|
115
|
+
"--disable-auto-reload",
|
116
|
+
"--no-default-browser-check",
|
117
|
+
"--homepage=about:blank",
|
118
|
+
"--no-pings",
|
119
|
+
"--wm-window-animations-disabled",
|
120
|
+
"--animation-duration-scale=0",
|
121
|
+
"--enable-privacy-sandbox-ads-apis",
|
122
|
+
"--safebrowsing-disable-download-protection",
|
123
|
+
'--simulate-outdated-no-au="Tue, 31 Dec 2099 23:59:59 GMT"',
|
124
|
+
"--password-store=basic",
|
125
|
+
"--deny-permission-prompts",
|
126
|
+
"--disable-infobars",
|
127
|
+
"--disable-breakpad",
|
128
|
+
"--disable-prompt-on-repost",
|
129
|
+
"--disable-password-generation",
|
130
|
+
"--disable-ipc-flooding-protection",
|
131
|
+
"--disable-background-timer-throttling",
|
132
|
+
"--disable-search-engine-choice-screen",
|
133
|
+
"--disable-backgrounding-occluded-windows",
|
134
|
+
"--disable-client-side-phishing-detection",
|
135
|
+
"--disable-top-sites",
|
136
|
+
"--disable-translate",
|
137
|
+
"--disable-renderer-backgrounding",
|
138
|
+
"--disable-background-networking",
|
139
|
+
"--disable-dev-shm-usage",
|
140
|
+
"--disable-features=IsolateOrigins,site-per-process,Translate,"
|
141
|
+
"InsecureDownloadWarnings,DownloadBubble,DownloadBubbleV2,"
|
142
|
+
"OptimizationTargetPrediction,OptimizationGuideModelDownloading,"
|
143
|
+
"SidePanelPinning,UserAgentClientHint,PrivacySandboxSettings4",
|
144
|
+
]
|
145
|
+
|
146
|
+
@property
|
147
|
+
def browser_args(self):
|
148
|
+
return sorted(self._default_browser_args + self._browser_args)
|
149
|
+
|
150
|
+
@property
|
151
|
+
def user_data_dir(self):
|
152
|
+
return self._user_data_dir
|
153
|
+
|
154
|
+
@user_data_dir.setter
|
155
|
+
def user_data_dir(self, path: PathLike):
|
156
|
+
self._user_data_dir = str(path)
|
157
|
+
self._custom_data_dir = True
|
158
|
+
|
159
|
+
@property
|
160
|
+
def uses_custom_data_dir(self) -> bool:
|
161
|
+
return self._custom_data_dir
|
162
|
+
|
163
|
+
def add_extension(self, extension_path: PathLike):
|
164
|
+
"""
|
165
|
+
Adds an extension to load. You can set the extension_path to a
|
166
|
+
folder (containing the manifest), or an extension zip file (.crx)
|
167
|
+
:param extension_path:
|
168
|
+
"""
|
169
|
+
path = pathlib.Path(extension_path)
|
170
|
+
if not path.exists():
|
171
|
+
raise FileNotFoundError(
|
172
|
+
"Could not find anything here: %s" % str(path)
|
173
|
+
)
|
174
|
+
if path.is_file():
|
175
|
+
tf = tempfile.mkdtemp(
|
176
|
+
prefix="extension_", suffix=secrets.token_hex(4)
|
177
|
+
)
|
178
|
+
with zipfile.ZipFile(path, "r") as z:
|
179
|
+
z.extractall(tf)
|
180
|
+
self._extensions.append(tf)
|
181
|
+
elif path.is_dir():
|
182
|
+
for item in path.rglob("manifest.*"):
|
183
|
+
path = item.parent
|
184
|
+
self._extensions.append(path)
|
185
|
+
|
186
|
+
def __call__(self):
|
187
|
+
# The host and port will be added when starting the browser.
|
188
|
+
# By the time it starts, the port is probably already taken.
|
189
|
+
args = self._default_browser_args.copy()
|
190
|
+
args += ["--user-data-dir=%s" % self.user_data_dir]
|
191
|
+
args += ["--disable-features=IsolateOrigins,site-per-process"]
|
192
|
+
args += ["--disable-session-crashed-bubble"]
|
193
|
+
if self.expert:
|
194
|
+
args += [
|
195
|
+
"--disable-web-security",
|
196
|
+
"--disable-site-isolation-trials",
|
197
|
+
]
|
198
|
+
if self._browser_args:
|
199
|
+
args.extend([arg for arg in self._browser_args if arg not in args])
|
200
|
+
if self.headless:
|
201
|
+
args.append("--headless=new")
|
202
|
+
if self.incognito:
|
203
|
+
args.append("--incognito")
|
204
|
+
if self.guest:
|
205
|
+
args.append("--guest")
|
206
|
+
if not self.sandbox:
|
207
|
+
args.append("--no-sandbox")
|
208
|
+
if self.host:
|
209
|
+
args.append("--remote-debugging-host=%s" % self.host)
|
210
|
+
if self.port:
|
211
|
+
args.append("--remote-debugging-port=%s" % self.port)
|
212
|
+
return args
|
213
|
+
|
214
|
+
def add_argument(self, arg: str):
|
215
|
+
if any(
|
216
|
+
x in arg.lower()
|
217
|
+
for x in [
|
218
|
+
"headless",
|
219
|
+
"data-dir",
|
220
|
+
"data_dir",
|
221
|
+
"no-sandbox",
|
222
|
+
"no_sandbox",
|
223
|
+
"lang",
|
224
|
+
]
|
225
|
+
):
|
226
|
+
raise ValueError(
|
227
|
+
'"%s" is not allowed. Please use one of the '
|
228
|
+
'attributes of the Config object to set it.'
|
229
|
+
% arg
|
230
|
+
)
|
231
|
+
self._browser_args.append(arg)
|
232
|
+
|
233
|
+
def __repr__(self):
|
234
|
+
s = f"{self.__class__.__name__}"
|
235
|
+
for k, v in ({**self.__dict__, **self.__class__.__dict__}).items():
|
236
|
+
if k[0] == "_":
|
237
|
+
continue
|
238
|
+
if not v:
|
239
|
+
continue
|
240
|
+
if isinstance(v, property):
|
241
|
+
v = getattr(self, k)
|
242
|
+
if callable(v):
|
243
|
+
continue
|
244
|
+
s += f"\n\t{k} = {v}"
|
245
|
+
return s
|
246
|
+
|
247
|
+
|
248
|
+
def is_root():
|
249
|
+
"""
|
250
|
+
Helper function to determine if the user is trying to launch chrome
|
251
|
+
under linux as root, which needs some alternative handling.
|
252
|
+
"""
|
253
|
+
import ctypes
|
254
|
+
import os
|
255
|
+
|
256
|
+
try:
|
257
|
+
return os.getuid() == 0
|
258
|
+
except AttributeError:
|
259
|
+
return ctypes.windll.shell32.IsUserAnAdmin() != 0
|
260
|
+
|
261
|
+
|
262
|
+
def temp_profile_dir():
|
263
|
+
"""Generate a temp dir (path)"""
|
264
|
+
path = os.path.normpath(tempfile.mkdtemp(prefix="uc_"))
|
265
|
+
return path
|
266
|
+
|
267
|
+
|
268
|
+
def find_chrome_executable(return_all=False):
|
269
|
+
"""
|
270
|
+
Finds the chrome, beta, canary, chromium executable
|
271
|
+
and returns the disk path.
|
272
|
+
"""
|
273
|
+
candidates = []
|
274
|
+
if is_posix:
|
275
|
+
for item in os.environ.get("PATH").split(os.pathsep):
|
276
|
+
for subitem in (
|
277
|
+
"google-chrome",
|
278
|
+
"chromium",
|
279
|
+
"chromium-browser",
|
280
|
+
"chrome",
|
281
|
+
"google-chrome-stable",
|
282
|
+
):
|
283
|
+
candidates.append(os.sep.join((item, subitem)))
|
284
|
+
if "darwin" in sys.platform:
|
285
|
+
candidates += [
|
286
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
287
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
288
|
+
]
|
289
|
+
else:
|
290
|
+
for item in map(
|
291
|
+
os.environ.get,
|
292
|
+
(
|
293
|
+
"PROGRAMFILES",
|
294
|
+
"PROGRAMFILES(X86)",
|
295
|
+
"LOCALAPPDATA",
|
296
|
+
"PROGRAMW6432",
|
297
|
+
),
|
298
|
+
):
|
299
|
+
if item is not None:
|
300
|
+
for subitem in (
|
301
|
+
"Google/Chrome/Application",
|
302
|
+
"Google/Chrome Beta/Application",
|
303
|
+
"Google/Chrome Canary/Application",
|
304
|
+
):
|
305
|
+
candidates.append(
|
306
|
+
os.sep.join((item, subitem, "chrome.exe"))
|
307
|
+
)
|
308
|
+
rv = []
|
309
|
+
for candidate in candidates:
|
310
|
+
if os.path.exists(candidate) and os.access(candidate, os.X_OK):
|
311
|
+
logger.debug("%s is a valid candidate... " % candidate)
|
312
|
+
rv.append(candidate)
|
313
|
+
else:
|
314
|
+
logger.debug(
|
315
|
+
"%s is not a valid candidate because it doesn't exist "
|
316
|
+
"or isn't an executable."
|
317
|
+
% candidate
|
318
|
+
)
|
319
|
+
winner = None
|
320
|
+
if return_all and rv:
|
321
|
+
return rv
|
322
|
+
if rv and len(rv) > 1:
|
323
|
+
# Assuming the shortest path wins
|
324
|
+
winner = min(rv, key=lambda x: len(x))
|
325
|
+
elif len(rv) == 1:
|
326
|
+
winner = rv[0]
|
327
|
+
if winner:
|
328
|
+
return os.path.normpath(winner)
|
329
|
+
raise FileNotFoundError(
|
330
|
+
"Could not find a valid chrome browser binary. "
|
331
|
+
"Please make sure Chrome is installed. "
|
332
|
+
"Or use the keyword argument: "
|
333
|
+
"'browser_executable_path=/path/to/your/browser'."
|
334
|
+
)
|