seleniumbase 4.24.10__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.
Files changed (79) hide show
  1. sbase/__init__.py +1 -0
  2. sbase/steps.py +7 -0
  3. seleniumbase/__init__.py +16 -7
  4. seleniumbase/__version__.py +1 -1
  5. seleniumbase/behave/behave_sb.py +97 -32
  6. seleniumbase/common/decorators.py +16 -7
  7. seleniumbase/config/proxy_list.py +3 -3
  8. seleniumbase/config/settings.py +4 -0
  9. seleniumbase/console_scripts/logo_helper.py +47 -8
  10. seleniumbase/console_scripts/run.py +345 -335
  11. seleniumbase/console_scripts/sb_behave_gui.py +5 -12
  12. seleniumbase/console_scripts/sb_caseplans.py +6 -13
  13. seleniumbase/console_scripts/sb_commander.py +5 -12
  14. seleniumbase/console_scripts/sb_install.py +62 -54
  15. seleniumbase/console_scripts/sb_mkchart.py +13 -20
  16. seleniumbase/console_scripts/sb_mkdir.py +11 -17
  17. seleniumbase/console_scripts/sb_mkfile.py +69 -43
  18. seleniumbase/console_scripts/sb_mkpres.py +13 -20
  19. seleniumbase/console_scripts/sb_mkrec.py +88 -21
  20. seleniumbase/console_scripts/sb_objectify.py +30 -30
  21. seleniumbase/console_scripts/sb_print.py +5 -12
  22. seleniumbase/console_scripts/sb_recorder.py +16 -11
  23. seleniumbase/core/browser_launcher.py +1658 -221
  24. seleniumbase/core/detect_b_ver.py +7 -8
  25. seleniumbase/core/log_helper.py +42 -27
  26. seleniumbase/core/mysql.py +1 -4
  27. seleniumbase/core/proxy_helper.py +35 -30
  28. seleniumbase/core/recorder_helper.py +24 -5
  29. seleniumbase/core/sb_cdp.py +1951 -0
  30. seleniumbase/core/sb_driver.py +162 -8
  31. seleniumbase/core/settings_parser.py +6 -0
  32. seleniumbase/core/style_sheet.py +10 -0
  33. seleniumbase/extensions/recorder.zip +0 -0
  34. seleniumbase/fixtures/base_case.py +1234 -632
  35. seleniumbase/fixtures/constants.py +10 -1
  36. seleniumbase/fixtures/js_utils.py +171 -144
  37. seleniumbase/fixtures/page_actions.py +177 -13
  38. seleniumbase/fixtures/page_utils.py +25 -53
  39. seleniumbase/fixtures/shared_utils.py +97 -11
  40. seleniumbase/js_code/active_css_js.py +1 -1
  41. seleniumbase/js_code/recorder_js.py +1 -1
  42. seleniumbase/plugins/base_plugin.py +2 -3
  43. seleniumbase/plugins/driver_manager.py +340 -65
  44. seleniumbase/plugins/pytest_plugin.py +276 -47
  45. seleniumbase/plugins/sb_manager.py +412 -99
  46. seleniumbase/plugins/selenium_plugin.py +122 -17
  47. seleniumbase/translate/translator.py +0 -7
  48. seleniumbase/undetected/__init__.py +59 -52
  49. seleniumbase/undetected/cdp.py +0 -1
  50. seleniumbase/undetected/cdp_driver/__init__.py +1 -0
  51. seleniumbase/undetected/cdp_driver/_contradict.py +110 -0
  52. seleniumbase/undetected/cdp_driver/browser.py +829 -0
  53. seleniumbase/undetected/cdp_driver/cdp_util.py +458 -0
  54. seleniumbase/undetected/cdp_driver/config.py +334 -0
  55. seleniumbase/undetected/cdp_driver/connection.py +639 -0
  56. seleniumbase/undetected/cdp_driver/element.py +1168 -0
  57. seleniumbase/undetected/cdp_driver/tab.py +1323 -0
  58. seleniumbase/undetected/dprocess.py +4 -7
  59. seleniumbase/undetected/options.py +6 -8
  60. seleniumbase/undetected/patcher.py +11 -13
  61. seleniumbase/undetected/reactor.py +0 -1
  62. seleniumbase/undetected/webelement.py +16 -3
  63. {seleniumbase-4.24.10.dist-info → seleniumbase-4.33.15.dist-info}/LICENSE +1 -1
  64. {seleniumbase-4.24.10.dist-info → seleniumbase-4.33.15.dist-info}/METADATA +299 -252
  65. {seleniumbase-4.24.10.dist-info → seleniumbase-4.33.15.dist-info}/RECORD +68 -70
  66. {seleniumbase-4.24.10.dist-info → seleniumbase-4.33.15.dist-info}/WHEEL +1 -1
  67. sbase/ReadMe.txt +0 -2
  68. seleniumbase/ReadMe.md +0 -25
  69. seleniumbase/common/ReadMe.md +0 -71
  70. seleniumbase/console_scripts/ReadMe.md +0 -731
  71. seleniumbase/drivers/ReadMe.md +0 -27
  72. seleniumbase/extensions/ReadMe.md +0 -12
  73. seleniumbase/masterqa/ReadMe.md +0 -61
  74. seleniumbase/resources/ReadMe.md +0 -31
  75. seleniumbase/resources/favicon.ico +0 -0
  76. seleniumbase/utilities/selenium_grid/ReadMe.md +0 -84
  77. seleniumbase/utilities/selenium_ide/ReadMe.md +0 -111
  78. {seleniumbase-4.24.10.dist-info → seleniumbase-4.33.15.dist-info}/entry_points.txt +0 -0
  79. {seleniumbase-4.24.10.dist-info → seleniumbase-4.33.15.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  """Selenium Plugin for SeleniumBase tests that run with pynose / nosetests"""
2
2
  import sys
3
+ from contextlib import suppress
3
4
  from nose.plugins import Plugin
4
5
  from seleniumbase import config as sb_config
5
6
  from seleniumbase.config import settings
@@ -40,11 +41,14 @@ class SeleniumBrowser(Plugin):
40
41
  --binary-location=PATH (Set path of the Chromium browser binary to use.)
41
42
  --driver-version=VER (Set the chromedriver or uc_driver version to use.)
42
43
  --sjw (Skip JS Waits for readyState to be "complete" or Angular to load.)
44
+ --wfa (Wait for AngularJS to be done loading after specific web actions.)
43
45
  --pls=PLS (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".)
44
- --headless (Run tests in headless mode. The default arg on Linux OS.)
45
- --headless2 (Use the new headless mode, which supports extensions.)
46
+ --headless (The default headless mode. Linux uses this mode by default.)
47
+ --headless1 (Use Chrome's old headless mode. Fast, but has limitations.)
48
+ --headless2 (Use Chrome's new headless mode, which supports extensions.)
46
49
  --headed (Run tests in headed/GUI mode on Linux OS, where not default.)
47
50
  --xvfb (Run tests using the Xvfb virtual display server on Linux OS.)
51
+ --xvfb-metrics=STRING (Set Xvfb display size on Linux: "Width,Height".)
48
52
  --locale=LOCALE_CODE (Set the Language Locale Code for the web browser.)
49
53
  --interval=SECONDS (The autoplay interval for presentations & tour steps)
50
54
  --start-page=URL (The starting URL for the web browser when tests begin.)
@@ -60,10 +64,12 @@ class SeleniumBrowser(Plugin):
60
64
  --block-images (Block images from loading during tests.)
61
65
  --do-not-track (Indicate to websites that you don't want to be tracked.)
62
66
  --verify-delay=SECONDS (The delay before MasterQA verification checks.)
67
+ --ee / --esc-end (Lets the user end the current test via the ESC key.)
63
68
  --recorder (Enables the Recorder for turning browser actions into code.)
64
69
  --rec-behave (Same as Recorder Mode, but also generates behave-gherkin.)
65
70
  --rec-sleep (If the Recorder is enabled, also records self.sleep calls.)
66
71
  --rec-print (If the Recorder is enabled, prints output after tests end.)
72
+ --disable-cookies (Disable Cookies on websites. Pages might break!)
67
73
  --disable-js (Disable JavaScript on websites. Pages might break!)
68
74
  --disable-csp (Disable the Content Security Policy of websites.)
69
75
  --disable-ws (Disable Web Security on Chromium-based browsers.)
@@ -80,6 +86,7 @@ class SeleniumBrowser(Plugin):
80
86
  --dark (Enable Chrome's Dark mode.)
81
87
  --devtools (Open Chrome's DevTools when the browser opens.)
82
88
  --disable-beforeunload (Disable the "beforeunload" event on Chrome.)
89
+ --window-position=X,Y (Set the browser's starting window position.)
83
90
  --window-size=WIDTH,HEIGHT (Set the browser's starting window size.)
84
91
  --maximize (Start tests with the browser window maximized.)
85
92
  --screenshot (Save a screenshot at the end of each test.)
@@ -177,6 +184,17 @@ class SeleniumBrowser(Plugin):
177
184
  and wait_for_angularjs(), which are part of many
178
185
  SeleniumBase methods for improving reliability.""",
179
186
  )
187
+ parser.addoption(
188
+ "--wfa",
189
+ "--wait_for_angularjs",
190
+ "--wait-for-angularjs",
191
+ action="store_true",
192
+ dest="wait_for_angularjs",
193
+ default=False,
194
+ help="""Add waiting for AngularJS. (The default setting
195
+ was changed to no longer wait for AngularJS to
196
+ finish loading as an extra JavaScript call.)""",
197
+ )
180
198
  parser.addoption(
181
199
  "--protocol",
182
200
  action="store",
@@ -421,6 +439,15 @@ class SeleniumBrowser(Plugin):
421
439
  UNLESS using a virtual display with Xvfb.
422
440
  Default: False on Mac/Windows. True on Linux.""",
423
441
  )
442
+ parser.addoption(
443
+ "--headless1",
444
+ action="store_true",
445
+ dest="headless1",
446
+ default=False,
447
+ help="""This option activates the old headless mode,
448
+ which is faster, but has limitations.
449
+ (May be phased out by Chrome in the future.)""",
450
+ )
424
451
  parser.addoption(
425
452
  "--headless2",
426
453
  action="store_true",
@@ -452,6 +479,17 @@ class SeleniumBrowser(Plugin):
452
479
  will no longer be enabled by default on Linux.
453
480
  Default: False. (Linux-ONLY!)""",
454
481
  )
482
+ parser.addoption(
483
+ "--xvfb-metrics",
484
+ "--xvfb_metrics",
485
+ action="store",
486
+ dest="xvfb_metrics",
487
+ default=None,
488
+ help="""Customize the Xvfb metrics (Width,Height) on Linux.
489
+ Format: A comma-separated string with the 2 values.
490
+ Examples: "1920,1080" or "1366,768" or "1024,768".
491
+ Default: None. (None: "1366,768". Min: "1024,768".)""",
492
+ )
455
493
  parser.addoption(
456
494
  "--locale_code",
457
495
  "--locale-code",
@@ -613,6 +651,16 @@ class SeleniumBrowser(Plugin):
613
651
  help="""Setting this overrides the default wait time
614
652
  before each MasterQA verification pop-up.""",
615
653
  )
654
+ parser.addoption(
655
+ "--esc-end",
656
+ "--esc_end",
657
+ "--ee",
658
+ action="store_true",
659
+ dest="esc_end",
660
+ default=False,
661
+ help="""End the current test early via the ESC key.
662
+ The test will be marked as skipped.""",
663
+ )
616
664
  parser.addoption(
617
665
  "--recorder",
618
666
  "--record",
@@ -663,6 +711,15 @@ class SeleniumBrowser(Plugin):
663
711
  help="""The option to disable JavaScript on web pages.
664
712
  Warning: Most web pages will stop working!""",
665
713
  )
714
+ parser.addoption(
715
+ "--disable_cookies",
716
+ "--disable-cookies",
717
+ action="store_true",
718
+ dest="disable_cookies",
719
+ default=False,
720
+ help="""The option to disable Cookies on web pages.
721
+ Warning: Several pages may stop working!""",
722
+ )
666
723
  parser.addoption(
667
724
  "--disable_csp",
668
725
  "--disable-csp",
@@ -682,6 +739,7 @@ class SeleniumBrowser(Plugin):
682
739
  parser.addoption(
683
740
  "--disable_ws",
684
741
  "--disable-ws",
742
+ "--dws",
685
743
  "--disable-web-security",
686
744
  action="store_true",
687
745
  dest="disable_ws",
@@ -863,6 +921,17 @@ class SeleniumBrowser(Plugin):
863
921
  on Chromium browsers (Chrome or Edge).
864
922
  This is already the default Firefox option.""",
865
923
  )
924
+ parser.addoption(
925
+ "--window-position",
926
+ "--window_position",
927
+ action="store",
928
+ dest="window_position",
929
+ default=None,
930
+ help="""The option to set the starting window x,y position.
931
+ Format: A comma-separated string with the 2 values.
932
+ Example: "55,66"
933
+ Default: None. (Will use default values if None)""",
934
+ )
866
935
  parser.addoption(
867
936
  "--window-size",
868
937
  "--window_size",
@@ -961,6 +1030,7 @@ class SeleniumBrowser(Plugin):
961
1030
  browser = self.options.browser
962
1031
  test.test.browser = browser
963
1032
  test.test.headless = None
1033
+ test.test.headless1 = None
964
1034
  test.test.headless2 = None
965
1035
  # As a shortcut, you can use "--edge" instead of "--browser=edge", etc,
966
1036
  # but you can only specify one default browser. (Default: chrome)
@@ -1037,6 +1107,29 @@ class SeleniumBrowser(Plugin):
1037
1107
  '\n (Your browser choice was: "%s")\n' % browser
1038
1108
  )
1039
1109
  raise Exception(message)
1110
+ window_position = self.options.window_position
1111
+ if window_position:
1112
+ if window_position.count(",") != 1:
1113
+ message = (
1114
+ '\n\n window_position expects an "x,y" string!'
1115
+ '\n (Your input was: "%s")\n' % window_position
1116
+ )
1117
+ raise Exception(message)
1118
+ window_position = window_position.replace(" ", "")
1119
+ win_x = None
1120
+ win_y = None
1121
+ try:
1122
+ win_x = int(window_position.split(",")[0])
1123
+ win_y = int(window_position.split(",")[1])
1124
+ except Exception:
1125
+ message = (
1126
+ '\n\n Expecting integer values for "x,y"!'
1127
+ '\n (window_position input was: "%s")\n'
1128
+ % window_position
1129
+ )
1130
+ raise Exception(message)
1131
+ settings.WINDOW_START_X = win_x
1132
+ settings.WINDOW_START_Y = win_y
1040
1133
  window_size = self.options.window_size
1041
1134
  if window_size:
1042
1135
  if window_size.count(",") != 1:
@@ -1076,9 +1169,13 @@ class SeleniumBrowser(Plugin):
1076
1169
  test.test.cap_file = self.options.cap_file
1077
1170
  test.test.cap_string = self.options.cap_string
1078
1171
  test.test.headless = self.options.headless
1172
+ test.test.headless1 = self.options.headless1
1173
+ if test.test.headless1:
1174
+ test.test.headless = True
1079
1175
  test.test.headless2 = self.options.headless2
1080
1176
  if test.test.headless and test.test.browser == "safari":
1081
1177
  test.test.headless = False # Safari doesn't use headless
1178
+ test.test.headless1 = False
1082
1179
  if test.test.headless2 and test.test.browser == "firefox":
1083
1180
  test.test.headless2 = False # Only for Chromium browsers
1084
1181
  test.test.headless = True # Firefox has regular headless
@@ -1089,11 +1186,14 @@ class SeleniumBrowser(Plugin):
1089
1186
  self.options.headless2 = False
1090
1187
  test.test.headed = self.options.headed
1091
1188
  test.test.xvfb = self.options.xvfb
1189
+ test.test.xvfb_metrics = self.options.xvfb_metrics
1092
1190
  test.test.locale_code = self.options.locale_code
1093
1191
  test.test.interval = self.options.interval
1094
1192
  test.test.start_page = self.options.start_page
1095
1193
  if self.options.skip_js_waits:
1096
1194
  settings.SKIP_JS_WAITS = True
1195
+ if self.options.wait_for_angularjs:
1196
+ settings.WAIT_FOR_ANGULARJS = True
1097
1197
  test.test.protocol = self.options.protocol
1098
1198
  test.test.servername = self.options.servername
1099
1199
  test.test.port = self.options.port
@@ -1126,6 +1226,7 @@ class SeleniumBrowser(Plugin):
1126
1226
  test.test.block_images = self.options.block_images
1127
1227
  test.test.do_not_track = self.options.do_not_track
1128
1228
  test.test.verify_delay = self.options.verify_delay # MasterQA
1229
+ test.test.esc_end = self.options.esc_end
1129
1230
  test.test.recorder_mode = self.options.recorder_mode
1130
1231
  test.test.recorder_ext = self.options.recorder_mode # Again
1131
1232
  test.test.rec_behave = self.options.rec_behave
@@ -1140,6 +1241,7 @@ class SeleniumBrowser(Plugin):
1140
1241
  elif self.options.record_sleep:
1141
1242
  test.test.recorder_mode = True
1142
1243
  test.test.recorder_ext = True
1244
+ test.test.disable_cookies = self.options.disable_cookies
1143
1245
  test.test.disable_js = self.options.disable_js
1144
1246
  test.test.disable_csp = self.options.disable_csp
1145
1247
  test.test.disable_ws = self.options.disable_ws
@@ -1166,6 +1268,7 @@ class SeleniumBrowser(Plugin):
1166
1268
  test.test.dark_mode = self.options.dark_mode
1167
1269
  test.test.devtools = self.options.devtools
1168
1270
  test.test._disable_beforeunload = self.options._disable_beforeunload
1271
+ test.test.window_position = self.options.window_position
1169
1272
  test.test.window_size = self.options.window_size
1170
1273
  test.test.maximize_option = self.options.maximize_option
1171
1274
  if self.options.save_screenshot and self.options.no_screenshot:
@@ -1192,15 +1295,19 @@ class SeleniumBrowser(Plugin):
1192
1295
  and not self.options.headless2
1193
1296
  and not self.options.xvfb
1194
1297
  ):
1195
- print(
1196
- "(Linux uses --headless by default. "
1197
- "To override, use --headed / --gui. "
1198
- "For Xvfb mode instead, use --xvfb. "
1199
- "Or you can hide this info by using "
1200
- "--headless / --headless2.)"
1201
- )
1202
- self.options.headless = True
1203
- test.test.headless = True
1298
+ if not self.options.undetectable:
1299
+ print(
1300
+ "(Linux uses --headless by default. "
1301
+ "To override, use --headed / --gui. "
1302
+ "For Xvfb mode instead, use --xvfb. "
1303
+ "Or you can hide this info by using "
1304
+ "--headless / --headless2 / --uc.)"
1305
+ )
1306
+ self.options.headless = True
1307
+ test.test.headless = True
1308
+ else:
1309
+ self.options.xvfb = True
1310
+ test.test.xvfb = True
1204
1311
  if self.options.use_wire and self.options.undetectable:
1205
1312
  print(
1206
1313
  "\n"
@@ -1214,8 +1321,10 @@ class SeleniumBrowser(Plugin):
1214
1321
  # Recorder Mode can still optimize scripts in --headless2 mode.
1215
1322
  if self.options.recorder_mode and self.options.headless:
1216
1323
  self.options.headless = False
1324
+ self.options.headless1 = False
1217
1325
  self.options.headless2 = True
1218
1326
  test.test.headless = False
1327
+ test.test.headless1 = False
1219
1328
  test.test.headless2 = True
1220
1329
  if not self.options.headless and not self.options.headless2:
1221
1330
  self.options.headed = True
@@ -1229,7 +1338,7 @@ class SeleniumBrowser(Plugin):
1229
1338
  ):
1230
1339
  width = settings.HEADLESS_START_WIDTH
1231
1340
  height = settings.HEADLESS_START_HEIGHT
1232
- try:
1341
+ with suppress(Exception):
1233
1342
  from sbvirtualdisplay import Display
1234
1343
 
1235
1344
  self._xvfb_display = Display(visible=0, size=(width, height))
@@ -1237,8 +1346,6 @@ class SeleniumBrowser(Plugin):
1237
1346
  sb_config._virtual_display = self._xvfb_display
1238
1347
  self.headless_active = True
1239
1348
  sb_config.headless_active = True
1240
- except Exception:
1241
- pass
1242
1349
  sb_config._is_timeout_changed = False
1243
1350
  sb_config._SMALL_TIMEOUT = settings.SMALL_TIMEOUT
1244
1351
  sb_config._LARGE_TIMEOUT = settings.LARGE_TIMEOUT
@@ -1271,7 +1378,7 @@ class SeleniumBrowser(Plugin):
1271
1378
  pass
1272
1379
  except Exception:
1273
1380
  pass
1274
- try:
1381
+ with suppress(Exception):
1275
1382
  if (
1276
1383
  hasattr(self, "_xvfb_display")
1277
1384
  and self._xvfb_display
@@ -1288,5 +1395,3 @@ class SeleniumBrowser(Plugin):
1288
1395
  ):
1289
1396
  sb_config._virtual_display.stop()
1290
1397
  sb_config._virtual_display = None
1291
- except Exception:
1292
- pass
@@ -267,13 +267,6 @@ def process_test_file(code_lines, new_lang):
267
267
 
268
268
 
269
269
  def main():
270
- if (
271
- "win32" in sys.platform
272
- and hasattr(colorama, "just_fix_windows_console")
273
- ):
274
- colorama.just_fix_windows_console()
275
- else:
276
- colorama.init(autoreset=True)
277
270
  c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX
278
271
  c2 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX
279
272
  c3 = colorama.Fore.RED + colorama.Back.LIGHTGREEN_EX
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env python3
2
1
  import logging
3
2
  import os
4
3
  import re
@@ -10,6 +9,7 @@ import selenium.webdriver.chrome.service
10
9
  import selenium.webdriver.chrome.webdriver
11
10
  import selenium.webdriver.common.service
12
11
  import selenium.webdriver.remote.command
12
+ from contextlib import suppress
13
13
  from .cdp import CDP
14
14
  from .cdp import PageElement
15
15
  from .dprocess import start_detached
@@ -132,8 +132,11 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
132
132
  options = ChromeOptions()
133
133
  try:
134
134
  if hasattr(options, "_session") and options._session is not None:
135
- # Prevent reuse of options
136
- raise RuntimeError("You cannot reuse the ChromeOptions object")
135
+ # Prevent reuse of options.
136
+ # (Probably a port overlap. Quit existing driver and continue.)
137
+ logger.debug("You cannot reuse the ChromeOptions object")
138
+ with suppress(Exception):
139
+ options._session.quit()
137
140
  except AttributeError:
138
141
  pass
139
142
  options._session = self
@@ -201,11 +204,9 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
201
204
  # Create a temporary folder for the user-data profile.
202
205
  options.add_argument(arg)
203
206
  if not language:
204
- try:
207
+ with suppress(Exception):
205
208
  import locale
206
209
  language = locale.getlocale()[0].replace("_", "-")
207
- except Exception:
208
- pass
209
210
  if (
210
211
  not language
211
212
  or "English" in language
@@ -234,6 +235,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
234
235
  "--no-first-run",
235
236
  "--no-service-autorun",
236
237
  "--password-store=basic",
238
+ "--profile-directory=Default",
237
239
  ]
238
240
  )
239
241
  options.add_argument(
@@ -242,7 +244,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
242
244
  )
243
245
  if hasattr(options, 'handle_prefs'):
244
246
  options.handle_prefs(user_data_dir)
245
- try:
247
+ with suppress(Exception):
246
248
  import json
247
249
  with open(
248
250
  os.path.join(
@@ -263,8 +265,6 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
263
265
  fs.seek(0, 0)
264
266
  fs.truncate()
265
267
  json.dump(config, fs)
266
- except Exception:
267
- pass
268
268
  creationflags = 0
269
269
  if "win32" in sys.platform:
270
270
  creationflags = subprocess.CREATE_NO_WINDOW
@@ -278,19 +278,21 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
278
278
  options.binary_location, *options.arguments
279
279
  )
280
280
  else:
281
- browser = subprocess.Popen(
282
- [options.binary_location, *options.arguments],
283
- stdin=subprocess.PIPE,
284
- stdout=subprocess.PIPE,
285
- stderr=subprocess.PIPE,
286
- close_fds=IS_POSIX,
287
- creationflags=creationflags,
281
+ gui_lock = fasteners.InterProcessLock(
282
+ constants.MultiBrowser.PYAUTOGUILOCK
288
283
  )
289
- self.browser_pid = browser.pid
284
+ with gui_lock:
285
+ browser = subprocess.Popen(
286
+ [options.binary_location, *options.arguments],
287
+ stdin=subprocess.PIPE,
288
+ stdout=subprocess.PIPE,
289
+ stderr=subprocess.PIPE,
290
+ close_fds=IS_POSIX,
291
+ creationflags=creationflags,
292
+ )
293
+ self.browser_pid = browser.pid
290
294
  service_ = None
291
295
  log_output = subprocess.PIPE
292
- if sys.version_info < (3, 8):
293
- log_output = os.devnull
294
296
  if patch_driver:
295
297
  service_ = selenium.webdriver.chrome.service.Service(
296
298
  executable_path=self.patcher.executable_path,
@@ -338,7 +340,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
338
340
 
339
341
  def _get_cdc_props(self):
340
342
  cdc_props = []
341
- try:
343
+ with suppress(Exception):
342
344
  cdc_props = self.execute_script(
343
345
  """
344
346
  let objectToInspect = window,
@@ -351,8 +353,6 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
351
353
  return result.filter(i => i.match(/^[a-z]{3}_[a-z]{22}_.*/i))
352
354
  """
353
355
  )
354
- except Exception:
355
- pass
356
356
  return cdc_props
357
357
 
358
358
  def _hook_remove_cdc_props(self, cdc_props):
@@ -423,46 +423,57 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
423
423
  - Starts the chromedriver service that runs in the background.
424
424
  - Recreates the session."""
425
425
  if hasattr(self, "service"):
426
- try:
427
- self.service.stop()
428
- except Exception:
429
- pass
426
+ with suppress(Exception):
427
+ if self.service.is_connectable():
428
+ self.stop_client()
429
+ self.service.stop()
430
430
  if isinstance(timeout, str):
431
431
  if timeout.lower() == "breakpoint":
432
432
  breakpoint() # To continue:
433
433
  pass # Type "c" & press ENTER!
434
434
  else:
435
435
  time.sleep(timeout)
436
- try:
436
+ with suppress(Exception):
437
437
  self.service.start()
438
- except Exception:
439
- pass
440
- try:
438
+ with suppress(Exception):
441
439
  self.start_session()
442
- except Exception:
443
- pass
440
+ with suppress(Exception):
441
+ if self.current_url.startswith("chrome-extension://"):
442
+ self.close()
443
+ if self.service.is_connectable():
444
+ self.stop_client()
445
+ self.service.stop()
446
+ self.service.start()
447
+ self.start_session()
448
+ self._is_connected = True
444
449
 
445
450
  def disconnect(self):
446
451
  """Stops the chromedriver service that runs in the background.
447
452
  To use driver methods again, you MUST call driver.connect()"""
448
453
  if hasattr(self, "service"):
449
- try:
450
- self.service.stop()
451
- except Exception:
452
- pass
454
+ with suppress(Exception):
455
+ if self.service.is_connectable():
456
+ self.stop_client()
457
+ self.service.stop()
458
+ self._is_connected = False
453
459
 
454
460
  def connect(self):
455
461
  """Starts the chromedriver service that runs in the background
456
462
  and recreates the session."""
457
463
  if hasattr(self, "service"):
458
- try:
464
+ with suppress(Exception):
459
465
  self.service.start()
460
- except Exception:
461
- pass
462
- try:
466
+ with suppress(Exception):
463
467
  self.start_session()
464
- except Exception:
465
- pass
468
+ with suppress(Exception):
469
+ if self.current_url.startswith("chrome-extension://"):
470
+ self.close()
471
+ if self.service.is_connectable():
472
+ self.stop_client()
473
+ self.service.stop()
474
+ self.service.start()
475
+ self.start_session()
476
+ self._is_connected = True
466
477
 
467
478
  def start_session(self, capabilities=None):
468
479
  if not capabilities:
@@ -486,13 +497,13 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
486
497
  pass
487
498
  if hasattr(self, "service") and getattr(self.service, "process", None):
488
499
  logger.debug("Stopping webdriver service")
489
- self.service.stop()
490
- try:
500
+ with suppress(Exception):
501
+ self.stop_client()
502
+ self.service.stop()
503
+ with suppress(Exception):
491
504
  if self.reactor and isinstance(self.reactor, Reactor):
492
505
  logger.debug("Shutting down Reactor")
493
506
  self.reactor.event.set()
494
- except Exception:
495
- pass
496
507
  if (
497
508
  hasattr(self, "keep_user_data_dir")
498
509
  and hasattr(self, "user_data_dir")
@@ -521,18 +532,14 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
521
532
  self.patcher = None
522
533
 
523
534
  def __del__(self):
524
- try:
535
+ with suppress(Exception):
525
536
  if "win32" in sys.platform:
526
537
  self.stop_client()
527
538
  self.command_executor.close()
528
539
  else:
529
540
  super().quit()
530
- except Exception:
531
- pass
532
- try:
541
+ with suppress(Exception):
533
542
  self.quit()
534
- except Exception:
535
- pass
536
543
 
537
544
  def __enter__(self):
538
545
  return self
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env python3
2
1
  import fasteners
3
2
  import json
4
3
  import logging
@@ -0,0 +1 @@
1
+ from seleniumbase.undetected.cdp_driver import cdp_util # noqa
@@ -0,0 +1,110 @@
1
+ import warnings as _warnings
2
+ from collections.abc import Mapping as _Mapping, Sequence as _Sequence
3
+ import logging
4
+
5
+ __logger__ = logging.getLogger(__name__)
6
+ __all__ = ["cdict", "ContraDict"]
7
+
8
+
9
+ def cdict(*args, **kwargs):
10
+ """Factory function"""
11
+ return ContraDict(*args, **kwargs)
12
+
13
+
14
+ class ContraDict(dict):
15
+ """
16
+ Directly inherited from dict.
17
+ Accessible by attribute. o.x == o['x']
18
+ This works also for all corner cases.
19
+ Native json.dumps and json.loads work with it.
20
+
21
+ Names like "keys", "update", "values" etc won't overwrite the methods,
22
+ but will just be available using dict lookup notation obj['items']
23
+ instead of obj.items.
24
+
25
+ All key names are converted to snake_case.
26
+ Hyphen's (-), dot's (.) or whitespaces are replaced by underscore (_).
27
+ Autocomplete works even if the objects comes from a list.
28
+ Recursive action. Dict assignments will be converted too.
29
+ """
30
+ __module__ = None
31
+
32
+ def __init__(self, *args, **kwargs):
33
+ super().__init__()
34
+ # silent = kwargs.pop("silent", False)
35
+ _ = dict(*args, **kwargs)
36
+
37
+ super().__setattr__("__dict__", self)
38
+ for k, v in _.items():
39
+ _check_key(k, self, False, True)
40
+ super().__setitem__(k, _wrap(self.__class__, v))
41
+
42
+ def __setitem__(self, key, value):
43
+ super().__setitem__(key, _wrap(self.__class__, value))
44
+
45
+ def __setattr__(self, key, value):
46
+ super().__setitem__(key, _wrap(self.__class__, value))
47
+
48
+ def __getattribute__(self, attribute):
49
+ if attribute in self:
50
+ return self[attribute]
51
+ if not _check_key(attribute, self, True, silent=True):
52
+ return getattr(super(), attribute)
53
+ return object.__getattribute__(self, attribute)
54
+
55
+
56
+ def _wrap(cls, v):
57
+ if isinstance(v, _Mapping):
58
+ v = cls(v)
59
+ elif isinstance(v, _Sequence) and not isinstance(
60
+ v, (str, bytes, bytearray, set, tuple)
61
+ ):
62
+ v = list([_wrap(cls, x) for x in v])
63
+ return v
64
+
65
+
66
+ _warning_names = (
67
+ "items",
68
+ "keys",
69
+ "values",
70
+ "update",
71
+ "clear",
72
+ "copy",
73
+ "fromkeys",
74
+ "get",
75
+ "items",
76
+ "keys",
77
+ "pop",
78
+ "popitem",
79
+ "setdefault",
80
+ "update",
81
+ "values",
82
+ "class",
83
+ )
84
+
85
+ _warning_names_message = """\n\
86
+ While creating a ContraDict object, a key offending key name '{0}'
87
+ has been found, which might behave unexpected.
88
+ You will only be able to look it up using key,
89
+ eg. myobject['{0}']. myobject.{0} will not work with that name."""
90
+
91
+
92
+ def _check_key(
93
+ key: str, mapping: _Mapping, boolean: bool = False, silent=True
94
+ ):
95
+ """Checks `key` and warns if needed.
96
+ :param key:
97
+ :param boolean: return True or False instead of passthrough
98
+ """
99
+ e = None
100
+ if not isinstance(key, (str,)):
101
+ if boolean:
102
+ return True
103
+ return key
104
+ if key.lower() in _warning_names or any(_ in key for _ in ("-", ".")):
105
+ if not silent:
106
+ _warnings.warn(_warning_names_message.format(key))
107
+ e = True
108
+ if not boolean:
109
+ return key
110
+ return not e