seleniumbase 4.54.6__tar.gz → 4.54.8__tar.gz

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 (164) hide show
  1. {seleniumbase-4.54.6/seleniumbase.egg-info → seleniumbase-4.54.8}/PKG-INFO +8 -8
  2. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/mcp_servers/server.py +36 -29
  3. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/requirements.txt +4 -4
  4. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/__version__.py +1 -1
  5. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/behave/behave_sb.py +13 -8
  6. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/run.py +2 -2
  7. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/sb_mkchart.py +9 -8
  8. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/sb_mkdir.py +40 -38
  9. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/sb_mkfile.py +9 -8
  10. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/sb_mkpres.py +9 -8
  11. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/sb_mkrec.py +35 -42
  12. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/fixtures/base_case.py +4 -7
  13. {seleniumbase-4.54.6 → seleniumbase-4.54.8/seleniumbase.egg-info}/PKG-INFO +8 -8
  14. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase.egg-info/requires.txt +7 -7
  15. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/setup.py +7 -7
  16. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/.gitignore +0 -0
  17. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/LICENSE +0 -0
  18. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/MANIFEST.in +0 -0
  19. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/README.md +0 -0
  20. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/install.sh +0 -0
  21. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/mcp_servers/__init__.py +0 -0
  22. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/pyproject.toml +0 -0
  23. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/pytest.ini +0 -0
  24. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/sbase/__init__.py +0 -0
  25. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/sbase/__main__.py +0 -0
  26. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/sbase/steps.py +0 -0
  27. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/__init__.py +0 -0
  28. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/__main__.py +0 -0
  29. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/behave/__init__.py +0 -0
  30. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/behave/behave_helper.py +0 -0
  31. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/behave/steps.py +0 -0
  32. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/common/__init__.py +0 -0
  33. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/common/decorators.py +0 -0
  34. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/common/encryption.py +0 -0
  35. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/common/exceptions.py +0 -0
  36. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/common/obfuscate.py +0 -0
  37. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/common/unobfuscate.py +0 -0
  38. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/config/__init__.py +0 -0
  39. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/config/ad_block_list.py +0 -0
  40. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/config/proxy_list.py +0 -0
  41. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/config/settings.py +0 -0
  42. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/__init__.py +0 -0
  43. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/logo_helper.py +0 -0
  44. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/rich_helper.py +0 -0
  45. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/sb_behave_gui.py +0 -0
  46. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/sb_caseplans.py +0 -0
  47. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/sb_commander.py +0 -0
  48. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/sb_install.py +0 -0
  49. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/sb_objectify.py +0 -0
  50. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/sb_print.py +0 -0
  51. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/console_scripts/sb_recorder.py +0 -0
  52. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/__init__.py +0 -0
  53. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/application_manager.py +0 -0
  54. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/browser_launcher.py +0 -0
  55. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/capabilities_parser.py +0 -0
  56. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/colored_traceback.py +0 -0
  57. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/create_db_tables.sql +0 -0
  58. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/detect_b_ver.py +0 -0
  59. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/download_helper.py +0 -0
  60. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/encoded_images.py +0 -0
  61. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/jqc_helper.py +0 -0
  62. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/log_helper.py +0 -0
  63. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/mysql.py +0 -0
  64. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/nest_asyncio.py +0 -0
  65. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/proxy_helper.py +0 -0
  66. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/recorder_helper.py +0 -0
  67. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/report_helper.py +0 -0
  68. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/s3_manager.py +0 -0
  69. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/sb_cdp.py +0 -0
  70. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/sb_driver.py +0 -0
  71. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/session_helper.py +0 -0
  72. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/settings_parser.py +0 -0
  73. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/style_sheet.py +0 -0
  74. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/testcase_manager.py +0 -0
  75. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/tour_helper.py +0 -0
  76. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/core/visual_helper.py +0 -0
  77. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/drivers/__init__.py +0 -0
  78. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/drivers/brave_drivers/__init__.py +0 -0
  79. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/drivers/cft_drivers/__init__.py +0 -0
  80. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
  81. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/drivers/chs_drivers/__init__.py +0 -0
  82. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/drivers/comet_drivers/__init__.py +0 -0
  83. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/drivers/opera_drivers/__init__.py +0 -0
  84. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/extensions/__init__.py +0 -0
  85. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/extensions/ad_block.zip +0 -0
  86. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/extensions/disable_csp.zip +0 -0
  87. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/extensions/recorder.zip +0 -0
  88. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/extensions/sbase_ext.zip +0 -0
  89. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/fixtures/__init__.py +0 -0
  90. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/fixtures/constants.py +0 -0
  91. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/fixtures/css_to_xpath.py +0 -0
  92. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/fixtures/errors.py +0 -0
  93. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/fixtures/js_utils.py +0 -0
  94. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/fixtures/page_actions.py +0 -0
  95. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/fixtures/page_utils.py +0 -0
  96. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/fixtures/shared_utils.py +0 -0
  97. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/fixtures/unittest_helper.py +0 -0
  98. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/fixtures/words.py +0 -0
  99. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/fixtures/xpath_to_css.py +0 -0
  100. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/js_code/__init__.py +0 -0
  101. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/js_code/active_css_js.py +0 -0
  102. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/js_code/live_js.py +0 -0
  103. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/js_code/recorder_js.py +0 -0
  104. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/masterqa/__init__.py +0 -0
  105. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/masterqa/master_qa.py +0 -0
  106. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/plugins/__init__.py +0 -0
  107. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/plugins/base_plugin.py +0 -0
  108. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/plugins/basic_test_info.py +0 -0
  109. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/plugins/db_reporting_plugin.py +0 -0
  110. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/plugins/driver_manager.py +0 -0
  111. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/plugins/page_source.py +0 -0
  112. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/plugins/pytest_plugin.py +0 -0
  113. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/plugins/s3_logging_plugin.py +0 -0
  114. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/plugins/sb_manager.py +0 -0
  115. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/plugins/screen_shots.py +0 -0
  116. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/plugins/selenium_plugin.py +0 -0
  117. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/resources/__init__.py +0 -0
  118. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/translate/__init__.py +0 -0
  119. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/translate/chinese.py +0 -0
  120. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/translate/dutch.py +0 -0
  121. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/translate/french.py +0 -0
  122. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/translate/italian.py +0 -0
  123. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/translate/japanese.py +0 -0
  124. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/translate/korean.py +0 -0
  125. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/translate/master_dict.py +0 -0
  126. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/translate/portuguese.py +0 -0
  127. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/translate/russian.py +0 -0
  128. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/translate/spanish.py +0 -0
  129. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/translate/translator.py +0 -0
  130. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/__init__.py +0 -0
  131. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/cdp.py +0 -0
  132. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/cdp_driver/__init__.py +0 -0
  133. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/cdp_driver/_contradict.py +0 -0
  134. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/cdp_driver/browser.py +0 -0
  135. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/cdp_driver/cdp_util.py +0 -0
  136. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/cdp_driver/config.py +0 -0
  137. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/cdp_driver/connection.py +0 -0
  138. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/cdp_driver/element.py +0 -0
  139. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/cdp_driver/tab.py +0 -0
  140. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/dprocess.py +0 -0
  141. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/options.py +0 -0
  142. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/patcher.py +0 -0
  143. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/reactor.py +0 -0
  144. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/undetected/webelement.py +0 -0
  145. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/__init__.py +0 -0
  146. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_grid/__init__.py +0 -0
  147. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_grid/download_selenium_server.py +0 -0
  148. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_grid/font_color +0 -0
  149. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_grid/grid-hub +0 -0
  150. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_grid/grid-node +0 -0
  151. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_grid/grid_hub.py +0 -0
  152. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_grid/grid_node.py +0 -0
  153. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_grid/register-grid-node.bat +0 -0
  154. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_grid/register-grid-node.sh +0 -0
  155. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_grid/start-grid-hub.bat +0 -0
  156. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_grid/start-grid-hub.sh +0 -0
  157. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_ide/__init__.py +0 -0
  158. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase/utilities/selenium_ide/convert_ide.py +0 -0
  159. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase.egg-info/SOURCES.txt +0 -0
  160. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase.egg-info/dependency_links.txt +0 -0
  161. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase.egg-info/entry_points.txt +0 -0
  162. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/seleniumbase.egg-info/top_level.txt +0 -0
  163. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/setup.cfg +0 -0
  164. {seleniumbase-4.54.6 → seleniumbase-4.54.8}/virtualenv_install.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seleniumbase
3
- Version: 4.54.6
3
+ Version: 4.54.8
4
4
  Summary: SeleniumBase is a framework for web crawling, scraping, and testing. Supports pytest. CDP Mode adds stealth. Includes many tools.
5
5
  Home-page: https://github.com/seleniumbase/SeleniumBase
6
6
  Author: Michael Mintz
@@ -67,11 +67,11 @@ Requires-Dist: certifi>=2026.7.22
67
67
  Requires-Dist: exceptiongroup>=1.3.1
68
68
  Requires-Dist: websockets~=16.1.1; python_version == "3.10"
69
69
  Requires-Dist: websockets>=16.1.1; python_version >= "3.11"
70
- Requires-Dist: filelock>=3.32.6
70
+ Requires-Dist: filelock>=3.32.7
71
71
  Requires-Dist: fasteners>=0.20
72
72
  Requires-Dist: mycdp>=1.4.0
73
73
  Requires-Dist: pynose>=1.5.5
74
- Requires-Dist: platformdirs>=4.11.8
74
+ Requires-Dist: platformdirs>=4.11.9
75
75
  Requires-Dist: typing-extensions>=4.16.0
76
76
  Requires-Dist: sbvirtualdisplay>=1.4.0
77
77
  Requires-Dist: MarkupSafe>=3.0.3
@@ -85,10 +85,10 @@ Requires-Dist: pyyaml>=6.0.3
85
85
  Requires-Dist: pygments>=2.20.1
86
86
  Requires-Dist: pyreadline3>=3.5.4; platform_system == "Windows"
87
87
  Requires-Dist: tabcompleter>=1.4.1
88
- Requires-Dist: pdbp>=1.8.2
88
+ Requires-Dist: pdbp>=1.8.3
89
89
  Requires-Dist: idna>=3.19
90
90
  Requires-Dist: charset-normalizer<4,>=3.5.1
91
- Requires-Dist: urllib3<3,>=2.7.0
91
+ Requires-Dist: urllib3<3,>=2.8.0
92
92
  Requires-Dist: requests~=2.34.2
93
93
  Requires-Dist: sniffio==1.3.1
94
94
  Requires-Dist: h11==0.16.0
@@ -149,13 +149,13 @@ Requires-Dist: pip-system-certs>=4.0; platform_system == "Windows" and extra ==
149
149
  Provides-Extra: proxy
150
150
  Requires-Dist: proxy.py==2.4.3; extra == "proxy"
151
151
  Provides-Extra: playwright
152
- Requires-Dist: playwright>=1.62.0; extra == "playwright"
153
- Requires-Dist: greenlet>=3.5.5; extra == "playwright"
152
+ Requires-Dist: playwright>=1.63.0; extra == "playwright"
153
+ Requires-Dist: greenlet>=3.5.6; extra == "playwright"
154
154
  Requires-Dist: pyee>=13.0.1; extra == "playwright"
155
155
  Provides-Extra: pyautogui
156
156
  Requires-Dist: PyAutoGUI>=0.9.54; platform_system != "Linux" and extra == "pyautogui"
157
157
  Provides-Extra: uv
158
- Requires-Dist: uv>=0.12.14; extra == "uv"
158
+ Requires-Dist: uv>=0.12.15; extra == "uv"
159
159
  Dynamic: author
160
160
  Dynamic: author-email
161
161
  Dynamic: classifier
@@ -588,10 +588,7 @@ def get_content(
588
588
  output_format: Literal["text", "html", "urls"] = "text",
589
589
  timeout: float = 5,
590
590
  ) -> str | list[str]:
591
- """Read visible text, HTML, or discovered URLs from the selected element.
592
-
593
- Use this tool when you need to get actual page content or URL information
594
- rather than page metadata.
591
+ """Get visible text, HTML, or discovered URLs from the selected element.
595
592
 
596
593
  Args:
597
594
  selector: CSS selector or SeleniumBase-supported XPath selector.
@@ -649,15 +646,15 @@ def get_attributes(
649
646
  attribute: str | None = None,
650
647
  timeout: float = 5,
651
648
  ) -> str | dict[str, Any] | None:
652
- """Get a specific HTML attribute (or all attributes) from the
653
- first-matching element. Examples of possible attributes include
654
- href, src, value, class, id, name, type, aria-label, etc.
649
+ """Get a specific HTML attribute (or all attributes) from the selected
650
+ element. Examples of possible attributes include href, src, value, class,
651
+ id, name, type, aria-label, etc.
655
652
 
656
653
  Args:
657
654
  selector: CSS selector or SeleniumBase-supported XPath selector.
658
655
 
659
- attribute: Specific HTML attribute to retrieve. When omitted, return
660
- all HTML attributes of the first matching element as a dictionary.
656
+ attribute: Specific HTML attribute to retrieve. When omitted, returns
657
+ all HTML attributes of the matching element as a dictionary.
661
658
 
662
659
  timeout: Maximum seconds to wait for the target element. Default: 5.
663
660
 
@@ -670,9 +667,6 @@ def get_attributes(
670
667
  - Need to check element presence/visibility ->
671
668
  use 'check_if_condition'.
672
669
 
673
- This is a read-only operation: It finds elements to get the requested data,
674
- but it does not make any modifications to those elements.
675
-
676
670
  If there's no matching element found within the timeout,
677
671
  then @handle_sb_errors returns details from the exception raised.
678
672
  """
@@ -1625,10 +1619,18 @@ def scroll_page(
1625
1619
  # Windows & tabs
1626
1620
  # ---------------------------------------------------------------------------
1627
1621
 
1628
- @mcp.tool(title="Manage Window")
1629
- # Annotations intentionally omitted: 'get_rect' is a pure read while
1630
- # 'set_rect'/'maximize'/'minimize' modify window state -- no single
1631
- # read_only_hint value would be accurate for the whole tool.
1622
+ @mcp.tool(
1623
+ title="Manage Window",
1624
+ annotations=ToolAnnotations(
1625
+ destructive_hint=False,
1626
+ idempotent_hint=True,
1627
+ open_world_hint=False,
1628
+ ),
1629
+ )
1630
+ # The 'read_only_hint' annotation has been intentionally omitted:
1631
+ # 'get_rect' is a pure read while
1632
+ # 'set_rect'/'maximize'/'minimize' modify window state.
1633
+ # There's no single 'read_only_hint' value that's accurate for the whole tool.
1632
1634
  @handle_sb_errors
1633
1635
  def manage_window(
1634
1636
  action: Literal[
@@ -1805,19 +1807,17 @@ def manage_tabs(
1805
1807
  )
1806
1808
  @handle_sb_errors
1807
1809
  def solve_captcha() -> str:
1808
- """Attempt a SeleniumBase CDP-based CAPTCHA interaction, such as clicking
1810
+ """Perform a SeleniumBase CDP-based CAPTCHA interaction, such as clicking
1809
1811
  a CAPTCHA checkbox, or performing a drag/drop action on a slider CAPTCHA.
1810
1812
 
1811
- This tool attempts to interact with CAPTCHA controls such as Cloudflare
1812
- Turnstile, reCAPTCHA, hCaptcha, DataDome Slider, or FriendlyCaptcha via
1813
- the Chrome DevTools Protocol (CDP), which is usually stealthier than
1814
- JavaScript because CDP actions can avoid triggering `isTrusted: false`.
1813
+ Supported CAPTCHAs include: Cloudflare Turnstile, reCAPTCHA, hCaptcha,
1814
+ DataDome Slider, and FriendlyCaptcha.
1815
1815
 
1816
- This tool automatically detects the coordinates of CAPTCHA checkboxes
1817
- for determining the correct location to perform the click. If no CAPTCHA
1818
- is detected on the current page, then no click action is attempted.
1816
+ This tool automatically detects the location of CAPTCHA checkboxes
1817
+ for the click action. If none of the supported CAPTCHAs are detected
1818
+ on the current page, then no click action is attempted.
1819
1819
 
1820
- The tool does not guarantee that the CAPTCHA was solved. Some CAPTCHA
1820
+ The tool does not guarantee that the CAPTCHA gets solved. Some CAPTCHA
1821
1821
  controls are embedded inside shadow DOM or otherwise do not expose an
1822
1822
  easy success signal. A successful attempt may result in changes to page
1823
1823
  state or browser cookies.
@@ -1826,12 +1826,19 @@ def solve_captcha() -> str:
1826
1826
  1. Inspect the webpage with get_content when you need to
1827
1827
  determine whether CAPTCHA-related controls are present.
1828
1828
  2. Call 'solve_captcha' to attempt the CAPTCHA interaction.
1829
- 3. Use 'get_page_info', 'get_content', 'check_if_condition',
1830
- or 'manage_cookies' to inspect resulting page/session state.
1829
+ 3. Use 'get_page_info', 'get_content', 'check_if_condition', or
1830
+ 'manage_cookies' to inspect the resulting page/session state
1831
+ to determine whether the interaction appears to have succeeded.
1831
1832
 
1832
1833
  Returns:
1833
- A message confirming that the CAPTCHA interaction was attempted.
1834
- The message is the same for both successful and failed attempts.
1834
+ A message indicating that the CAPTCHA interaction was attempted.
1835
+
1836
+ Tool selection:
1837
+ - Need to interact with one of the supported CAPTCHAs ->
1838
+ use solve_captcha.
1839
+ - Need to hover an element in order to click another ->
1840
+ use hover_action with action="hover_and_click".
1841
+ - Need to click a specific visible element -> use click_element.
1835
1842
  """
1836
1843
  sb = _get_sb()
1837
1844
  sb.solve_captcha()
@@ -7,11 +7,11 @@ certifi>=2026.7.22
7
7
  exceptiongroup>=1.3.1
8
8
  websockets~=16.1.1;python_version=="3.10"
9
9
  websockets>=16.1.1;python_version>="3.11"
10
- filelock>=3.32.6
10
+ filelock>=3.32.7
11
11
  fasteners>=0.20
12
12
  mycdp>=1.4.0
13
13
  pynose>=1.5.5
14
- platformdirs>=4.11.8
14
+ platformdirs>=4.11.9
15
15
  typing-extensions>=4.16.0
16
16
  sbvirtualdisplay>=1.4.0
17
17
  MarkupSafe>=3.0.3
@@ -25,10 +25,10 @@ pyyaml>=6.0.3
25
25
  pygments>=2.20.1
26
26
  pyreadline3>=3.5.4;platform_system=="Windows"
27
27
  tabcompleter>=1.4.1
28
- pdbp>=1.8.2
28
+ pdbp>=1.8.3
29
29
  idna>=3.19
30
30
  charset-normalizer>=3.5.1,<4
31
- urllib3>=2.7.0,<3
31
+ urllib3>=2.8.0,<3
32
32
  requests~=2.34.2
33
33
  sniffio==1.3.1
34
34
  h11==0.16.0
@@ -1,2 +1,2 @@
1
1
  # seleniumbase package
2
- __version__ = "4.54.6"
2
+ __version__ = "4.54.8"
@@ -334,7 +334,7 @@ def get_configured_sb(context):
334
334
  continue
335
335
  # Handle: -D locale-code=CODE / locale_code=CODE / locale=CODE
336
336
  if low_key in ["locale-code", "locale_code", "locale"]:
337
- sb.start_page = userdata[key]
337
+ sb.locale_code = userdata[key]
338
338
  continue
339
339
  # Handle: -D pdb / ipdb
340
340
  if low_key in ["pdb", "ipdb"]:
@@ -881,7 +881,7 @@ def get_configured_sb(context):
881
881
  sb.cap_file = cap_file
882
882
  continue
883
883
  # Handle: -D cap-string=STRING / cap_string=STRING
884
- if low_key == "cap_string":
884
+ if low_key in ["cap-string", "cap_string"]:
885
885
  cap_string = userdata[key]
886
886
  if cap_string == "true":
887
887
  cap_string = sb.cap_string # revert to default
@@ -1134,14 +1134,19 @@ def _get_test_ids_():
1134
1134
  def dashboard_pre_processing():
1135
1135
  import subprocess
1136
1136
 
1137
- command_args = sys.argv[1:]
1138
- command_string = " ".join(command_args)
1139
- command_string = command_string.replace("--quiet", "")
1140
- command_string = command_string.replace("-q", "")
1137
+ # Safely filter out --quiet and -q without shell string joining
1138
+ command_args = [
1139
+ arg for arg in sys.argv[1:] if arg not in ("--quiet", "-q")
1140
+ ]
1141
+
1142
+ # Construct a explicit argument list without shell=True
1143
+ cmd = ["behave", "-d"] + command_args + ["--show-source"]
1144
+
1141
1145
  proc = subprocess.Popen(
1142
- "behave -d %s --show-source" % command_string,
1146
+ cmd,
1143
1147
  stdout=subprocess.PIPE,
1144
- shell=True,
1148
+ stderr=subprocess.PIPE,
1149
+ shell=False,
1145
1150
  )
1146
1151
  (output, error) = proc.communicate()
1147
1152
  filename_count = 0
@@ -1205,7 +1205,7 @@ def main():
1205
1205
  show_behave_options()
1206
1206
  elif command == "proxy" or command == "--proxy":
1207
1207
  import fasteners
1208
- import os
1208
+ import subprocess
1209
1209
  import warnings
1210
1210
  with warnings.catch_warnings():
1211
1211
  warnings.simplefilter("ignore", category=UserWarning)
@@ -1219,7 +1219,7 @@ def main():
1219
1219
  shared_utils.pip_install(
1220
1220
  "proxy.py", version=constants.ProxyPy.VER
1221
1221
  )
1222
- os.system("proxy %s" % " ".join(sys.argv[2:]))
1222
+ subprocess.run(["proxy"] + sys.argv[2:])
1223
1223
  elif command == "help" or command == "--help" or command == "-h":
1224
1224
  if len(command_args) >= 1:
1225
1225
  if command_args[0] == "get":
@@ -24,6 +24,7 @@ Output:
24
24
  """
25
25
  import colorama
26
26
  import os
27
+ import subprocess
27
28
  import sys
28
29
 
29
30
 
@@ -73,15 +74,17 @@ def main():
73
74
 
74
75
  command_args = sys.argv[2:]
75
76
  file_name = command_args[0]
77
+ if file_name:
78
+ file_name = str(file_name)
76
79
  if file_name == "-h" or file_name == "--help":
77
80
  invalid_run_command("help")
78
81
  elif not file_name.endswith(".py"):
79
82
  error_msg = 'File name must end with ".py"!'
80
- elif "*" in file_name or len(str(file_name)) < 4:
83
+ elif "*" in file_name or len(file_name) < 4:
81
84
  error_msg = "Invalid file name!"
82
85
  elif file_name.startswith("-"):
83
86
  error_msg = 'File name cannot start with "-"!'
84
- elif "/" in str(file_name) or "\\" in str(file_name):
87
+ elif any(sep in file_name for sep in ("/", "\\")):
85
88
  error_msg = "File must be created in the current directory!"
86
89
  elif os.path.exists(os.getcwd() + "/" + file_name):
87
90
  error_msg = 'File "%s" already exists in this directory!' % file_name
@@ -256,12 +259,10 @@ def main():
256
259
  file = open(file_path, mode="w+", encoding="utf-8")
257
260
  file.writelines("\r\n".join(data))
258
261
  file.close()
259
- if " " not in file_name:
260
- os.system("sbase print %s -n" % file_name)
261
- elif '"' not in file_name:
262
- os.system('sbase print "%s" -n' % file_name)
263
- else:
264
- os.system("sbase print '%s' -n" % file_name)
262
+
263
+ print_cmd = ["sbase", "print", file_name, "-n"]
264
+ subprocess.run(print_cmd)
265
+
265
266
  success = (
266
267
  "\n" + c1 + '* Chart Presentation: "' + file_name + '" was created! *'
267
268
  "" + cr + "\n"
@@ -21,6 +21,7 @@ Output:
21
21
  """
22
22
  import colorama
23
23
  import os
24
+ import subprocess
24
25
  import sys
25
26
 
26
27
 
@@ -68,15 +69,17 @@ def main():
68
69
 
69
70
  command_args = sys.argv[2:]
70
71
  dir_name = command_args[0]
72
+ if dir_name:
73
+ dir_name = str(dir_name)
71
74
  if dir_name == "-h" or dir_name == "--help":
72
75
  invalid_run_command("help")
73
- elif len(str(dir_name)) < 2:
76
+ elif len(dir_name) < 2:
74
77
  error_msg = "Directory name length must be at least 2 characters long!"
75
- elif "/" in str(dir_name) or "\\" in str(dir_name):
78
+ elif any(sep in dir_name for sep in ("/", "\\")):
76
79
  error_msg = 'Directory name must not include slashes ("/", "\\")!'
77
80
  elif dir_name.startswith("-"):
78
81
  error_msg = 'Directory name cannot start with "-"!'
79
- elif os.path.exists(os.getcwd() + "/" + dir_name):
82
+ elif os.path.exists(os.path.join(os.getcwd(), dir_name)):
80
83
  error_msg = (
81
84
  'Directory "%s" already exists in this directory!' % dir_name
82
85
  )
@@ -117,7 +120,7 @@ def main():
117
120
  pass
118
121
  data.append(seleniumbase_req)
119
122
  data.append("")
120
- file_path = "%s/%s" % (dir_name, "requirements.txt")
123
+ file_path = os.path.join(dir_name, "requirements.txt")
121
124
  file = open(file_path, mode="w+", encoding="utf-8")
122
125
  file.writelines("\r\n".join(data))
123
126
  file.close()
@@ -155,7 +158,7 @@ def main():
155
158
  data.append(" staging: custom marker")
156
159
  data.append(" production: custom marker")
157
160
  data.append("")
158
- file_path = "%s/%s" % (dir_name, "pytest.ini")
161
+ file_path = os.path.join(dir_name, "pytest.ini")
159
162
  file = open(file_path, mode="w+", encoding="utf-8")
160
163
  file.writelines("\r\n".join(data))
161
164
  file.close()
@@ -172,14 +175,14 @@ def main():
172
175
  data.append("[behave]")
173
176
  data.append("show_skipped=false")
174
177
  data.append("show_timings=false")
175
- file_path = "%s/%s" % (dir_name, "setup.cfg")
178
+ file_path = os.path.join(dir_name, "setup.cfg")
176
179
  file = open(file_path, mode="w+", encoding="utf-8")
177
180
  file.writelines("\r\n".join(data))
178
181
  file.close()
179
182
 
180
183
  data = []
181
184
  data.append("")
182
- file_path = "%s/%s" % (dir_name, "__init__.py")
185
+ file_path = os.path.join(dir_name, "__init__.py")
183
186
  file = open(file_path, mode="w+", encoding="utf-8")
184
187
  file.writelines("\r\n".join(data))
185
188
  file.close()
@@ -321,15 +324,15 @@ def main():
321
324
  data.append("temp")
322
325
  data.append("temp_*/")
323
326
  data.append("node_modules")
324
- file_path = "%s/%s" % (dir_name, ".gitignore")
327
+ file_path = os.path.join(dir_name, ".gitignore")
325
328
  file = open(file_path, mode="w+", encoding="utf-8")
326
329
  file.writelines("\r\n".join(data))
327
330
  file.close()
328
331
 
329
332
  if gha:
330
- dir_name_b = dir_name + "/" + ".github"
333
+ dir_name_b = os.path.join(dir_name, ".github")
331
334
  os.mkdir(dir_name_b)
332
- dir_name_c = dir_name_b + "/" + "workflows"
335
+ dir_name_c = os.path.join(dir_name_b, "workflows")
333
336
  os.mkdir(dir_name_c)
334
337
 
335
338
  data = []
@@ -377,7 +380,7 @@ def main():
377
380
  data.append(" path: ./latest_logs/")
378
381
  data.append(" if-no-files-found: ignore")
379
382
  data.append("")
380
- file_path = "%s/%s" % (dir_name_c, "python-package.yml")
383
+ file_path = os.path.join(dir_name_c, "python-package.yml")
381
384
  file = open(file_path, mode="w+", encoding="utf-8")
382
385
  file.writelines("\r\n".join(data))
383
386
  file.close()
@@ -393,12 +396,14 @@ def main():
393
396
  data.append(" ├── pytest.ini")
394
397
  data.append(" ├── requirements.txt")
395
398
  data.append(" └── setup.cfg")
396
- file_path = "%s/%s" % (dir_name, "outline.rst")
399
+ file_path = os.path.join(dir_name, "outline.rst")
397
400
  file = open(file_path, mode="w+", encoding="utf-8")
398
401
  file.writelines("\r\n".join(data))
399
402
  file.close()
400
- os.system("sbase print %s -n" % file_path)
403
+
404
+ subprocess.run(["sbase", "print", file_path, "-n"])
401
405
  os.remove(file_path)
406
+
402
407
  success = (
403
408
  "\n" + c1 + '* Directory "' + dir_name + '" was created '
404
409
  "with config files! *" + cr + "\n"
@@ -437,7 +442,7 @@ def main():
437
442
  data.append(' self.js_click("a#logout_sidebar_link")')
438
443
  data.append(' self.assert_element("div#login_button_container")')
439
444
  data.append("")
440
- file_path = "%s/%s" % (dir_name, "my_first_test.py")
445
+ file_path = os.path.join(dir_name, "my_first_test.py")
441
446
  file = open(file_path, mode="w+", encoding="utf-8")
442
447
  file.writelines("\r\n".join(data))
443
448
  file.close()
@@ -530,7 +535,7 @@ def main():
530
535
  data.append(' self.type("input", "Have a Nice Day!")')
531
536
  data.append(' self.assert_text("SeleniumBase", "h2")')
532
537
  data.append("")
533
- file_path = "%s/%s" % (dir_name, "test_demo_site.py")
538
+ file_path = os.path.join(dir_name, "test_demo_site.py")
534
539
  file = open(file_path, mode="w+", encoding="utf-8")
535
540
  file.writelines("\r\n".join(data))
536
541
  file.close()
@@ -570,17 +575,17 @@ def main():
570
575
  data.append(' self.assert_title_contains(title_text)')
571
576
  data.append(' self.save_screenshot_to_logs()')
572
577
  data.append("")
573
- file_path = "%s/%s" % (dir_name, "parameterized_test.py")
578
+ file_path = os.path.join(dir_name, "parameterized_test.py")
574
579
  file = open(file_path, mode="w+", encoding="utf-8")
575
580
  file.writelines("\r\n".join(data))
576
581
  file.close()
577
582
 
578
- dir_name_2 = dir_name + "/" + "boilerplates"
583
+ dir_name_2 = os.path.join(dir_name, "boilerplates")
579
584
  os.mkdir(dir_name_2)
580
585
 
581
586
  data = []
582
587
  data.append("")
583
- file_path = "%s/%s" % (dir_name_2, "__init__.py")
588
+ file_path = os.path.join(dir_name_2, "__init__.py")
584
589
  file = open(file_path, mode="w+", encoding="utf-8")
585
590
  file.writelines("\r\n".join(data))
586
591
  file.close()
@@ -615,7 +620,7 @@ def main():
615
620
  data.append(" # <<< Placeholder. Add your code here. >>>")
616
621
  data.append(" pass")
617
622
  data.append("")
618
- file_path = "%s/%s" % (dir_name_2, "base_test_case.py")
623
+ file_path = os.path.join(dir_name_2, "base_test_case.py")
619
624
  file = open(file_path, mode="w+", encoding="utf-8")
620
625
  file.writelines("\r\n".join(data))
621
626
  file.close()
@@ -624,7 +629,7 @@ def main():
624
629
  data.append("class Page(object):")
625
630
  data.append(' html = "html"')
626
631
  data.append("")
627
- file_path = "%s/%s" % (dir_name_2, "page_objects.py")
632
+ file_path = os.path.join(dir_name_2, "page_objects.py")
628
633
  file = open(file_path, mode="w+", encoding="utf-8")
629
634
  file.writelines("\r\n".join(data))
630
635
  file.close()
@@ -640,7 +645,7 @@ def main():
640
645
  data.append(" self.example_method()")
641
646
  data.append(" self.assert_element(Page.html)")
642
647
  data.append("")
643
- file_path = "%s/%s" % (dir_name_2, "boilerplate_test.py")
648
+ file_path = os.path.join(dir_name_2, "boilerplate_test.py")
644
649
  file = open(file_path, mode="w+", encoding="utf-8")
645
650
  file.writelines("\r\n".join(data))
646
651
  file.close()
@@ -664,7 +669,7 @@ def main():
664
669
  data.append(' self.assert_text("Hello!", "p")')
665
670
  data.append(' DataPage().add_input_text(self, "Goodbye!")')
666
671
  data.append("")
667
- file_path = "%s/%s" % (dir_name_2, "classic_obj_test.py")
672
+ file_path = os.path.join(dir_name_2, "classic_obj_test.py")
668
673
  file = open(file_path, mode="w+", encoding="utf-8")
669
674
  file.writelines("\r\n".join(data))
670
675
  file.close()
@@ -684,17 +689,17 @@ def main():
684
689
  data.append(' sb.assert_text("Hello!", "p")')
685
690
  data.append(' DataPage().add_input_text(sb, "Goodbye!")')
686
691
  data.append("")
687
- file_path = "%s/%s" % (dir_name_2, "sb_fixture_test.py")
692
+ file_path = os.path.join(dir_name_2, "sb_fixture_test.py")
688
693
  file = open(file_path, mode="w+", encoding="utf-8")
689
694
  file.writelines("\r\n".join(data))
690
695
  file.close()
691
696
 
692
- dir_name_3 = dir_name_2 + "/" + "samples"
697
+ dir_name_3 = os.path.join(dir_name_2, "samples")
693
698
  os.mkdir(dir_name_3)
694
699
 
695
700
  data = []
696
701
  data.append("")
697
- file_path = "%s/%s" % (dir_name_3, "__init__.py")
702
+ file_path = os.path.join(dir_name_3, "__init__.py")
698
703
  file = open(file_path, mode="w+", encoding="utf-8")
699
704
  file.writelines("\r\n".join(data))
700
705
  file.close()
@@ -713,9 +718,10 @@ def main():
713
718
  data.append(" self.get_new_driver(undetectable=True)")
714
719
  data.append(' self.goto("https://google.com/ncr")')
715
720
  data.append(
716
- " sb.click_if_visible('button:contains(\"Accept all\")')"
721
+ " self.click_if_visible('button:contains(\"Accept all\")')"
717
722
  )
718
723
  data.append(' self.assert_title_contains("Google")')
724
+ data.append(" self.sleep(0.05)")
719
725
  data.append(" self.save_screenshot_to_logs()")
720
726
  data.append(' self.type(HomePage.search_box, "GitHub")')
721
727
  data.append(" self.assert_element(HomePage.search_button)")
@@ -726,7 +732,7 @@ def main():
726
732
  ' self.assert_text("github.com", ResultsPage.search_results)'
727
733
  )
728
734
  data.append("")
729
- file_path = "%s/%s" % (dir_name_3, "google_test.py")
735
+ file_path = os.path.join(dir_name_3, "google_test.py")
730
736
  file = open(file_path, mode="w+", encoding="utf-8")
731
737
  file.writelines("\r\n".join(data))
732
738
  file.close()
@@ -734,7 +740,7 @@ def main():
734
740
  data = []
735
741
  data.append("class HomePage(object):")
736
742
  data.append(" dialog_box = '[role=\"dialog\"] div'")
737
- data.append(" search_box = '[title=\"Search\"]'")
743
+ data.append(" search_box = '[name=\"q\"]'")
738
744
  data.append(" search_button = 'input[value=\"Google Search\"]'")
739
745
  data.append(
740
746
  ' feeling_lucky_button = """input[value="I\'m Feeling Lucky"]"""'
@@ -744,7 +750,7 @@ def main():
744
750
  data.append("class ResultsPage(object):")
745
751
  data.append(' search_results = "div#center_col"')
746
752
  data.append("")
747
- file_path = "%s/%s" % (dir_name_3, "google_objects.py")
753
+ file_path = os.path.join(dir_name_3, "google_objects.py")
748
754
  file = open(file_path, mode="w+", encoding="utf-8")
749
755
  file.writelines("\r\n".join(data))
750
756
  file.close()
@@ -776,7 +782,7 @@ def main():
776
782
  data.append(' self.js_click("a#logout_sidebar_link")')
777
783
  data.append(' self.assert_element("div#login_button_container")')
778
784
  data.append("")
779
- file_path = "%s/%s" % (dir_name_3, "swag_labs_test.py")
785
+ file_path = os.path.join(dir_name_3, "swag_labs_test.py")
780
786
  file = open(file_path, mode="w+", encoding="utf-8")
781
787
  file.writelines("\r\n".join(data))
782
788
  file.close()
@@ -803,7 +809,7 @@ def main():
803
809
  data.append(' sb.js_click("a#logout_sidebar_link")')
804
810
  data.append(' sb.assert_element("div#login_button_container")')
805
811
  data.append("")
806
- file_path = "%s/%s" % (dir_name_3, "sb_swag_test.py")
812
+ file_path = os.path.join(dir_name_3, "sb_swag_test.py")
807
813
  file = open(file_path, mode="w+", encoding="utf-8")
808
814
  file.writelines("\r\n".join(data))
809
815
  file.close()
@@ -834,16 +840,12 @@ def main():
834
840
  data.append(" ├── google_test.py")
835
841
  data.append(" ├── sb_swag_test.py")
836
842
  data.append(" └── swag_labs_test.py")
837
- file_path = "%s/%s" % (dir_name, "outline.rst")
843
+ file_path = os.path.join(dir_name, "outline.rst")
838
844
  file = open(file_path, mode="w+", encoding="utf-8")
839
845
  file.writelines("\r\n".join(data))
840
846
  file.close()
841
- if " " not in file_path:
842
- os.system("sbase print %s -n" % file_path)
843
- elif '"' not in file_path:
844
- os.system('sbase print "%s" -n' % file_path)
845
- else:
846
- os.system("sbase print '%s' -n" % file_path)
847
+
848
+ subprocess.run(["sbase", "print", file_path, "-n"])
847
849
  os.remove(file_path)
848
850
 
849
851
  success = (
@@ -42,6 +42,7 @@ Output:
42
42
  """
43
43
  import colorama
44
44
  import os
45
+ import subprocess
45
46
  import sys
46
47
 
47
48
 
@@ -112,15 +113,17 @@ def main():
112
113
 
113
114
  command_args = sys.argv[2:]
114
115
  file_name = command_args[0]
116
+ if file_name:
117
+ file_name = str(file_name)
115
118
  if file_name == "-h" or file_name == "--help":
116
119
  invalid_run_command("help")
117
120
  elif not file_name.endswith(".py"):
118
121
  error_msg = 'File name must end with ".py"!'
119
- elif "*" in file_name or len(str(file_name)) < 4:
122
+ elif "*" in file_name or len(file_name) < 4:
120
123
  error_msg = "Invalid file name!"
121
124
  elif file_name.startswith("-"):
122
125
  error_msg = 'File name cannot start with "-"!'
123
- elif "/" in str(file_name) or "\\" in str(file_name):
126
+ elif any(sep in file_name for sep in ("/", "\\")):
124
127
  error_msg = "File must be created in the current directory!"
125
128
  elif os.path.exists(os.getcwd() + "/" + file_name):
126
129
  error_msg = 'File "%s" already exists in this directory!' % file_name
@@ -415,12 +418,10 @@ def main():
415
418
  file = open(file_path, mode="w+", encoding="utf-8")
416
419
  file.writelines("\r\n".join(data))
417
420
  file.close()
418
- if " " not in file_name:
419
- os.system("sbase print %s -n" % file_name)
420
- elif '"' not in file_name:
421
- os.system('sbase print "%s" -n' % file_name)
422
- else:
423
- os.system("sbase print '%s' -n" % file_name)
421
+
422
+ print_cmd = ["sbase", "print", file_name, "-n"]
423
+ subprocess.run(print_cmd)
424
+
424
425
  success = (
425
426
  "\n" + c1 + '* Test file: "' + file_name + '" was created! *'
426
427
  "" + cr + "\n"