seleniumbase 4.30.8__py3-none-any.whl → 4.31.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. sbase/__init__.py +1 -0
  2. seleniumbase/__init__.py +2 -3
  3. seleniumbase/__version__.py +1 -1
  4. seleniumbase/behave/behave_sb.py +59 -11
  5. seleniumbase/config/settings.py +4 -0
  6. seleniumbase/console_scripts/logo_helper.py +47 -0
  7. seleniumbase/console_scripts/run.py +7 -4
  8. seleniumbase/console_scripts/sb_behave_gui.py +5 -5
  9. seleniumbase/console_scripts/sb_caseplans.py +6 -6
  10. seleniumbase/console_scripts/sb_commander.py +5 -5
  11. seleniumbase/console_scripts/sb_install.py +10 -2
  12. seleniumbase/console_scripts/sb_recorder.py +4 -4
  13. seleniumbase/core/browser_launcher.py +179 -108
  14. seleniumbase/core/mysql.py +1 -4
  15. seleniumbase/core/recorder_helper.py +24 -5
  16. seleniumbase/core/sb_driver.py +13 -3
  17. seleniumbase/core/settings_parser.py +4 -0
  18. seleniumbase/fixtures/base_case.py +324 -493
  19. seleniumbase/fixtures/js_utils.py +19 -52
  20. seleniumbase/fixtures/page_actions.py +3 -6
  21. seleniumbase/fixtures/page_utils.py +18 -53
  22. seleniumbase/plugins/base_plugin.py +2 -3
  23. seleniumbase/plugins/driver_manager.py +182 -5
  24. seleniumbase/plugins/pytest_plugin.py +51 -23
  25. seleniumbase/plugins/sb_manager.py +185 -5
  26. seleniumbase/plugins/selenium_plugin.py +71 -8
  27. seleniumbase/undetected/__init__.py +13 -38
  28. seleniumbase/undetected/dprocess.py +4 -6
  29. seleniumbase/undetected/options.py +3 -6
  30. seleniumbase/undetected/patcher.py +2 -3
  31. {seleniumbase-4.30.8.dist-info → seleniumbase-4.31.1.dist-info}/METADATA +111 -125
  32. {seleniumbase-4.30.8.dist-info → seleniumbase-4.31.1.dist-info}/RECORD +36 -47
  33. {seleniumbase-4.30.8.dist-info → seleniumbase-4.31.1.dist-info}/WHEEL +1 -1
  34. sbase/ReadMe.txt +0 -2
  35. seleniumbase/ReadMe.md +0 -25
  36. seleniumbase/common/ReadMe.md +0 -71
  37. seleniumbase/console_scripts/ReadMe.md +0 -734
  38. seleniumbase/drivers/ReadMe.md +0 -27
  39. seleniumbase/extensions/ReadMe.md +0 -12
  40. seleniumbase/masterqa/ReadMe.md +0 -61
  41. seleniumbase/resources/ReadMe.md +0 -31
  42. seleniumbase/resources/favicon.ico +0 -0
  43. seleniumbase/utilities/selenium_grid/ReadMe.md +0 -84
  44. seleniumbase/utilities/selenium_ide/ReadMe.md +0 -111
  45. {seleniumbase-4.30.8.dist-info → seleniumbase-4.31.1.dist-info}/LICENSE +0 -0
  46. {seleniumbase-4.30.8.dist-info → seleniumbase-4.31.1.dist-info}/entry_points.txt +0 -0
  47. {seleniumbase-4.30.8.dist-info → seleniumbase-4.31.1.dist-info}/top_level.txt +0 -0
sbase/__init__.py CHANGED
@@ -11,3 +11,4 @@ from seleniumbase import MasterQA # noqa
11
11
  from seleniumbase import page_actions # noqa
12
12
  from seleniumbase import page_utils # noqa
13
13
  from seleniumbase import SB # noqa
14
+ from seleniumbase import translate # noqa
seleniumbase/__init__.py CHANGED
@@ -10,7 +10,7 @@ from selenium import webdriver
10
10
  from seleniumbase.__version__ import __version__
11
11
  from seleniumbase.common import decorators # noqa
12
12
  from seleniumbase.common import encryption # noqa
13
- from seleniumbase.core import colored_traceback
13
+ from seleniumbase.core import colored_traceback # noqa
14
14
  from seleniumbase.core.browser_launcher import get_driver # noqa
15
15
  from seleniumbase.fixtures import js_utils # noqa
16
16
  from seleniumbase.fixtures import page_actions # noqa
@@ -36,8 +36,7 @@ if sys.version_info[0] < 3 and "pdbp" in locals():
36
36
  pdb.DefaultConfig.sticky_by_default = True
37
37
  colored_traceback.add_hook()
38
38
  os.environ["SE_AVOID_STATS"] = "true" # Disable Selenium Manager stats
39
- if sys.version_info >= (3, 7):
40
- webdriver.TouchActions = None # Lifeline for past selenium-wire versions
39
+ webdriver.TouchActions = None # Lifeline for past selenium-wire versions
41
40
  if sys.version_info >= (3, 10):
42
41
  collections.Callable = collections.abc.Callable # Lifeline for nosetests
43
42
  del collections # Undo "import collections" / Simplify "dir(seleniumbase)"
@@ -1,2 +1,2 @@
1
1
  # seleniumbase package
2
- __version__ = "4.30.8"
2
+ __version__ = "4.31.1"
@@ -44,10 +44,12 @@ behave -D agent="User Agent String" -D demo
44
44
  -D sjw (Skip JS Waits for readyState to be "complete" or Angular to load.)
45
45
  -D wfa (Wait for AngularJS to be done loading after specific web actions.)
46
46
  -D pls=PLS (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".)
47
- -D headless (Run tests in headless mode. The default arg on Linux OS.)
48
- -D headless2 (Use the new headless mode, which supports extensions.)
47
+ -D headless (The default headless mode. Linux uses this mode by default.)
48
+ -D headless1 (Use Chrome's old headless mode. Fast, but has limitations.)
49
+ -D headless2 (Use Chrome's new headless mode, which supports extensions.)
49
50
  -D headed (Run tests in headed/GUI mode on Linux OS, where not default.)
50
51
  -D xvfb (Run tests using the Xvfb virtual display server on Linux OS.)
52
+ -D xvfb-metrics=STRING (Set Xvfb display size on Linux: "Width,Height".)
51
53
  -D locale=LOCALE_CODE (Set the Language Locale Code for the web browser.)
52
54
  -D pdb (Activate Post Mortem Debug Mode if a test fails.)
53
55
  -D interval=SECONDS (The autoplay interval for presentations & tour steps)
@@ -90,6 +92,7 @@ behave -D agent="User Agent String" -D demo
90
92
  -D rcs | -D reuse-class-session (Reuse session for tests in class/feature)
91
93
  -D crumbs (Delete all cookies between tests reusing a session.)
92
94
  -D disable-beforeunload (Disable the "beforeunload" event on Chrome.)
95
+ -D window-position=X,Y (Set the browser's starting window position.)
93
96
  -D window-size=WIDTH,HEIGHT (Set the browser's starting window size.)
94
97
  -D maximize (Start tests with the browser window maximized.)
95
98
  -D screenshot (Save a screenshot at the end of each test.)
@@ -104,6 +107,7 @@ import colorama
104
107
  import os
105
108
  import re
106
109
  import sys
110
+ from contextlib import suppress
107
111
  from seleniumbase import config as sb_config
108
112
  from seleniumbase.config import settings
109
113
  from seleniumbase.core import download_helper
@@ -141,10 +145,12 @@ def get_configured_sb(context):
141
145
  sb.browser = "chrome"
142
146
  sb.is_behave = True
143
147
  sb.headless = False
148
+ sb.headless1 = False
144
149
  sb.headless2 = False
145
150
  sb.headless_active = False
146
151
  sb.headed = False
147
152
  sb.xvfb = False
153
+ sb.xvfb_metrics = None
148
154
  sb.start_page = None
149
155
  sb.locale_code = None
150
156
  sb.pdb_option = False
@@ -193,6 +199,7 @@ def get_configured_sb(context):
193
199
  sb._disable_beforeunload = False
194
200
  sb.visual_baseline = False
195
201
  sb.use_wire = False
202
+ sb.window_position = None
196
203
  sb.window_size = None
197
204
  sb.maximize_option = False
198
205
  sb.is_context_manager = False
@@ -291,6 +298,11 @@ def get_configured_sb(context):
291
298
  sb.headless = True
292
299
  continue
293
300
  # Handle: -D headless2
301
+ if low_key == "headless1":
302
+ sb.headless1 = True
303
+ sb.headless = True
304
+ continue
305
+ # Handle: -D headless2
294
306
  if low_key == "headless2":
295
307
  sb.headless2 = True
296
308
  continue
@@ -302,6 +314,13 @@ def get_configured_sb(context):
302
314
  if low_key == "xvfb":
303
315
  sb.xvfb = True
304
316
  continue
317
+ # Handle: -D xvfb-metrics=STR / xvfb_metrics=STR
318
+ if low_key in ["xvfb-metrics", "xvfb_metrics"]:
319
+ xvfb_metrics = userdata[key]
320
+ if xvfb_metrics == "true":
321
+ xvfb_metrics = sb.xvfb_metrics # revert to default
322
+ sb.xvfb_metrics = xvfb_metrics
323
+ continue
305
324
  # Handle: -D start-page=URL / start_page=URL / url=URL
306
325
  if low_key in ["start-page", "start_page", "url"]:
307
326
  start_page = userdata[key]
@@ -601,6 +620,13 @@ def get_configured_sb(context):
601
620
  if low_key == "wire":
602
621
  sb.use_wire = True
603
622
  continue
623
+ # Handle: -D window-position=X,Y / window_position=X,Y
624
+ if low_key in ["window-position", "window_position"]:
625
+ window_position = userdata[key]
626
+ if window_position == "true":
627
+ window_position = sb.window_position # revert to default
628
+ sb.window_position = window_position
629
+ continue
604
630
  # Handle: -D window-size=Width,Height / window_size=Width,Height
605
631
  if low_key in ["window-size", "window_size"]:
606
632
  window_size = userdata[key]
@@ -845,6 +871,7 @@ def get_configured_sb(context):
845
871
  # Recorder Mode can still optimize scripts in "-D headless2" mode.
846
872
  if sb.recorder_ext and sb.headless:
847
873
  sb.headless = False
874
+ sb.headless1 = False
848
875
  sb.headless2 = True
849
876
  if sb.headless2 and sb.browser == "firefox":
850
877
  sb.headless2 = False # Only for Chromium browsers
@@ -881,11 +908,13 @@ def get_configured_sb(context):
881
908
  # Recorder Mode can still optimize scripts in --headless2 mode.
882
909
  if sb.recorder_mode and sb.headless:
883
910
  sb.headless = False
911
+ sb.headless1 = False
884
912
  sb.headless2 = True
885
913
  if not sb.headless and not sb.headless2:
886
914
  sb.headed = True
887
915
  if sb.browser == "safari" and sb.headless:
888
916
  sb.headless = False # Safari doesn't support headless mode
917
+ sb.headless1 = False
889
918
  if sb.save_screenshot_after_test and sb.no_screenshot_after_test:
890
919
  sb.save_screenshot_after_test = False # "no_screenshot" has priority
891
920
  if sb.servername != "localhost":
@@ -904,6 +933,29 @@ def get_configured_sb(context):
904
933
  else:
905
934
  sb.enable_ws = False
906
935
  sb.disable_ws = True
936
+ if sb.window_position:
937
+ window_position = sb.window_position
938
+ if window_position.count(",") != 1:
939
+ message = (
940
+ '\n\n window_position expects an "x,y" string!'
941
+ '\n (Your input was: "%s")\n' % window_position
942
+ )
943
+ raise Exception(message)
944
+ window_position = window_position.replace(" ", "")
945
+ win_x = None
946
+ win_y = None
947
+ try:
948
+ win_x = int(window_position.split(",")[0])
949
+ win_y = int(window_position.split(",")[1])
950
+ except Exception:
951
+ message = (
952
+ '\n\n Expecting integer values for "x,y"!'
953
+ '\n (window_position input was: "%s")\n'
954
+ % window_position
955
+ )
956
+ raise Exception(message)
957
+ settings.WINDOW_START_X = win_x
958
+ settings.WINDOW_START_Y = win_y
907
959
  if sb.window_size:
908
960
  window_size = sb.window_size
909
961
  if window_size.count(",") != 1:
@@ -938,9 +990,11 @@ def get_configured_sb(context):
938
990
  sb_config.is_pytest = False
939
991
  sb_config.is_nosetest = False
940
992
  sb_config.is_context_manager = False
993
+ sb_config.window_position = sb.window_position
941
994
  sb_config.window_size = sb.window_size
942
995
  sb_config.maximize_option = sb.maximize_option
943
996
  sb_config.xvfb = sb.xvfb
997
+ sb_config.xvfb_metrics = sb.xvfb_metrics
944
998
  sb_config.reuse_class_session = sb._reuse_class_session
945
999
  sb_config.save_screenshot = sb.save_screenshot_after_test
946
1000
  sb_config.no_screenshot = sb.no_screenshot_after_test
@@ -1162,12 +1216,10 @@ def behave_dashboard_prepare():
1162
1216
  sb_config.item_count_untested = sb_config.item_count
1163
1217
  dash_path = os.path.join(os.getcwd(), "dashboard.html")
1164
1218
  star_len = len("Dashboard: ") + len(dash_path)
1165
- try:
1219
+ with suppress(Exception):
1166
1220
  terminal_size = os.get_terminal_size().columns
1167
1221
  if terminal_size > 30 and star_len > terminal_size:
1168
1222
  star_len = terminal_size
1169
- except Exception:
1170
- pass
1171
1223
  stars = "*" * star_len
1172
1224
  c1 = ""
1173
1225
  cr = ""
@@ -1263,7 +1315,7 @@ def _perform_behave_unconfigure_():
1263
1315
 
1264
1316
 
1265
1317
  def do_final_driver_cleanup_as_needed():
1266
- try:
1318
+ with suppress(Exception):
1267
1319
  if hasattr(sb_config, "last_driver") and sb_config.last_driver:
1268
1320
  if (
1269
1321
  not is_windows
@@ -1271,8 +1323,6 @@ def do_final_driver_cleanup_as_needed():
1271
1323
  or sb_config.last_driver.service.process
1272
1324
  ):
1273
1325
  sb_config.last_driver.quit()
1274
- except Exception:
1275
- pass
1276
1326
 
1277
1327
 
1278
1328
  def _perform_behave_terminal_summary_():
@@ -1281,12 +1331,10 @@ def _perform_behave_terminal_summary_():
1281
1331
  )
1282
1332
  dash_path = os.path.join(os.getcwd(), "dashboard.html")
1283
1333
  equals_len = len("Dashboard: ") + len(dash_path)
1284
- try:
1334
+ with suppress(Exception):
1285
1335
  terminal_size = os.get_terminal_size().columns
1286
1336
  if terminal_size > 30 and equals_len > terminal_size:
1287
1337
  equals_len = terminal_size
1288
- except Exception:
1289
- pass
1290
1338
  equals = "=" * (equals_len + 2)
1291
1339
  c2 = ""
1292
1340
  cr = ""
@@ -110,6 +110,10 @@ DISABLE_CSP_ON_CHROME = False
110
110
  # (This applies when using --proxy=[PROXY_STRING] for using a proxy server.)
111
111
  RAISE_INVALID_PROXY_STRING_EXCEPTION = True
112
112
 
113
+ # Default browser coordinates when opening new windows for tests.
114
+ WINDOW_START_X = 20
115
+ WINDOW_START_Y = 54
116
+
113
117
  # Default browser resolutions when opening new windows for tests.
114
118
  # (Headless resolutions take priority, and include all browsers.)
115
119
  # (Firefox starts maximized by default when running in GUI Mode.)
@@ -3,7 +3,9 @@
3
3
  http://www.patorjk.com/software/taag/#p=display&f=Slant&t=SeleniumBase """
4
4
 
5
5
  import colorama
6
+ import os
6
7
  import sys
8
+ from contextlib import suppress
7
9
 
8
10
  r"""
9
11
  ______ __ _ ____
@@ -66,4 +68,49 @@ def get_seleniumbase_logo():
66
68
  sb += " "
67
69
  sb += cr
68
70
  sb += cr
71
+ with suppress(Exception):
72
+ terminal_width = os.get_terminal_size().columns
73
+ if isinstance(terminal_width, int) and terminal_width >= 66:
74
+ return sb
75
+
76
+ # If the logo is wider than the screen width, use a smaller one:
77
+ r"""
78
+ ___ _ _ ___
79
+ / __| ___| |___ _ _ (_)_ _ _ __ | _ ) __ _ ______
80
+ \__ \/ -_) / -_) ' \| | \| | ' \ | _ \/ _` (_-< -_)
81
+ |___/\___|_\___|_||_|_|\_,_|_|_|_\|___/\__,_/__|___|
82
+ """
83
+ sb = " "
84
+ sb += cr
85
+ sb += "\n"
86
+ sb += c1
87
+ sb += " ___ _ _ "
88
+ sb += c2
89
+ sb += " ___ "
90
+ sb += cr
91
+ sb += "\n"
92
+ sb += c1
93
+ sb += "/ __| ___| |___ _ _ (_)_ _ _ __ "
94
+ sb += c2
95
+ sb += "| _ ) __ _ ______ "
96
+ sb += cr
97
+ sb += "\n"
98
+ sb += c1
99
+ sb += "\\__ \\/ -_) / -_) ' \\| | \\| | ' \\ "
100
+ sb += c2
101
+ sb += "| _ \\/ _` (_-< -_)"
102
+ sb += cr
103
+ sb += "\n"
104
+ sb += c1
105
+ sb += "|___/\\___|_\\___|_||_|_|\\_,_|_|_|_\\"
106
+ sb += c2
107
+ sb += "|___/\\__,_/__|___|"
108
+ sb += cr
109
+ sb += "\n"
110
+ sb += c3
111
+ sb += " "
112
+ sb += c4
113
+ sb += " "
114
+ sb += cr
115
+ sb += cr
69
116
  return sb
@@ -54,8 +54,8 @@ else:
54
54
  def show_usage():
55
55
  show_basic_usage()
56
56
  sc = ""
57
- sc += ' Type "sbase help [COMMAND]" for specific command info.\n'
58
- sc += ' For info on all commands, type: "seleniumbase --help".\n'
57
+ sc += ' Type "sbase help [COMMAND]" for specific info.\n'
58
+ sc += ' For all commands, type: "seleniumbase --help".\n'
59
59
  sc += ' Use "pytest" for running tests.\n'
60
60
  if "linux" not in sys.platform:
61
61
  c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX
@@ -76,12 +76,15 @@ def show_basic_usage():
76
76
 
77
77
  seleniumbase_logo = logo_helper.get_seleniumbase_logo()
78
78
  print(seleniumbase_logo)
79
+ time.sleep(0.044)
79
80
  print("")
80
- time.sleep(0.28) # Enough time to see the logo
81
+ time.sleep(0.033)
81
82
  show_package_location()
83
+ time.sleep(0.032)
82
84
  show_version_info()
85
+ time.sleep(0.031)
83
86
  print("")
84
- time.sleep(0.62) # Enough time to see the version
87
+ time.sleep(0.68) # Enough time to see the logo & version
85
88
  sc = ""
86
89
  sc += ' * USAGE: "seleniumbase [COMMAND] [PARAMETERS]"\n'
87
90
  sc += ' * OR: "sbase [COMMAND] [PARAMETERS]"\n'
@@ -16,16 +16,16 @@ Output:
16
16
  import colorama
17
17
  import subprocess
18
18
  import sys
19
+ import tkinter as tk
20
+ from seleniumbase.fixtures import shared_utils
21
+ from tkinter.scrolledtext import ScrolledText
19
22
 
20
- if sys.version_info <= (3, 7):
23
+ if sys.version_info <= (3, 8):
21
24
  current_version = ".".join(str(ver) for ver in sys.version_info[:3])
22
25
  raise Exception(
23
- "\n* SBase Commander requires Python 3.7 or newer!"
26
+ "\n* SBase Commander requires Python 3.8 or newer!"
24
27
  "\n** You are currently using Python %s" % current_version
25
28
  )
26
- from seleniumbase.fixtures import shared_utils
27
- import tkinter as tk # noqa: E402
28
- from tkinter.scrolledtext import ScrolledText # noqa: E402
29
29
 
30
30
 
31
31
  def set_colors(use_colors):
@@ -20,17 +20,17 @@ import colorama
20
20
  import os
21
21
  import subprocess
22
22
  import sys
23
+ import tkinter as tk
24
+ from seleniumbase.fixtures import shared_utils
25
+ from tkinter import messagebox
26
+ from tkinter.scrolledtext import ScrolledText
23
27
 
24
- if sys.version_info <= (3, 7):
28
+ if sys.version_info <= (3, 8):
25
29
  current_version = ".".join(str(ver) for ver in sys.version_info[:3])
26
30
  raise Exception(
27
- "\n* SBase Case Plans Generator requires Python 3.7 or newer!"
31
+ "\n* SBase Case Plans Generator requires Python 3.8 or newer!"
28
32
  "\n** You are currently using Python %s" % current_version
29
33
  )
30
- from seleniumbase.fixtures import shared_utils
31
- import tkinter as tk # noqa: E402
32
- from tkinter import messagebox # noqa: E402
33
- from tkinter.scrolledtext import ScrolledText # noqa: E402
34
34
 
35
35
 
36
36
  def set_colors(use_colors):
@@ -21,16 +21,16 @@ import colorama
21
21
  import os
22
22
  import subprocess
23
23
  import sys
24
+ import tkinter as tk
25
+ from seleniumbase.fixtures import shared_utils
26
+ from tkinter.scrolledtext import ScrolledText
24
27
 
25
- if sys.version_info <= (3, 7):
28
+ if sys.version_info <= (3, 8):
26
29
  current_version = ".".join(str(ver) for ver in sys.version_info[:3])
27
30
  raise Exception(
28
- "\n* SBase Commander requires Python 3.7 or newer!"
31
+ "\n* SBase Commander requires Python 3.8 or newer!"
29
32
  "\n** You are currently using Python %s" % current_version
30
33
  )
31
- from seleniumbase.fixtures import shared_utils
32
- import tkinter as tk # noqa: E402
33
- from tkinter.scrolledtext import ScrolledText # noqa: E402
34
34
 
35
35
 
36
36
  def set_colors(use_colors):
@@ -578,10 +578,18 @@ def main(override=None, intel_for_uc=None, force_uc=None):
578
578
  else:
579
579
  invalid_run_command()
580
580
  if IS_MAC:
581
- file_name = "geckodriver-%s-macos.tar.gz" % use_version
581
+ if IS_ARM_MAC:
582
+ file_name = "geckodriver-%s-macos-aarch64.tar.gz" % use_version
583
+ else:
584
+ file_name = "geckodriver-%s-macos.tar.gz" % use_version
582
585
  elif IS_LINUX:
583
586
  if "64" in ARCH:
584
- file_name = "geckodriver-%s-linux64.tar.gz" % use_version
587
+ if "aarch64" in platform.processor():
588
+ file_name = (
589
+ "geckodriver-%s-linux-aarch64.tar.gz" % use_version
590
+ )
591
+ else:
592
+ file_name = "geckodriver-%s-linux64.tar.gz" % use_version
585
593
  else:
586
594
  file_name = "geckodriver-%s-linux32.tar.gz" % use_version
587
595
  elif IS_WINDOWS:
@@ -18,23 +18,23 @@ import colorama
18
18
  import os
19
19
  import subprocess
20
20
  import sys
21
+ import tkinter as tk
21
22
  from seleniumbase import config as sb_config
22
23
  from seleniumbase.fixtures import page_utils
23
24
  from seleniumbase.fixtures import shared_utils
25
+ from tkinter import messagebox
24
26
 
25
27
  sb_config.rec_subprocess_p = None
26
28
  sb_config.rec_subprocess_used = False
27
29
  sys_executable = sys.executable
28
30
  if " " in sys_executable:
29
31
  sys_executable = "python"
30
- if sys.version_info <= (3, 7):
32
+ if sys.version_info <= (3, 8):
31
33
  current_version = ".".join(str(ver) for ver in sys.version_info[:3])
32
34
  raise Exception(
33
- "\n* Recorder Desktop requires Python 3.7 or newer!"
35
+ "\n* Recorder Desktop requires Python 3.8 or newer!"
34
36
  "\n*** You are currently using Python %s" % current_version
35
37
  )
36
- import tkinter as tk # noqa: E402
37
- from tkinter import messagebox # noqa: E402
38
38
 
39
39
 
40
40
  def set_colors(use_colors):