seleniumbase 4.54.7__tar.gz → 4.54.9__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.7/seleniumbase.egg-info → seleniumbase-4.54.9}/PKG-INFO +6 -6
  2. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/mcp_servers/server.py +34 -33
  3. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/requirements.txt +5 -5
  4. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/__version__.py +1 -1
  5. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/behave/behave_sb.py +13 -8
  6. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/run.py +2 -2
  7. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/sb_mkchart.py +9 -8
  8. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/sb_mkdir.py +40 -38
  9. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/sb_mkfile.py +9 -8
  10. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/sb_mkpres.py +9 -8
  11. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/sb_mkrec.py +35 -42
  12. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/fixtures/base_case.py +4 -7
  13. {seleniumbase-4.54.7 → seleniumbase-4.54.9/seleniumbase.egg-info}/PKG-INFO +6 -6
  14. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase.egg-info/requires.txt +5 -5
  15. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/setup.py +5 -5
  16. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/.gitignore +0 -0
  17. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/LICENSE +0 -0
  18. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/MANIFEST.in +0 -0
  19. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/README.md +0 -0
  20. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/install.sh +0 -0
  21. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/mcp_servers/__init__.py +0 -0
  22. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/pyproject.toml +0 -0
  23. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/pytest.ini +0 -0
  24. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/sbase/__init__.py +0 -0
  25. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/sbase/__main__.py +0 -0
  26. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/sbase/steps.py +0 -0
  27. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/__init__.py +0 -0
  28. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/__main__.py +0 -0
  29. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/behave/__init__.py +0 -0
  30. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/behave/behave_helper.py +0 -0
  31. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/behave/steps.py +0 -0
  32. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/common/__init__.py +0 -0
  33. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/common/decorators.py +0 -0
  34. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/common/encryption.py +0 -0
  35. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/common/exceptions.py +0 -0
  36. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/common/obfuscate.py +0 -0
  37. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/common/unobfuscate.py +0 -0
  38. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/config/__init__.py +0 -0
  39. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/config/ad_block_list.py +0 -0
  40. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/config/proxy_list.py +0 -0
  41. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/config/settings.py +0 -0
  42. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/__init__.py +0 -0
  43. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/logo_helper.py +0 -0
  44. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/rich_helper.py +0 -0
  45. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/sb_behave_gui.py +0 -0
  46. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/sb_caseplans.py +0 -0
  47. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/sb_commander.py +0 -0
  48. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/sb_install.py +0 -0
  49. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/sb_objectify.py +0 -0
  50. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/sb_print.py +0 -0
  51. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/console_scripts/sb_recorder.py +0 -0
  52. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/__init__.py +0 -0
  53. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/application_manager.py +0 -0
  54. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/browser_launcher.py +0 -0
  55. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/capabilities_parser.py +0 -0
  56. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/colored_traceback.py +0 -0
  57. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/create_db_tables.sql +0 -0
  58. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/detect_b_ver.py +0 -0
  59. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/download_helper.py +0 -0
  60. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/encoded_images.py +0 -0
  61. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/jqc_helper.py +0 -0
  62. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/log_helper.py +0 -0
  63. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/mysql.py +0 -0
  64. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/nest_asyncio.py +0 -0
  65. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/proxy_helper.py +0 -0
  66. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/recorder_helper.py +0 -0
  67. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/report_helper.py +0 -0
  68. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/s3_manager.py +0 -0
  69. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/sb_cdp.py +0 -0
  70. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/sb_driver.py +0 -0
  71. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/session_helper.py +0 -0
  72. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/settings_parser.py +0 -0
  73. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/style_sheet.py +0 -0
  74. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/testcase_manager.py +0 -0
  75. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/tour_helper.py +0 -0
  76. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/core/visual_helper.py +0 -0
  77. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/drivers/__init__.py +0 -0
  78. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/drivers/brave_drivers/__init__.py +0 -0
  79. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/drivers/cft_drivers/__init__.py +0 -0
  80. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
  81. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/drivers/chs_drivers/__init__.py +0 -0
  82. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/drivers/comet_drivers/__init__.py +0 -0
  83. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/drivers/opera_drivers/__init__.py +0 -0
  84. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/extensions/__init__.py +0 -0
  85. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/extensions/ad_block.zip +0 -0
  86. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/extensions/disable_csp.zip +0 -0
  87. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/extensions/recorder.zip +0 -0
  88. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/extensions/sbase_ext.zip +0 -0
  89. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/fixtures/__init__.py +0 -0
  90. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/fixtures/constants.py +0 -0
  91. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/fixtures/css_to_xpath.py +0 -0
  92. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/fixtures/errors.py +0 -0
  93. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/fixtures/js_utils.py +0 -0
  94. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/fixtures/page_actions.py +0 -0
  95. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/fixtures/page_utils.py +0 -0
  96. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/fixtures/shared_utils.py +0 -0
  97. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/fixtures/unittest_helper.py +0 -0
  98. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/fixtures/words.py +0 -0
  99. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/fixtures/xpath_to_css.py +0 -0
  100. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/js_code/__init__.py +0 -0
  101. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/js_code/active_css_js.py +0 -0
  102. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/js_code/live_js.py +0 -0
  103. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/js_code/recorder_js.py +0 -0
  104. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/masterqa/__init__.py +0 -0
  105. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/masterqa/master_qa.py +0 -0
  106. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/plugins/__init__.py +0 -0
  107. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/plugins/base_plugin.py +0 -0
  108. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/plugins/basic_test_info.py +0 -0
  109. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/plugins/db_reporting_plugin.py +0 -0
  110. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/plugins/driver_manager.py +0 -0
  111. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/plugins/page_source.py +0 -0
  112. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/plugins/pytest_plugin.py +0 -0
  113. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/plugins/s3_logging_plugin.py +0 -0
  114. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/plugins/sb_manager.py +0 -0
  115. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/plugins/screen_shots.py +0 -0
  116. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/plugins/selenium_plugin.py +0 -0
  117. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/resources/__init__.py +0 -0
  118. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/translate/__init__.py +0 -0
  119. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/translate/chinese.py +0 -0
  120. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/translate/dutch.py +0 -0
  121. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/translate/french.py +0 -0
  122. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/translate/italian.py +0 -0
  123. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/translate/japanese.py +0 -0
  124. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/translate/korean.py +0 -0
  125. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/translate/master_dict.py +0 -0
  126. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/translate/portuguese.py +0 -0
  127. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/translate/russian.py +0 -0
  128. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/translate/spanish.py +0 -0
  129. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/translate/translator.py +0 -0
  130. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/__init__.py +0 -0
  131. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/cdp.py +0 -0
  132. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/cdp_driver/__init__.py +0 -0
  133. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/cdp_driver/_contradict.py +0 -0
  134. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/cdp_driver/browser.py +0 -0
  135. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/cdp_driver/cdp_util.py +0 -0
  136. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/cdp_driver/config.py +0 -0
  137. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/cdp_driver/connection.py +0 -0
  138. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/cdp_driver/element.py +0 -0
  139. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/cdp_driver/tab.py +0 -0
  140. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/dprocess.py +0 -0
  141. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/options.py +0 -0
  142. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/patcher.py +0 -0
  143. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/reactor.py +0 -0
  144. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/undetected/webelement.py +0 -0
  145. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/__init__.py +0 -0
  146. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_grid/__init__.py +0 -0
  147. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_grid/download_selenium_server.py +0 -0
  148. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_grid/font_color +0 -0
  149. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_grid/grid-hub +0 -0
  150. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_grid/grid-node +0 -0
  151. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_grid/grid_hub.py +0 -0
  152. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_grid/grid_node.py +0 -0
  153. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_grid/register-grid-node.bat +0 -0
  154. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_grid/register-grid-node.sh +0 -0
  155. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_grid/start-grid-hub.bat +0 -0
  156. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_grid/start-grid-hub.sh +0 -0
  157. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_ide/__init__.py +0 -0
  158. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase/utilities/selenium_ide/convert_ide.py +0 -0
  159. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase.egg-info/SOURCES.txt +0 -0
  160. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase.egg-info/dependency_links.txt +0 -0
  161. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase.egg-info/entry_points.txt +0 -0
  162. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/seleniumbase.egg-info/top_level.txt +0 -0
  163. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/setup.cfg +0 -0
  164. {seleniumbase-4.54.7 → seleniumbase-4.54.9}/virtualenv_install.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seleniumbase
3
- Version: 4.54.7
3
+ Version: 4.54.9
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.7
70
+ Requires-Dist: filelock>=4.0.0
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,8 +85,8 @@ 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
89
- Requires-Dist: idna>=3.19
88
+ Requires-Dist: pdbp>=1.8.3
89
+ Requires-Dist: idna>=3.20
90
90
  Requires-Dist: charset-normalizer<4,>=3.5.1
91
91
  Requires-Dist: urllib3<3,>=2.8.0
92
92
  Requires-Dist: requests~=2.34.2
@@ -109,7 +109,7 @@ Requires-Dist: pytest-html==4.0.2
109
109
  Requires-Dist: pytest-metadata==3.1.1
110
110
  Requires-Dist: pytest-ordering==0.6
111
111
  Requires-Dist: pytest-rerunfailures==16.3; python_version == "3.10"
112
- Requires-Dist: pytest-rerunfailures==16.6.1; python_version >= "3.11"
112
+ Requires-Dist: pytest-rerunfailures==16.7; python_version >= "3.11"
113
113
  Requires-Dist: pytest-xdist==3.8.0
114
114
  Requires-Dist: parameterized==0.9.0
115
115
  Requires-Dist: behave==1.2.6
@@ -646,15 +646,16 @@ def get_attributes(
646
646
  attribute: str | None = None,
647
647
  timeout: float = 5,
648
648
  ) -> str | dict[str, Any] | None:
649
- """Get a specific HTML attribute (or all attributes) from the
650
- first-matching element. Examples of possible attributes include
651
- 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.
652
652
 
653
653
  Args:
654
654
  selector: CSS selector or SeleniumBase-supported XPath selector.
655
+ If multiple elements match, then only the first one is used.
655
656
 
656
- attribute: Specific HTML attribute to retrieve. When omitted, return
657
- all HTML attributes of the first matching element as a dictionary.
657
+ attribute: Specific HTML attribute to retrieve. When omitted, returns
658
+ all HTML attributes of the matching element as a dictionary.
658
659
 
659
660
  timeout: Maximum seconds to wait for the target element. Default: 5.
660
661
 
@@ -1057,6 +1058,8 @@ def select_option(
1057
1058
  ) -> str:
1058
1059
  """Select an option from an HTML <select> dropdown.
1059
1060
 
1061
+ Changing a dropdown selection can trigger additional events on a page.
1062
+
1060
1063
  Args:
1061
1064
  dropdown_selector: CSS selector identifying the <select> element.
1062
1065
 
@@ -1445,8 +1448,11 @@ def manage_cookies(
1445
1448
  Notes:
1446
1449
  Loading saved cookies does not guarantee restoration of a login.
1447
1450
  Cookies may be expired, invalidated, domain/path restricted, or
1448
- dependent on other browser state. Navigate to the relevant site when
1449
- necessary so the browser has the appropriate origin for the cookies.
1451
+ dependent on other browser state.
1452
+
1453
+ When not to use:
1454
+ - Do not use this tool to load cookies if the cookie origin does
1455
+ not match the origin of the current page.
1450
1456
  """
1451
1457
  sb = _get_sb()
1452
1458
 
@@ -1800,43 +1806,38 @@ def manage_tabs(
1800
1806
  annotations=ToolAnnotations(
1801
1807
  read_only_hint=False,
1802
1808
  destructive_hint=False,
1803
- idempotent_hint=False, # A 2nd attempt on an already-handled or
1804
- # rotated CAPTCHA widget isn't guaranteed to be a no-op.
1809
+ idempotent_hint=False, # Re-attempting an already-handled or
1810
+ # rotated CAPTCHA is not a no-op.
1805
1811
  open_world_hint=True, # Interacts with a 3rd-party CAPTCHA widget.
1806
1812
  ),
1807
1813
  )
1808
1814
  @handle_sb_errors
1809
1815
  def solve_captcha() -> str:
1810
- """Attempt a SeleniumBase CDP-based CAPTCHA interaction, such as clicking
1811
- a CAPTCHA checkbox, or performing a drag/drop action on a slider CAPTCHA.
1816
+ """Attempts a SeleniumBase CDP interaction (click or drag/drop) to solve
1817
+ detectable CAPTCHAs on the current page.
1812
1818
 
1813
- Supported CAPTCHAs include: Cloudflare Turnstile, reCAPTCHA, hCaptcha,
1814
- DataDome Slider, and FriendlyCaptcha.
1819
+ Supported CAPTCHAs:
1820
+ - CF Turnstile, reCAPTCHA, hCaptcha, DataDome Slider, FriendlyCaptcha
1815
1821
 
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.
1822
+ Tool workflow:
1823
+ 1. Inspect page structure (e.g., via `get_content`) to verify that a
1824
+ CAPTCHA is present.
1825
+ 2. Call `solve_captcha` if there's a CAPTCHA. (No-op if no CAPTCHA.)
1826
+ 3. Verify success via `get_page_info`, `get_content`, or `manage_cookies`.
1819
1827
 
1820
- The tool does not guarantee that the CAPTCHA was solved. Some CAPTCHA
1821
- controls are embedded inside shadow DOM or otherwise do not expose an
1822
- easy success signal. A successful attempt may result in changes to page
1823
- state or browser cookies.
1828
+ Notes:
1829
+ This tool does not guarantee that the CAPTCHA gets solved.
1830
+ A successful attempt may result in changes to page state and cookies.
1824
1831
 
1825
- Tool workflow:
1826
- 1. Inspect the webpage with get_content when you need to
1827
- determine whether CAPTCHA-related controls are present.
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.
1832
+ Tool Selection:
1833
+ - Use `solve_captcha` specifically for automated CAPTCHA widget
1834
+ interactions.
1835
+ - Use `click_element` for standard visible elements.
1836
+ - Use `hover_action` (action="hover_and_click") to reveal and
1837
+ click targets.
1831
1838
 
1832
1839
  Returns:
1833
- A confirmation message of the CAPTCHA interaction.
1834
- The message is the same for both successful and failed attempts.
1835
-
1836
- Notes:
1837
- Clicking with the Chrome DevTools Protocol (CDP) is generally
1838
- stealthier than clicking with JavaScript because CDP actions
1839
- can avoid triggering `isTrusted: false`.
1840
+ A confirmation message indicating that the attempt was initiated.
1840
1841
  """
1841
1842
  sb = _get_sb()
1842
1843
  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.7
10
+ filelock>=4.0.0
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,8 +25,8 @@ 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
29
- idna>=3.19
28
+ pdbp>=1.8.3
29
+ idna>=3.20
30
30
  charset-normalizer>=3.5.1,<4
31
31
  urllib3>=2.8.0,<3
32
32
  requests~=2.34.2
@@ -49,7 +49,7 @@ pytest-html==4.0.2
49
49
  pytest-metadata==3.1.1
50
50
  pytest-ordering==0.6
51
51
  pytest-rerunfailures==16.3;python_version=="3.10"
52
- pytest-rerunfailures==16.6.1;python_version>="3.11"
52
+ pytest-rerunfailures==16.7;python_version>="3.11"
53
53
  pytest-xdist==3.8.0
54
54
  parameterized==0.9.0
55
55
  behave==1.2.6
@@ -1,2 +1,2 @@
1
1
  # seleniumbase package
2
- __version__ = "4.54.7"
2
+ __version__ = "4.54.9"
@@ -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"