seleniumbase 4.24.11__py3-none-any.whl → 4.33.15__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
)
|