seleniumbase 4.47.9__tar.gz → 4.48.0__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 (163) hide show
  1. {seleniumbase-4.47.9/seleniumbase.egg-info → seleniumbase-4.48.0}/PKG-INFO +7 -10
  2. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/README.md +0 -2
  3. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/requirements.txt +4 -5
  4. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/__version__.py +1 -1
  5. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/sb_mkfile.py +1 -1
  6. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/sb_mkrec.py +1 -1
  7. seleniumbase-4.48.0/seleniumbase/core/nest_asyncio.py +282 -0
  8. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/sb_cdp.py +71 -12
  9. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/plugins/sb_manager.py +1 -3
  10. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/cdp_driver/tab.py +48 -0
  11. {seleniumbase-4.47.9 → seleniumbase-4.48.0/seleniumbase.egg-info}/PKG-INFO +7 -10
  12. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase.egg-info/SOURCES.txt +1 -0
  13. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase.egg-info/requires.txt +6 -7
  14. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/setup.py +6 -7
  15. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/.gitignore +0 -0
  16. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/LICENSE +0 -0
  17. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/MANIFEST.in +0 -0
  18. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/install.sh +0 -0
  19. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/pyproject.toml +0 -0
  20. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/pytest.ini +0 -0
  21. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/sbase/__init__.py +0 -0
  22. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/sbase/__main__.py +0 -0
  23. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/sbase/steps.py +0 -0
  24. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/__init__.py +0 -0
  25. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/__main__.py +0 -0
  26. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/behave/__init__.py +0 -0
  27. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/behave/behave_helper.py +0 -0
  28. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/behave/behave_sb.py +0 -0
  29. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/behave/steps.py +0 -0
  30. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/common/__init__.py +0 -0
  31. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/common/decorators.py +0 -0
  32. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/common/encryption.py +0 -0
  33. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/common/exceptions.py +0 -0
  34. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/common/obfuscate.py +0 -0
  35. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/common/unobfuscate.py +0 -0
  36. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/config/__init__.py +0 -0
  37. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/config/ad_block_list.py +0 -0
  38. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/config/proxy_list.py +0 -0
  39. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/config/settings.py +0 -0
  40. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/__init__.py +0 -0
  41. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/logo_helper.py +0 -0
  42. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/rich_helper.py +0 -0
  43. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/run.py +0 -0
  44. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/sb_behave_gui.py +0 -0
  45. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/sb_caseplans.py +0 -0
  46. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/sb_commander.py +0 -0
  47. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/sb_install.py +0 -0
  48. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/sb_mkchart.py +0 -0
  49. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/sb_mkdir.py +0 -0
  50. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/sb_mkpres.py +0 -0
  51. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/sb_objectify.py +0 -0
  52. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/sb_print.py +0 -0
  53. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/console_scripts/sb_recorder.py +0 -0
  54. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/__init__.py +0 -0
  55. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/application_manager.py +0 -0
  56. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/browser_launcher.py +0 -0
  57. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/capabilities_parser.py +0 -0
  58. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/colored_traceback.py +0 -0
  59. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/create_db_tables.sql +0 -0
  60. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/detect_b_ver.py +0 -0
  61. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/download_helper.py +0 -0
  62. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/encoded_images.py +0 -0
  63. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/jqc_helper.py +0 -0
  64. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/log_helper.py +0 -0
  65. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/mysql.py +0 -0
  66. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/proxy_helper.py +0 -0
  67. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/recorder_helper.py +0 -0
  68. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/report_helper.py +0 -0
  69. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/s3_manager.py +0 -0
  70. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/sb_driver.py +0 -0
  71. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/session_helper.py +0 -0
  72. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/settings_parser.py +0 -0
  73. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/style_sheet.py +0 -0
  74. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/testcase_manager.py +0 -0
  75. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/tour_helper.py +0 -0
  76. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/core/visual_helper.py +0 -0
  77. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/drivers/__init__.py +0 -0
  78. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/drivers/atlas_drivers/__init__.py +0 -0
  79. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/drivers/brave_drivers/__init__.py +0 -0
  80. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/drivers/cft_drivers/__init__.py +0 -0
  81. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
  82. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/drivers/chs_drivers/__init__.py +0 -0
  83. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/drivers/comet_drivers/__init__.py +0 -0
  84. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/drivers/opera_drivers/__init__.py +0 -0
  85. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/extensions/__init__.py +0 -0
  86. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/extensions/ad_block.zip +0 -0
  87. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/extensions/disable_csp.zip +0 -0
  88. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/extensions/recorder.zip +0 -0
  89. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/extensions/sbase_ext.zip +0 -0
  90. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/fixtures/__init__.py +0 -0
  91. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/fixtures/base_case.py +0 -0
  92. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/fixtures/constants.py +0 -0
  93. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/fixtures/css_to_xpath.py +0 -0
  94. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/fixtures/errors.py +0 -0
  95. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/fixtures/js_utils.py +0 -0
  96. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/fixtures/page_actions.py +0 -0
  97. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/fixtures/page_utils.py +0 -0
  98. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/fixtures/shared_utils.py +0 -0
  99. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/fixtures/unittest_helper.py +0 -0
  100. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/fixtures/words.py +0 -0
  101. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/fixtures/xpath_to_css.py +0 -0
  102. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/js_code/__init__.py +0 -0
  103. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/js_code/active_css_js.py +0 -0
  104. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/js_code/live_js.py +0 -0
  105. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/js_code/recorder_js.py +0 -0
  106. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/masterqa/__init__.py +0 -0
  107. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/masterqa/master_qa.py +0 -0
  108. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/plugins/__init__.py +0 -0
  109. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/plugins/base_plugin.py +0 -0
  110. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/plugins/basic_test_info.py +0 -0
  111. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/plugins/db_reporting_plugin.py +0 -0
  112. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/plugins/driver_manager.py +0 -0
  113. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/plugins/page_source.py +0 -0
  114. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/plugins/pytest_plugin.py +0 -0
  115. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/plugins/s3_logging_plugin.py +0 -0
  116. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/plugins/screen_shots.py +0 -0
  117. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/plugins/selenium_plugin.py +0 -0
  118. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/resources/__init__.py +0 -0
  119. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/translate/__init__.py +0 -0
  120. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/translate/chinese.py +0 -0
  121. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/translate/dutch.py +0 -0
  122. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/translate/french.py +0 -0
  123. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/translate/italian.py +0 -0
  124. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/translate/japanese.py +0 -0
  125. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/translate/korean.py +0 -0
  126. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/translate/master_dict.py +0 -0
  127. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/translate/portuguese.py +0 -0
  128. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/translate/russian.py +0 -0
  129. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/translate/spanish.py +0 -0
  130. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/translate/translator.py +0 -0
  131. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/__init__.py +0 -0
  132. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/cdp.py +0 -0
  133. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/cdp_driver/__init__.py +0 -0
  134. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/cdp_driver/_contradict.py +0 -0
  135. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/cdp_driver/browser.py +0 -0
  136. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/cdp_driver/cdp_util.py +0 -0
  137. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/cdp_driver/config.py +0 -0
  138. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/cdp_driver/connection.py +0 -0
  139. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/cdp_driver/element.py +0 -0
  140. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/dprocess.py +0 -0
  141. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/options.py +0 -0
  142. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/patcher.py +0 -0
  143. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/reactor.py +0 -0
  144. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/undetected/webelement.py +0 -0
  145. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/__init__.py +0 -0
  146. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_grid/__init__.py +0 -0
  147. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_grid/download_selenium_server.py +0 -0
  148. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_grid/font_color +0 -0
  149. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_grid/grid-hub +0 -0
  150. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_grid/grid-node +0 -0
  151. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_grid/grid_hub.py +0 -0
  152. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_grid/grid_node.py +0 -0
  153. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_grid/register-grid-node.bat +0 -0
  154. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_grid/register-grid-node.sh +0 -0
  155. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_grid/start-grid-hub.bat +0 -0
  156. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_grid/start-grid-hub.sh +0 -0
  157. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_ide/__init__.py +0 -0
  158. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase/utilities/selenium_ide/convert_ide.py +0 -0
  159. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase.egg-info/dependency_links.txt +0 -0
  160. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase.egg-info/entry_points.txt +0 -0
  161. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/seleniumbase.egg-info/top_level.txt +0 -0
  162. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/setup.cfg +0 -0
  163. {seleniumbase-4.47.9 → seleniumbase-4.48.0}/virtualenv_install.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seleniumbase
3
- Version: 4.47.9
3
+ Version: 4.48.0
4
4
  Summary: A complete web automation framework for end-to-end testing.
5
5
  Home-page: https://github.com/seleniumbase/SeleniumBase
6
6
  Author: Michael Mintz
@@ -74,7 +74,7 @@ Requires-Dist: fasteners>=0.20
74
74
  Requires-Dist: mycdp>=1.3.7
75
75
  Requires-Dist: pynose>=1.5.5
76
76
  Requires-Dist: platformdirs~=4.4.0; python_version < "3.10"
77
- Requires-Dist: platformdirs>=4.9.4; python_version >= "3.10"
77
+ Requires-Dist: platformdirs>=4.9.6; python_version >= "3.10"
78
78
  Requires-Dist: typing-extensions>=4.15.0
79
79
  Requires-Dist: sbvirtualdisplay>=1.4.0
80
80
  Requires-Dist: MarkupSafe>=3.0.3
@@ -89,7 +89,7 @@ Requires-Dist: pyreadline3>=3.5.4; platform_system == "Windows"
89
89
  Requires-Dist: tabcompleter>=1.4.0
90
90
  Requires-Dist: pdbp>=1.8.2
91
91
  Requires-Dist: idna>=3.11
92
- Requires-Dist: charset-normalizer<4,>=3.4.6
92
+ Requires-Dist: charset-normalizer<4,>=3.4.7
93
93
  Requires-Dist: urllib3<2,>=1.26.20; python_version < "3.10"
94
94
  Requires-Dist: urllib3<3,>=1.26.20; python_version >= "3.10"
95
95
  Requires-Dist: requests~=2.32.5; python_version < "3.10"
@@ -104,10 +104,9 @@ Requires-Dist: wsproto==1.2.0; python_version < "3.10"
104
104
  Requires-Dist: wsproto~=1.3.2; python_version >= "3.10"
105
105
  Requires-Dist: websocket-client~=1.9.0
106
106
  Requires-Dist: selenium==4.32.0; python_version < "3.10"
107
- Requires-Dist: selenium==4.41.0; python_version >= "3.10"
107
+ Requires-Dist: selenium==4.43.0; python_version >= "3.10"
108
108
  Requires-Dist: cssselect==1.3.0; python_version < "3.10"
109
109
  Requires-Dist: cssselect<2,>=1.4.0; python_version >= "3.10"
110
- Requires-Dist: nest-asyncio==1.6.0
111
110
  Requires-Dist: sortedcontainers==2.4.0
112
111
  Requires-Dist: execnet==2.1.1; python_version < "3.10"
113
112
  Requires-Dist: execnet==2.1.2; python_version >= "3.10"
@@ -115,7 +114,7 @@ Requires-Dist: iniconfig==2.1.0; python_version < "3.10"
115
114
  Requires-Dist: iniconfig==2.3.0; python_version >= "3.10"
116
115
  Requires-Dist: pluggy==1.6.0
117
116
  Requires-Dist: pytest==8.4.2; python_version < "3.11"
118
- Requires-Dist: pytest==9.0.2; python_version >= "3.11"
117
+ Requires-Dist: pytest==9.0.3; python_version >= "3.11"
119
118
  Requires-Dist: pytest-html==4.0.2
120
119
  Requires-Dist: pytest-metadata==3.1.1
121
120
  Requires-Dist: pytest-ordering==0.6
@@ -154,13 +153,13 @@ Requires-Dist: mss==10.1.0; extra == "mss"
154
153
  Provides-Extra: pdfminer
155
154
  Requires-Dist: pdfminer.six==20251107; python_version < "3.10" and extra == "pdfminer"
156
155
  Requires-Dist: pdfminer.six==20260107; python_version >= "3.10" and extra == "pdfminer"
157
- Requires-Dist: cryptography==46.0.6; extra == "pdfminer"
156
+ Requires-Dist: cryptography==46.0.7; extra == "pdfminer"
158
157
  Requires-Dist: cffi==2.0.0; extra == "pdfminer"
159
158
  Requires-Dist: pycparser==2.23; python_version < "3.10" and extra == "pdfminer"
160
159
  Requires-Dist: pycparser==3.0; python_version >= "3.10" and extra == "pdfminer"
161
160
  Provides-Extra: pillow
162
161
  Requires-Dist: Pillow>=11.3.0; python_version < "3.10" and extra == "pillow"
163
- Requires-Dist: Pillow>=12.1.1; python_version >= "3.10" and extra == "pillow"
162
+ Requires-Dist: Pillow>=12.2.0; python_version >= "3.10" and extra == "pillow"
164
163
  Provides-Extra: pip-system-certs
165
164
  Requires-Dist: pip-system-certs==4.0; platform_system == "Windows" and extra == "pip-system-certs"
166
165
  Provides-Extra: proxy
@@ -818,8 +817,6 @@ import time; time.sleep(3) # Do nothing for 3 seconds.
818
817
  🔵 **Debug Mode** with Python's built-in **[pdb](https://docs.python.org/3/library/pdb.html)** library helps you debug tests:
819
818
 
820
819
  ```python
821
- import pdb; pdb.set_trace()
822
- import pytest; pytest.set_trace()
823
820
  breakpoint() # Shortcut for "import pdb; pdb.set_trace()"
824
821
  ```
825
822
 
@@ -617,8 +617,6 @@ import time; time.sleep(3) # Do nothing for 3 seconds.
617
617
  🔵 **Debug Mode** with Python's built-in **[pdb](https://docs.python.org/3/library/pdb.html)** library helps you debug tests:
618
618
 
619
619
  ```python
620
- import pdb; pdb.set_trace()
621
- import pytest; pytest.set_trace()
622
620
  breakpoint() # Shortcut for "import pdb; pdb.set_trace()"
623
621
  ```
624
622
 
@@ -14,7 +14,7 @@ fasteners>=0.20
14
14
  mycdp>=1.3.7
15
15
  pynose>=1.5.5
16
16
  platformdirs~=4.4.0;python_version<"3.10"
17
- platformdirs>=4.9.4;python_version>="3.10"
17
+ platformdirs>=4.9.6;python_version>="3.10"
18
18
  typing-extensions>=4.15.0
19
19
  sbvirtualdisplay>=1.4.0
20
20
  MarkupSafe>=3.0.3
@@ -29,7 +29,7 @@ pyreadline3>=3.5.4;platform_system=="Windows"
29
29
  tabcompleter>=1.4.0
30
30
  pdbp>=1.8.2
31
31
  idna>=3.11
32
- charset-normalizer>=3.4.6,<4
32
+ charset-normalizer>=3.4.7,<4
33
33
  urllib3>=1.26.20,<2;python_version<"3.10"
34
34
  urllib3>=1.26.20,<3;python_version>="3.10"
35
35
  requests~=2.32.5;python_version<"3.10"
@@ -44,10 +44,9 @@ wsproto==1.2.0;python_version<"3.10"
44
44
  wsproto~=1.3.2;python_version>="3.10"
45
45
  websocket-client~=1.9.0
46
46
  selenium==4.32.0;python_version<"3.10"
47
- selenium==4.41.0;python_version>="3.10"
47
+ selenium==4.43.0;python_version>="3.10"
48
48
  cssselect==1.3.0;python_version<"3.10"
49
49
  cssselect>=1.4.0,<2;python_version>="3.10"
50
- nest-asyncio==1.6.0
51
50
  sortedcontainers==2.4.0
52
51
  execnet==2.1.1;python_version<"3.10"
53
52
  execnet==2.1.2;python_version>="3.10"
@@ -55,7 +54,7 @@ iniconfig==2.1.0;python_version<"3.10"
55
54
  iniconfig==2.3.0;python_version>="3.10"
56
55
  pluggy==1.6.0
57
56
  pytest==8.4.2;python_version<"3.11"
58
- pytest==9.0.2;python_version>="3.11"
57
+ pytest==9.0.3;python_version>="3.11"
59
58
  pytest-html==4.0.2
60
59
  pytest-metadata==3.1.1
61
60
  pytest-ordering==0.6
@@ -1,2 +1,2 @@
1
1
  # seleniumbase package
2
- __version__ = "4.47.9"
2
+ __version__ = "4.48.0"
@@ -277,7 +277,7 @@ def main():
277
277
  data.append(' self.open("%s")' % url)
278
278
  else:
279
279
  data.append(" if self.recorder_ext and not self.xvfb:")
280
- data.append(" import pdb; pdb.set_trace()")
280
+ data.append(" breakpoint()")
281
281
  if not basic and not recorder:
282
282
  data.append(
283
283
  ' self.type("input", "%s")' " # selector, text" % goodbye
@@ -216,7 +216,7 @@ def main():
216
216
  data.append(" if self.recorder_ext:")
217
217
  data.append(" # When done recording actions,")
218
218
  data.append(' # type "c", and press [Enter].')
219
- data.append(" import pdb; pdb.set_trace()")
219
+ data.append(" breakpoint()")
220
220
  data.append("")
221
221
 
222
222
  if esc_end:
@@ -0,0 +1,282 @@
1
+ """Patch asyncio to allow nested event loops."""
2
+ import asyncio
3
+ import asyncio.events as events
4
+ import os
5
+ import sys
6
+ import threading
7
+ from contextlib import contextmanager, suppress
8
+ from heapq import heappop
9
+
10
+ _run_close_loop = True
11
+
12
+
13
+ class PatchedNestAsyncio:
14
+ pass
15
+
16
+
17
+ def apply(loop=None, *, run_close_loop=False, error_on_mispatched=False):
18
+ global _run_close_loop
19
+ _patch_asyncio(error_on_mispatched=error_on_mispatched)
20
+ _patch_policy()
21
+ _patch_tornado()
22
+ loop = loop or _get_event_loop()
23
+ if loop is not None:
24
+ _patch_loop(loop)
25
+ _run_close_loop &= run_close_loop
26
+
27
+
28
+ if sys.version_info < (3, 14, 0):
29
+ def _get_event_loop():
30
+ return asyncio.get_event_loop()
31
+ else:
32
+ def _get_event_loop():
33
+ try:
34
+ return asyncio.get_event_loop()
35
+ except RuntimeError:
36
+ return None
37
+
38
+
39
+ if sys.version_info < (3, 12, 0):
40
+ def run(main, *, debug=False):
41
+ loop = asyncio.get_event_loop()
42
+ loop.set_debug(debug)
43
+ task = asyncio.ensure_future(main)
44
+ try:
45
+ return loop.run_until_complete(task)
46
+ finally:
47
+ if not task.done():
48
+ task.cancel()
49
+ with suppress(asyncio.CancelledError):
50
+ loop.run_until_complete(task)
51
+ else:
52
+ def run(main, *, debug=False, loop_factory=None):
53
+ new_event_loop = False
54
+ set_event_loop = None
55
+ try:
56
+ loop = asyncio.get_running_loop()
57
+ except RuntimeError:
58
+ if not _run_close_loop:
59
+ loop = _get_event_loop()
60
+ if loop is None:
61
+ if loop_factory is None:
62
+ loop_factory = asyncio.new_event_loop
63
+ loop = loop_factory()
64
+ asyncio.set_event_loop(loop)
65
+ else:
66
+ if loop_factory is None:
67
+ loop = asyncio.new_event_loop()
68
+ set_event_loop = _get_event_loop()
69
+ asyncio.set_event_loop(loop)
70
+ else:
71
+ loop = loop_factory()
72
+ new_event_loop = True
73
+ _patch_loop(loop)
74
+
75
+ loop.set_debug(debug)
76
+ task = asyncio.ensure_future(main, loop=loop)
77
+ try:
78
+ return loop.run_until_complete(task)
79
+ finally:
80
+ if not task.done():
81
+ task.cancel()
82
+ with suppress(asyncio.CancelledError):
83
+ loop.run_until_complete(task)
84
+ if set_event_loop:
85
+ asyncio.set_event_loop(set_event_loop)
86
+ if new_event_loop:
87
+ # Avoid ResourceWarning: unclosed event loop
88
+ loop.close()
89
+
90
+
91
+ def _patch_asyncio(*, error_on_mispatched=False):
92
+ """Patch asyncio module to use pure Python tasks and futures."""
93
+
94
+ def _get_event_loop(stacklevel=3):
95
+ loop = events._get_running_loop()
96
+ if loop is None:
97
+ if sys.version_info < (3, 14, 0):
98
+ policy = events.get_event_loop_policy()
99
+ else:
100
+ policy = events._get_event_loop_policy()
101
+ loop = policy.get_event_loop()
102
+ return loop
103
+
104
+ if hasattr(asyncio, "_nest_patched"):
105
+ if not hasattr(asyncio, "_nest_asyncio"):
106
+ if error_on_mispatched:
107
+ raise RuntimeError("asyncio was already patched!")
108
+ elif sys.version_info >= (3, 12, 0):
109
+ import warnings
110
+ warnings.warn("asyncio was already patched!")
111
+ return
112
+
113
+ asyncio.tasks.Task = asyncio.tasks._PyTask
114
+ asyncio.Task = asyncio.tasks._CTask = asyncio.tasks.Task
115
+ asyncio.Future = asyncio.futures._CFuture = asyncio.futures.Future = (
116
+ asyncio.futures._PyFuture
117
+ )
118
+ asyncio.get_event_loop = _get_event_loop
119
+ events._get_event_loop = events.get_event_loop = asyncio.get_event_loop
120
+ asyncio.run = run
121
+ asyncio._nest_patched = True
122
+ asyncio._nest_asyncio = PatchedNestAsyncio()
123
+
124
+
125
+ def _patch_policy():
126
+ """Patch the policy to always return a patched loop."""
127
+
128
+ def get_event_loop(self):
129
+ if self._local._loop is None:
130
+ loop = self.new_event_loop()
131
+ _patch_loop(loop)
132
+ self.set_event_loop(loop)
133
+ return self._local._loop
134
+
135
+ if sys.version_info < (3, 14, 0):
136
+ policy = events.get_event_loop_policy()
137
+ else:
138
+ policy = events._get_event_loop_policy()
139
+ policy.__class__.get_event_loop = get_event_loop
140
+
141
+
142
+ def _patch_loop(loop):
143
+ """Patch loop to make it reentrant."""
144
+
145
+ def run_forever(self):
146
+ with manage_run(self), manage_asyncgens(self):
147
+ while True:
148
+ self._run_once()
149
+ if self._stopping:
150
+ break
151
+ self._stopping = False
152
+
153
+ def run_until_complete(self, future):
154
+ with manage_run(self):
155
+ f = asyncio.ensure_future(future, loop=self)
156
+ if f is not future:
157
+ f._log_destroy_pending = False
158
+ while not f.done():
159
+ self._run_once()
160
+ if self._stopping:
161
+ break
162
+ if not f.done():
163
+ raise RuntimeError("Loop stopped before Future completed!")
164
+ return f.result()
165
+
166
+ def _run_once(self):
167
+ """Simplified re-implementation of asyncio's _run_once."""
168
+ ready = self._ready
169
+ scheduled = self._scheduled
170
+ while scheduled and scheduled[0]._cancelled:
171
+ heappop(scheduled)
172
+ timeout = (
173
+ 0
174
+ if ready or self._stopping
175
+ else min(max(scheduled[0]._when - self.time(), 0), 86400)
176
+ if scheduled
177
+ else None
178
+ )
179
+ event_list = self._selector.select(timeout)
180
+ self._process_events(event_list)
181
+ end_time = self.time() + self._clock_resolution
182
+ while scheduled and scheduled[0]._when < end_time:
183
+ handle = heappop(scheduled)
184
+ ready.append(handle)
185
+ for _ in range(len(ready)):
186
+ if not ready:
187
+ break
188
+ handle = ready.popleft()
189
+ if not handle._cancelled:
190
+ if sys.version_info < (3, 14, 0):
191
+ curr_task = curr_tasks.pop(self, None)
192
+ else:
193
+ try:
194
+ curr_task = asyncio.tasks._swap_current_task(
195
+ self, None
196
+ )
197
+ except KeyError:
198
+ curr_task = None
199
+ try:
200
+ handle._run()
201
+ finally:
202
+ if curr_task is not None:
203
+ if sys.version_info < (3, 14, 0):
204
+ curr_tasks[self] = curr_task
205
+ else:
206
+ asyncio.tasks._swap_current_task(self, curr_task)
207
+ handle = None
208
+
209
+ @contextmanager
210
+ def manage_run(self):
211
+ self._check_closed()
212
+ old_thread_id = self._thread_id
213
+ old_running_loop = events._get_running_loop()
214
+ try:
215
+ self._thread_id = threading.get_ident()
216
+ events._set_running_loop(self)
217
+ self._num_runs_pending += 1
218
+ if self._is_proactorloop:
219
+ if self._self_reading_future is None:
220
+ self.call_soon(self._loop_self_reading)
221
+ yield
222
+ finally:
223
+ self._thread_id = old_thread_id
224
+ events._set_running_loop(old_running_loop)
225
+ self._num_runs_pending -= 1
226
+ if self._is_proactorloop:
227
+ if (
228
+ self._num_runs_pending == 0
229
+ and self._self_reading_future is not None
230
+ ):
231
+ ov = self._self_reading_future._ov
232
+ self._self_reading_future.cancel()
233
+ if ov is not None:
234
+ self._proactor._unregister(ov)
235
+ self._self_reading_future = None
236
+
237
+ @contextmanager
238
+ def manage_asyncgens(self):
239
+ old_agen_hooks = sys.get_asyncgen_hooks()
240
+ try:
241
+ self._set_coroutine_origin_tracking(self._debug)
242
+ if self._asyncgens is not None:
243
+ sys.set_asyncgen_hooks(
244
+ firstiter=self._asyncgen_firstiter_hook,
245
+ finalizer=self._asyncgen_finalizer_hook,
246
+ )
247
+ yield
248
+ finally:
249
+ self._set_coroutine_origin_tracking(False)
250
+ if self._asyncgens is not None:
251
+ sys.set_asyncgen_hooks(*old_agen_hooks)
252
+
253
+ def _check_running(self):
254
+ """Do not throw exception if loop is already running."""
255
+ pass
256
+
257
+ if hasattr(loop, "_nest_patched"):
258
+ return
259
+ if not isinstance(loop, asyncio.BaseEventLoop):
260
+ raise ValueError("Can't patch loop of type %s" % type(loop))
261
+ cls = loop.__class__
262
+ cls.run_forever = run_forever
263
+ cls.run_until_complete = run_until_complete
264
+ cls._run_once = _run_once
265
+ cls._check_running = _check_running
266
+ cls._num_runs_pending = 1 if loop.is_running() else 0
267
+ cls._is_proactorloop = os.name == "nt" and issubclass(
268
+ cls, asyncio.ProactorEventLoop
269
+ )
270
+ curr_tasks = asyncio.tasks._current_tasks
271
+ cls._nest_patched = True
272
+ cls._nest_asyncio = PatchedNestAsyncio()
273
+
274
+
275
+ def _patch_tornado():
276
+ """If tornado is imported before nest_asyncio,
277
+ make tornado aware of the pure-Python asyncio Future."""
278
+ if "tornado" in sys.modules:
279
+ import tornado.concurrent as tc # type: ignore
280
+ tc.Future = asyncio.Future
281
+ if asyncio.Future not in tc.FUTURES:
282
+ tc.FUTURES += (asyncio.Future,)
@@ -193,7 +193,7 @@ class CDPMethods():
193
193
  Also sets an environment variable to hide this warning:
194
194
  Deprecation: "url.parse() behavior is not standardized".
195
195
  (github.com/microsoft/playwright-python/issues/3016)"""
196
- import nest_asyncio
196
+ from seleniumbase.core import nest_asyncio
197
197
  nest_asyncio.apply()
198
198
  os.environ["NODE_NO_WARNINGS"] = "1"
199
199
  driver = self.driver
@@ -400,7 +400,7 @@ class CDPMethods():
400
400
  self.__add_light_pause()
401
401
  selector = self.__convert_to_css_if_xpath(selector)
402
402
  if not self.is_element_present(selector):
403
- self.sleep(1)
403
+ time.sleep(1)
404
404
  timeout = timeout - 1
405
405
  if timeout < 1:
406
406
  timeout = 1
@@ -2061,7 +2061,7 @@ class CDPMethods():
2061
2061
  return False
2062
2062
 
2063
2063
  def _on_a_datadome_slider_page(self, *args, **kwargs):
2064
- self.loop.run_until_complete(self.page.wait(0.1))
2064
+ self.loop.run_until_complete(self.page.wait(0.05))
2065
2065
  if (
2066
2066
  self.is_element_visible(
2067
2067
  'body > iframe[src*="/geo.captcha-delivery.com/captcha/"]'
@@ -2070,6 +2070,12 @@ class CDPMethods():
2070
2070
  return True
2071
2071
  return False
2072
2072
 
2073
+ def _on_a_friendly_captcha_page(self, *args, **kwargs):
2074
+ self.loop.run_until_complete(self.page.wait(0.05))
2075
+ if self.is_element_visible('iframe[data--frc-frame-id]'):
2076
+ return True
2077
+ return False
2078
+
2073
2079
  def _on_a_g_recaptcha_page(self, *args, **kwargs):
2074
2080
  time.sleep(0.4) # reCAPTCHA may need a moment to appear
2075
2081
  self.loop.run_until_complete(self.page.wait(0.1))
@@ -2131,7 +2137,7 @@ class CDPMethods():
2131
2137
  sb_config._saved_cf_x_y = (x, y)
2132
2138
  time.sleep(0.08)
2133
2139
  if use_cdp:
2134
- self.sleep(0.03)
2140
+ time.sleep(0.03)
2135
2141
  gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
2136
2142
  with gui_lock: # Prevent issues with multiple processes
2137
2143
  self.bring_active_window_to_front()
@@ -2163,7 +2169,7 @@ class CDPMethods():
2163
2169
  time.sleep(0.15)
2164
2170
  return True
2165
2171
 
2166
- def __cdp_click_incapsula_hcaptcha(self):
2172
+ def __cdp_click_incapsula_hcaptcha(self, use_cdp=True):
2167
2173
  selector = "iframe[data-hcaptcha-widget-id]"
2168
2174
  if self.is_element_visible('iframe[src*="_Incapsula_Resource?"]'):
2169
2175
  outer_selector = 'iframe[src*="_Incapsula_Resource?"]'
@@ -2212,6 +2218,56 @@ class CDPMethods():
2212
2218
  print(" <DEBUG> hCaptcha was NOT clicked!")
2213
2219
  return False
2214
2220
 
2221
+ def __gui_click_friendly_captcha(self, use_cdp=False):
2222
+ selector = 'iframe[data--frc-frame-id]'
2223
+ if self.is_element_visible('iframe[data--frc-frame-id]'):
2224
+ element = self.select('iframe[data--frc-frame-id]')
2225
+ else:
2226
+ return False
2227
+ time.sleep(0.05)
2228
+ self.loop.run_until_complete(self.page.wait(0.1))
2229
+ time.sleep(0.05)
2230
+ with suppress(Exception):
2231
+ element_rect = self.get_element_rect(selector, timeout=0.1)
2232
+ e_x = element_rect["x"]
2233
+ e_y = element_rect["y"]
2234
+ window_rect = self.get_window_rect()
2235
+ win_width = window_rect["innerWidth"]
2236
+ win_height = window_rect["innerHeight"]
2237
+ if (
2238
+ e_x > 1040
2239
+ and e_y > 640
2240
+ and abs(win_width - e_x) < 110
2241
+ and abs(win_height - e_y) < 110
2242
+ ):
2243
+ return False
2244
+ gui_element_rect = self.get_gui_element_rect(selector, timeout=1)
2245
+ gui_e_x = gui_element_rect["x"]
2246
+ gui_e_y = gui_element_rect["y"]
2247
+ x_offset = 27
2248
+ y_offset = 34
2249
+ x = gui_e_x + x_offset
2250
+ y = gui_e_y + y_offset
2251
+ sb_config._saved_cf_x_y = (x, y)
2252
+ time.sleep(0.08)
2253
+ if use_cdp:
2254
+ time.sleep(0.03)
2255
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
2256
+ with gui_lock: # Prevent issues with multiple processes
2257
+ self.bring_active_window_to_front()
2258
+ time.sleep(0.06)
2259
+ element.mouse_move()
2260
+ time.sleep(0.08)
2261
+ self.click_with_offset(selector, x_offset, y_offset)
2262
+ time.sleep(0.08)
2263
+ element.mouse_move()
2264
+ time.sleep(0.25)
2265
+ return True
2266
+ else:
2267
+ self.gui_click_x_y(x, y)
2268
+ return True
2269
+ return False
2270
+
2215
2271
  def solve_captcha(self):
2216
2272
  self.__click_captcha(use_cdp=True)
2217
2273
 
@@ -2225,9 +2281,9 @@ class CDPMethods():
2225
2281
 
2226
2282
  def __click_captcha(self, use_cdp=False):
2227
2283
  """Uses PyAutoGUI unless use_cdp == True"""
2228
- self.sleep(0.075)
2284
+ time.sleep(0.075)
2229
2285
  self.loop.run_until_complete(self.page.wait(0.1))
2230
- self.sleep(0.025)
2286
+ time.sleep(0.025)
2231
2287
  source = self.get_page_source()
2232
2288
  if self._on_a_cf_turnstile_page(source):
2233
2289
  pass
@@ -2240,6 +2296,9 @@ class CDPMethods():
2240
2296
  elif self._on_a_datadome_slider_page():
2241
2297
  result = self.__gui_slide_datadome_captcha()
2242
2298
  return result
2299
+ elif self._on_a_friendly_captcha_page():
2300
+ result = self.__gui_click_friendly_captcha(use_cdp)
2301
+ return result
2243
2302
  else:
2244
2303
  return False
2245
2304
  selector = None
@@ -2395,7 +2454,7 @@ class CDPMethods():
2395
2454
  if hasattr(sb_config, "_cdp_proxy") and sb_config._cdp_proxy:
2396
2455
  time.sleep(0.22) # CAPTCHA may load slower with proxy
2397
2456
  if use_cdp:
2398
- self.sleep(0.03)
2457
+ time.sleep(0.03)
2399
2458
  gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
2400
2459
  with gui_lock: # Prevent issues with multiple processes
2401
2460
  self.bring_active_window_to_front()
@@ -2595,9 +2654,9 @@ class CDPMethods():
2595
2654
  gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
2596
2655
  with gui_lock:
2597
2656
  self.bring_active_window_to_front()
2598
- self.sleep(0.02)
2657
+ time.sleep(0.02)
2599
2658
  element.mouse_move()
2600
- self.sleep(timeframe)
2659
+ time.sleep(timeframe)
2601
2660
 
2602
2661
  def hover_and_click(self, hover_selector, click_selector):
2603
2662
  if getattr(sb_config, "_cdp_mobile_mode", None):
@@ -2607,9 +2666,9 @@ class CDPMethods():
2607
2666
  gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
2608
2667
  with gui_lock:
2609
2668
  self.bring_active_window_to_front()
2610
- self.sleep(0.02)
2669
+ time.sleep(0.02)
2611
2670
  hover_element.mouse_move()
2612
- self.sleep(0.25)
2671
+ time.sleep(0.25)
2613
2672
  try:
2614
2673
  self.click(click_selector, timeout=0.5)
2615
2674
  except Exception:
@@ -1408,9 +1408,7 @@ def SB(
1408
1408
  test_passed = True # This can change later
1409
1409
  teardown_exception = None
1410
1410
  if "--trace" in sys_argv:
1411
- import pdb
1412
-
1413
- pdb.set_trace() # Debug Mode
1411
+ breakpoint() # Debug Mode
1414
1412
  # Type "s" and press [Enter] to step into "yield sb".
1415
1413
  try:
1416
1414
  yield sb
@@ -1406,6 +1406,11 @@ class Tab(Connection):
1406
1406
  return True
1407
1407
  return False
1408
1408
 
1409
+ async def __on_a_friendly_captcha_page(self, *args, **kwargs):
1410
+ if await self.is_element_visible('iframe[data--frc-frame-id]'):
1411
+ return True
1412
+ return False
1413
+
1409
1414
  async def __on_a_g_recaptcha_page(self, *args, **kwargs):
1410
1415
  await self.sleep(0.4) # reCAPTCHA may need a moment to appear
1411
1416
  source = await self.get_html()
@@ -1514,6 +1519,46 @@ class Tab(Connection):
1514
1519
  print(" <DEBUG> hCaptcha was NOT clicked!")
1515
1520
  return False
1516
1521
 
1522
+ async def __gui_click_friendly_captcha(self):
1523
+ selector = 'iframe[data--frc-frame-id]'
1524
+ if await self.is_element_visible('iframe[data--frc-frame-id]'):
1525
+ element = await self.find_element_by_text(selector)
1526
+ else:
1527
+ return False
1528
+ await self.sleep(0.55)
1529
+ x_offset = 27
1530
+ y_offset = 34
1531
+ was_clicked = False
1532
+ gui_lock = AsyncFileLock(constants.MultiBrowser.PYAUTOGUILOCK)
1533
+ async with gui_lock:
1534
+ await self.bring_to_front()
1535
+ await self.sleep(0.056)
1536
+ if "--debug" in sys.argv:
1537
+ displayed_selector = "`%s`" % selector
1538
+ if '"' not in selector:
1539
+ displayed_selector = '"%s"' % selector
1540
+ elif "'" not in selector:
1541
+ displayed_selector = "'%s'" % selector
1542
+ print(
1543
+ " <DEBUG> click_with_offset(%s, %s, %s)"
1544
+ % (displayed_selector, x_offset, y_offset)
1545
+ )
1546
+ with suppress(Exception):
1547
+ await element.mouse_click_with_offset_async(
1548
+ x=x_offset, y=y_offset, center=False
1549
+ )
1550
+ was_clicked = True
1551
+ await self.sleep(0.075)
1552
+ if was_clicked:
1553
+ # Wait a moment for the click to succeed
1554
+ await self.sleep(0.75)
1555
+ if "--debug" in sys.argv:
1556
+ print(" <DEBUG> Friendly Captcha was clicked!")
1557
+ return True
1558
+ if "--debug" in sys.argv:
1559
+ print(" <DEBUG> Friendly Captcha was NOT clicked!")
1560
+ return False
1561
+
1517
1562
  async def get_element_rect(self, selector, timeout=5):
1518
1563
  element = await self.select(selector, timeout=timeout)
1519
1564
  coordinates = None
@@ -1649,6 +1694,9 @@ class Tab(Connection):
1649
1694
  elif await self.__on_an_incapsula_hcaptcha_page():
1650
1695
  result = await self.__cdp_click_incapsula_hcaptcha()
1651
1696
  return result
1697
+ elif await self.__on_a_friendly_captcha_page():
1698
+ result = await self.__gui_click_friendly_captcha()
1699
+ return result
1652
1700
  else:
1653
1701
  return False
1654
1702
  selector = None