seleniumbase 4.24.10__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.
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