seleniumbase 4.41.3__py3-none-any.whl → 4.45.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. sbase/steps.py +9 -0
  2. seleniumbase/__version__.py +1 -1
  3. seleniumbase/behave/behave_helper.py +2 -0
  4. seleniumbase/behave/behave_sb.py +21 -8
  5. seleniumbase/common/decorators.py +3 -1
  6. seleniumbase/console_scripts/run.py +1 -0
  7. seleniumbase/console_scripts/sb_caseplans.py +3 -4
  8. seleniumbase/console_scripts/sb_install.py +142 -11
  9. seleniumbase/console_scripts/sb_mkchart.py +1 -2
  10. seleniumbase/console_scripts/sb_mkdir.py +99 -29
  11. seleniumbase/console_scripts/sb_mkfile.py +1 -2
  12. seleniumbase/console_scripts/sb_mkpres.py +1 -2
  13. seleniumbase/console_scripts/sb_mkrec.py +26 -2
  14. seleniumbase/console_scripts/sb_objectify.py +4 -5
  15. seleniumbase/console_scripts/sb_print.py +1 -1
  16. seleniumbase/console_scripts/sb_recorder.py +40 -3
  17. seleniumbase/core/browser_launcher.py +474 -151
  18. seleniumbase/core/detect_b_ver.py +258 -16
  19. seleniumbase/core/log_helper.py +15 -21
  20. seleniumbase/core/mysql.py +1 -1
  21. seleniumbase/core/recorder_helper.py +3 -0
  22. seleniumbase/core/report_helper.py +9 -12
  23. seleniumbase/core/sb_cdp.py +734 -215
  24. seleniumbase/core/sb_driver.py +46 -5
  25. seleniumbase/core/session_helper.py +2 -4
  26. seleniumbase/core/tour_helper.py +1 -2
  27. seleniumbase/drivers/atlas_drivers/__init__.py +0 -0
  28. seleniumbase/drivers/brave_drivers/__init__.py +0 -0
  29. seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
  30. seleniumbase/drivers/comet_drivers/__init__.py +0 -0
  31. seleniumbase/drivers/opera_drivers/__init__.py +0 -0
  32. seleniumbase/fixtures/base_case.py +448 -251
  33. seleniumbase/fixtures/constants.py +36 -9
  34. seleniumbase/fixtures/js_utils.py +77 -18
  35. seleniumbase/fixtures/page_actions.py +41 -13
  36. seleniumbase/fixtures/page_utils.py +19 -12
  37. seleniumbase/fixtures/shared_utils.py +64 -6
  38. seleniumbase/masterqa/master_qa.py +16 -2
  39. seleniumbase/plugins/base_plugin.py +8 -0
  40. seleniumbase/plugins/basic_test_info.py +2 -3
  41. seleniumbase/plugins/driver_manager.py +131 -5
  42. seleniumbase/plugins/page_source.py +2 -3
  43. seleniumbase/plugins/pytest_plugin.py +244 -79
  44. seleniumbase/plugins/sb_manager.py +143 -20
  45. seleniumbase/plugins/selenium_plugin.py +144 -12
  46. seleniumbase/translate/translator.py +2 -3
  47. seleniumbase/undetected/__init__.py +17 -13
  48. seleniumbase/undetected/cdp.py +1 -12
  49. seleniumbase/undetected/cdp_driver/browser.py +330 -129
  50. seleniumbase/undetected/cdp_driver/cdp_util.py +328 -61
  51. seleniumbase/undetected/cdp_driver/config.py +110 -14
  52. seleniumbase/undetected/cdp_driver/connection.py +18 -48
  53. seleniumbase/undetected/cdp_driver/element.py +105 -33
  54. seleniumbase/undetected/cdp_driver/tab.py +414 -39
  55. seleniumbase/utilities/selenium_grid/download_selenium_server.py +1 -1
  56. seleniumbase/utilities/selenium_grid/grid_hub.py +1 -2
  57. seleniumbase/utilities/selenium_grid/grid_node.py +2 -3
  58. seleniumbase/utilities/selenium_ide/convert_ide.py +2 -3
  59. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/METADATA +193 -166
  60. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/RECORD +64 -59
  61. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/licenses/LICENSE +1 -1
  62. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/WHEEL +0 -0
  63. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/entry_points.txt +0 -0
  64. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/top_level.txt +0 -0
@@ -7,6 +7,7 @@ import time
7
7
  from contextlib import suppress
8
8
  from seleniumbase import config as sb_config
9
9
  from seleniumbase.config import settings
10
+ from seleniumbase.core import detect_b_ver
10
11
  from seleniumbase.core import log_helper
11
12
  from seleniumbase.fixtures import constants
12
13
  from seleniumbase.fixtures import shared_utils
@@ -28,6 +29,11 @@ def pytest_addoption(parser):
28
29
  --edge (Shortcut for "--browser=edge".)
29
30
  --firefox (Shortcut for "--browser=firefox".)
30
31
  --safari (Shortcut for "--browser=safari".)
32
+ --opera (Shortcut for "--browser=opera".)
33
+ --brave (Shortcut for "--browser=brave".)
34
+ --comet (Shortcut for "--browser=comet".)
35
+ --atlas (Shortcut for "--browser=atlas".)
36
+ --use-chromium (Shortcut for using base `Chromium`)
31
37
  --cft (Shortcut for using `Chrome for Testing`)
32
38
  --chs (Shortcut for using `Chrome-Headless-Shell`)
33
39
  --settings-file=FILE (Override default SeleniumBase settings.)
@@ -186,6 +192,41 @@ def pytest_addoption(parser):
186
192
  default=False,
187
193
  help="""Shortcut for --browser=safari""",
188
194
  )
195
+ parser.addoption(
196
+ "--opera",
197
+ action="store_true",
198
+ dest="use_opera",
199
+ default=False,
200
+ help="""Shortcut for --browser=opera""",
201
+ )
202
+ parser.addoption(
203
+ "--brave",
204
+ action="store_true",
205
+ dest="use_brave",
206
+ default=False,
207
+ help="""Shortcut for --browser=brave""",
208
+ )
209
+ parser.addoption(
210
+ "--comet",
211
+ action="store_true",
212
+ dest="use_comet",
213
+ default=False,
214
+ help="""Shortcut for --browser=comet""",
215
+ )
216
+ parser.addoption(
217
+ "--atlas",
218
+ action="store_true",
219
+ dest="use_atlas",
220
+ default=False,
221
+ help="""Shortcut for --browser=atlas""",
222
+ )
223
+ parser.addoption(
224
+ "--use-chromium",
225
+ action="store_true",
226
+ dest="use_chromium",
227
+ default=False,
228
+ help="""Shortcut for using base `Chromium`""",
229
+ )
189
230
  parser.addoption(
190
231
  "--cft",
191
232
  action="store_true",
@@ -592,7 +633,7 @@ def pytest_addoption(parser):
592
633
  help="""Designates the three device metrics of the mobile
593
634
  emulator: CSS Width, CSS Height, and Pixel-Ratio.
594
635
  Format: A comma-separated string with the 3 values.
595
- Examples: "375,734,5" or "411,731,3" or "390,715,3"
636
+ Examples: "375,734,5" or "412,732,3" or "390,715,3"
596
637
  Default: None. (Will use default values if None)""",
597
638
  )
598
639
  parser.addoption(
@@ -1388,8 +1429,14 @@ def pytest_addoption(parser):
1388
1429
 
1389
1430
  arg_join = " ".join(sys_argv)
1390
1431
  sb_config._browser_shortcut = None
1432
+ sb_config._cdp_browser = None
1433
+ sb_config._cdp_bin_loc = None
1391
1434
  sb_config._vd_list = []
1392
-
1435
+ # Check if binary-location in options
1436
+ bin_loc_in_options = False
1437
+ for arg in sys_argv:
1438
+ if arg in ["--binary-location", "--binary_location", "--bl"]:
1439
+ bin_loc_in_options = True
1393
1440
  # SeleniumBase does not support pytest-timeout due to hanging browsers.
1394
1441
  for arg in sys_argv:
1395
1442
  if "--timeout=" in arg:
@@ -1465,6 +1512,46 @@ def pytest_addoption(parser):
1465
1512
  browser_changes += 1
1466
1513
  browser_set = "remote"
1467
1514
  browser_list.append("--browser=remote")
1515
+ if "--browser=opera" in sys_argv or "--browser opera" in sys_argv:
1516
+ if not bin_loc_in_options:
1517
+ bin_loc = detect_b_ver.get_binary_location("opera")
1518
+ if os.path.exists(bin_loc):
1519
+ browser_changes += 1
1520
+ browser_set = "opera"
1521
+ sb_config._browser_shortcut = "opera"
1522
+ sb_config._cdp_browser = "opera"
1523
+ sb_config._cdp_bin_loc = bin_loc
1524
+ browser_list.append("--browser=opera")
1525
+ if "--browser=brave" in sys_argv or "--browser brave" in sys_argv:
1526
+ if not bin_loc_in_options:
1527
+ bin_loc = detect_b_ver.get_binary_location("brave")
1528
+ if os.path.exists(bin_loc):
1529
+ browser_changes += 1
1530
+ browser_set = "brave"
1531
+ sb_config._browser_shortcut = "brave"
1532
+ sb_config._cdp_browser = "brave"
1533
+ sb_config._cdp_bin_loc = bin_loc
1534
+ browser_list.append("--browser=brave")
1535
+ if "--browser=comet" in sys_argv or "--browser comet" in sys_argv:
1536
+ if not bin_loc_in_options:
1537
+ bin_loc = detect_b_ver.get_binary_location("comet")
1538
+ if os.path.exists(bin_loc):
1539
+ browser_changes += 1
1540
+ browser_set = "comet"
1541
+ sb_config._browser_shortcut = "comet"
1542
+ sb_config._cdp_browser = "comet"
1543
+ sb_config._cdp_bin_loc = bin_loc
1544
+ browser_list.append("--browser=comet")
1545
+ if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv:
1546
+ if not bin_loc_in_options:
1547
+ bin_loc = detect_b_ver.get_binary_location("atlas")
1548
+ if os.path.exists(bin_loc):
1549
+ browser_changes += 1
1550
+ browser_set = "atlas"
1551
+ sb_config._browser_shortcut = "atlas"
1552
+ sb_config._cdp_browser = "atlas"
1553
+ sb_config._cdp_bin_loc = bin_loc
1554
+ browser_list.append("--browser=atlas")
1468
1555
  browser_text = browser_set
1469
1556
  if "--chrome" in sys_argv and not browser_set == "chrome":
1470
1557
  browser_changes += 1
@@ -1491,6 +1578,46 @@ def pytest_addoption(parser):
1491
1578
  browser_text = "safari"
1492
1579
  sb_config._browser_shortcut = "safari"
1493
1580
  browser_list.append("--safari")
1581
+ if "--opera" in sys_argv and not browser_set == "opera":
1582
+ if not bin_loc_in_options:
1583
+ bin_loc = detect_b_ver.get_binary_location("opera")
1584
+ if os.path.exists(bin_loc):
1585
+ browser_changes += 1
1586
+ browser_text = "opera"
1587
+ sb_config._browser_shortcut = "opera"
1588
+ sb_config._cdp_browser = "opera"
1589
+ sb_config._cdp_bin_loc = bin_loc
1590
+ browser_list.append("--opera")
1591
+ if "--brave" in sys_argv and not browser_set == "brave":
1592
+ if not bin_loc_in_options:
1593
+ bin_loc = detect_b_ver.get_binary_location("brave")
1594
+ if os.path.exists(bin_loc):
1595
+ browser_changes += 1
1596
+ browser_text = "brave"
1597
+ sb_config._browser_shortcut = "brave"
1598
+ sb_config._cdp_browser = "brave"
1599
+ sb_config._cdp_bin_loc = bin_loc
1600
+ browser_list.append("--brave")
1601
+ if "--comet" in sys_argv and not browser_set == "comet":
1602
+ if not bin_loc_in_options:
1603
+ bin_loc = detect_b_ver.get_binary_location("comet")
1604
+ if os.path.exists(bin_loc):
1605
+ browser_changes += 1
1606
+ browser_text = "comet"
1607
+ sb_config._browser_shortcut = "comet"
1608
+ sb_config._cdp_browser = "comet"
1609
+ sb_config._cdp_bin_loc = bin_loc
1610
+ browser_list.append("--comet")
1611
+ if "--atlas" in sys_argv and not browser_set == "atlas":
1612
+ if not bin_loc_in_options:
1613
+ bin_loc = detect_b_ver.get_binary_location("atlas")
1614
+ if os.path.exists(bin_loc):
1615
+ browser_changes += 1
1616
+ browser_text = "atlas"
1617
+ sb_config._browser_shortcut = "atlas"
1618
+ sb_config._cdp_browser = "atlas"
1619
+ sb_config._cdp_bin_loc = bin_loc
1620
+ browser_list.append("--atlas")
1494
1621
  if browser_changes > 1:
1495
1622
  message = "\n TOO MANY browser types were entered!"
1496
1623
  message += "\n There were %s found:\n > %s" % (
@@ -1503,10 +1630,12 @@ def pytest_addoption(parser):
1503
1630
  if (
1504
1631
  using_recorder
1505
1632
  and browser_changes == 1
1506
- and browser_text not in ["chrome", "edge"]
1633
+ and browser_text not in [
1634
+ "chrome", "edge", "opera", "brave", "comet", "atlas", "chromium"
1635
+ ]
1507
1636
  ):
1508
1637
  message = (
1509
- "\n Recorder Mode ONLY supports Chrome and Edge!"
1638
+ "\n Recorder Mode ONLY supports Chromium browsers!"
1510
1639
  '\n (Your browser choice was: "%s")\n' % browser_list[0]
1511
1640
  )
1512
1641
  raise Exception(message)
@@ -1525,7 +1654,9 @@ def pytest_addoption(parser):
1525
1654
  undetectable = True
1526
1655
  if (
1527
1656
  browser_changes == 1
1528
- and browser_text not in ["chrome"]
1657
+ and browser_text not in [
1658
+ "chrome", "opera", "brave", "comet", "atlas", "chromium"
1659
+ ]
1529
1660
  and undetectable
1530
1661
  ):
1531
1662
  message = (
@@ -1557,6 +1688,23 @@ def pytest_configure(config):
1557
1688
  sb_config.browser = config.getoption("browser")
1558
1689
  if sb_config._browser_shortcut:
1559
1690
  sb_config.browser = sb_config._browser_shortcut
1691
+ elif sys_argv == ["-c"]: # Multithreading messes with args
1692
+ if config.getoption("use_opera"):
1693
+ bin_loc = detect_b_ver.get_binary_location("opera")
1694
+ if bin_loc and os.path.exists(bin_loc):
1695
+ sb_config.browser = "opera"
1696
+ elif config.getoption("use_brave"):
1697
+ bin_loc = detect_b_ver.get_binary_location("brave")
1698
+ if bin_loc and os.path.exists(bin_loc):
1699
+ sb_config.browser = "brave"
1700
+ elif config.getoption("use_comet"):
1701
+ bin_loc = detect_b_ver.get_binary_location("comet")
1702
+ if bin_loc and os.path.exists(bin_loc):
1703
+ sb_config.browser = "comet"
1704
+ elif config.getoption("use_atlas"):
1705
+ bin_loc = detect_b_ver.get_binary_location("atlas")
1706
+ if bin_loc and os.path.exists(bin_loc):
1707
+ sb_config.browser = "atlas"
1560
1708
  sb_config.account = config.getoption("account")
1561
1709
  sb_config.data = config.getoption("data")
1562
1710
  sb_config.var1 = config.getoption("var1")
@@ -1576,7 +1724,11 @@ def pytest_configure(config):
1576
1724
  if sb_config.headless2 and sb_config.browser == "firefox":
1577
1725
  sb_config.headless2 = False # Only for Chromium browsers
1578
1726
  sb_config.headless = True # Firefox has regular headless
1579
- elif sb_config.browser not in ["chrome", "edge"]:
1727
+ elif (
1728
+ sb_config.browser not in [
1729
+ "chrome", "edge", "opera", "brave", "comet", "atlas", "chromium"
1730
+ ]
1731
+ ):
1580
1732
  sb_config.headless2 = False # Only for Chromium browsers
1581
1733
  sb_config.headed = config.getoption("headed")
1582
1734
  sb_config.xvfb = config.getoption("xvfb")
@@ -1591,7 +1743,40 @@ def pytest_configure(config):
1591
1743
  sb_config.extension_dir = config.getoption("extension_dir")
1592
1744
  sb_config.disable_features = config.getoption("disable_features")
1593
1745
  sb_config.binary_location = config.getoption("binary_location")
1594
- if config.getoption("use_cft") and not sb_config.binary_location:
1746
+ if getattr(sb_config, "_cdp_bin_loc", None):
1747
+ sb_config.binary_location = sb_config._cdp_bin_loc
1748
+ elif not sb_config.binary_location:
1749
+ if (
1750
+ config.getoption("use_opera")
1751
+ or sb_config._browser_shortcut == "opera"
1752
+ ):
1753
+ bin_loc = detect_b_ver.get_binary_location("opera")
1754
+ if bin_loc and os.path.exists(bin_loc):
1755
+ sb_config.binary_location = bin_loc
1756
+ elif (
1757
+ config.getoption("use_brave")
1758
+ or sb_config._browser_shortcut == "brave"
1759
+ ):
1760
+ bin_loc = detect_b_ver.get_binary_location("brave")
1761
+ if bin_loc and os.path.exists(bin_loc):
1762
+ sb_config.binary_location = bin_loc
1763
+ elif (
1764
+ config.getoption("use_comet")
1765
+ or sb_config._browser_shortcut == "comet"
1766
+ ):
1767
+ bin_loc = detect_b_ver.get_binary_location("comet")
1768
+ if bin_loc and os.path.exists(bin_loc):
1769
+ sb_config.binary_location = bin_loc
1770
+ elif (
1771
+ config.getoption("use_atlas")
1772
+ or sb_config._browser_shortcut == "atlas"
1773
+ ):
1774
+ bin_loc = detect_b_ver.get_binary_location("atlas")
1775
+ if bin_loc and os.path.exists(bin_loc):
1776
+ sb_config.binary_location = bin_loc
1777
+ if config.getoption("use_chromium") and not sb_config.binary_location:
1778
+ sb_config.binary_location = "_chromium_"
1779
+ elif config.getoption("use_cft") and not sb_config.binary_location:
1595
1780
  sb_config.binary_location = "cft"
1596
1781
  elif config.getoption("use_chs") and not sb_config.binary_location:
1597
1782
  sb_config.binary_location = "chs"
@@ -1603,6 +1788,10 @@ def pytest_configure(config):
1603
1788
  sb_config.headless = True
1604
1789
  sb_config.headless1 = False
1605
1790
  sb_config.headless2 = False
1791
+ if sb_config.browser in constants.ChromiumSubs.chromium_subs:
1792
+ if not sb_config.binary_location:
1793
+ sb_config.browser = "chrome" # Still uses chromedriver
1794
+ sb_config._browser_shortcut = sb_config.browser
1606
1795
  sb_config.driver_version = config.getoption("driver_version")
1607
1796
  sb_config.page_load_strategy = config.getoption("page_load_strategy")
1608
1797
  sb_config.with_testing_base = config.getoption("with_testing_base")
@@ -1747,26 +1936,16 @@ def pytest_configure(config):
1747
1936
  or " -n=" in arg_join
1748
1937
  or " -n" in arg_join
1749
1938
  or "-c" in sys_argv
1750
- or (
1751
- "addopts" in config.inicfg.keys()
1752
- and (
1753
- "-n=" in config.inicfg["addopts"]
1754
- or "-n " in config.inicfg["addopts"]
1755
- or "-n" in config.inicfg["addopts"]
1756
- )
1757
- )
1939
+ or "-n=" in config.getini("addopts")
1940
+ or "-n " in config.getini("addopts")
1941
+ or "-n" in config.getini("addopts")
1758
1942
  ):
1759
1943
  sb_config._multithreaded = True
1760
1944
  if (
1761
1945
  "--html" in sys_argv
1762
1946
  or " --html=" in arg_join
1763
- or (
1764
- "addopts" in config.inicfg.keys()
1765
- and (
1766
- "--html=" in config.inicfg["addopts"]
1767
- or "--html " in config.inicfg["addopts"]
1768
- )
1769
- )
1947
+ or "--html=" in config.getini("addopts")
1948
+ or "--html " in config.getini("addopts")
1770
1949
  ):
1771
1950
  sb_config._using_html_report = True
1772
1951
  sb_config._html_report_name = config.getoption("htmlpath")
@@ -1898,7 +2077,6 @@ def _get_test_ids_(the_item):
1898
2077
 
1899
2078
 
1900
2079
  def _create_dashboard_assets_():
1901
- import codecs
1902
2080
  from seleniumbase.js_code.live_js import live_js
1903
2081
  from seleniumbase.core.style_sheet import get_pytest_style
1904
2082
 
@@ -1911,24 +2089,24 @@ def _create_dashboard_assets_():
1911
2089
  add_pytest_style_css = True
1912
2090
  if os.path.exists(pytest_style_css):
1913
2091
  existing_pytest_style = None
1914
- with open(pytest_style_css, "r") as f:
2092
+ with open(pytest_style_css, mode="r") as f:
1915
2093
  existing_pytest_style = f.read()
1916
2094
  if existing_pytest_style == get_pytest_style():
1917
2095
  add_pytest_style_css = False
1918
2096
  if add_pytest_style_css:
1919
- out_file = codecs.open(pytest_style_css, "w+", encoding="utf-8")
2097
+ out_file = open(pytest_style_css, mode="w+", encoding="utf-8")
1920
2098
  out_file.writelines(get_pytest_style())
1921
2099
  out_file.close()
1922
2100
  live_js_file = os.path.join(assets_folder, "live.js")
1923
2101
  add_live_js_file = True
1924
2102
  if os.path.exists(live_js_file):
1925
2103
  existing_live_js = None
1926
- with open(live_js_file, "r") as f:
2104
+ with open(live_js_file, mode="r") as f:
1927
2105
  existing_live_js = f.read()
1928
2106
  if existing_live_js == live_js:
1929
2107
  add_live_js_file = False
1930
2108
  if add_live_js_file:
1931
- out_file = codecs.open(live_js_file, "w+", encoding="utf-8")
2109
+ out_file = open(live_js_file, mode="w+", encoding="utf-8")
1932
2110
  out_file.writelines(live_js)
1933
2111
  out_file.close()
1934
2112
 
@@ -2025,16 +2203,12 @@ def pytest_runtest_teardown(item):
2025
2203
  self = item._testcase
2026
2204
  with suppress(Exception):
2027
2205
  if (
2028
- hasattr(self, "driver")
2029
- and self.driver
2206
+ getattr(self, "driver", None)
2030
2207
  and "--pdb" not in sys_argv
2031
2208
  ):
2032
2209
  if not (is_windows or self.driver.service.process):
2033
2210
  self.driver.quit()
2034
- elif (
2035
- hasattr(sb_config, "_sb_pdb_driver")
2036
- and sb_config._sb_pdb_driver
2037
- ):
2211
+ elif getattr(sb_config, "_sb_pdb_driver", None):
2038
2212
  with suppress(Exception):
2039
2213
  if (
2040
2214
  not is_windows
@@ -2044,32 +2218,18 @@ def pytest_runtest_teardown(item):
2044
2218
  sb_config._sb_pdb_driver = None
2045
2219
  with suppress(Exception):
2046
2220
  if (
2047
- hasattr(self, "_xvfb_display")
2048
- and self._xvfb_display
2221
+ getattr(self, "_xvfb_display", None)
2049
2222
  and hasattr(self._xvfb_display, "stop")
2050
- and (
2051
- not hasattr(sb_config, "reuse_session")
2052
- or (
2053
- hasattr(sb_config, "reuse_session")
2054
- and not sb_config.reuse_session
2055
- )
2056
- )
2223
+ and not getattr(sb_config, "reuse_session", None)
2057
2224
  ):
2058
2225
  self.headless_active = False
2059
2226
  sb_config.headless_active = False
2060
2227
  self._xvfb_display.stop()
2061
2228
  self._xvfb_display = None
2062
2229
  if (
2063
- hasattr(sb_config, "_virtual_display")
2064
- and sb_config._virtual_display
2230
+ getattr(sb_config, "_virtual_display", None)
2065
2231
  and hasattr(sb_config._virtual_display, "stop")
2066
- and (
2067
- not hasattr(sb_config, "reuse_session")
2068
- or (
2069
- hasattr(sb_config, "reuse_session")
2070
- and not sb_config.reuse_session
2071
- )
2072
- )
2232
+ and not getattr(sb_config, "reuse_session", None)
2073
2233
  ):
2074
2234
  sb_config._virtual_display.stop()
2075
2235
  sb_config._virtual_display = None
@@ -2168,12 +2328,9 @@ def _perform_pytest_unconfigure_(config):
2168
2328
  else:
2169
2329
  start_time = reporter._session_start.time # (pytest >= 8.4.0)
2170
2330
  duration = time.time() - start_time
2171
- if (
2172
- (hasattr(sb_config, "multi_proxy") and not sb_config.multi_proxy)
2173
- or not hasattr(sb_config, "multi_proxy")
2174
- ):
2331
+ if not getattr(sb_config, "multi_proxy", None):
2175
2332
  proxy_helper.remove_proxy_zip_if_present()
2176
- if hasattr(sb_config, "reuse_session") and sb_config.reuse_session:
2333
+ if getattr(sb_config, "reuse_session", None):
2177
2334
  # Close the shared browser session
2178
2335
  if sb_config.shared_driver:
2179
2336
  try:
@@ -2190,14 +2347,13 @@ def _perform_pytest_unconfigure_(config):
2190
2347
  sb_config.shared_driver = None
2191
2348
  with suppress(Exception):
2192
2349
  if (
2193
- hasattr(sb_config, "_virtual_display")
2194
- and sb_config._virtual_display
2350
+ getattr(sb_config, "_virtual_display", None)
2195
2351
  and hasattr(sb_config._virtual_display, "stop")
2196
2352
  ):
2197
2353
  sb_config._virtual_display.stop()
2198
2354
  sb_config._virtual_display = None
2199
2355
  sb_config.headless_active = False
2200
- if hasattr(sb_config, "_vd_list") and sb_config._vd_list:
2356
+ if getattr(sb_config, "_vd_list", None):
2201
2357
  if isinstance(sb_config._vd_list, list):
2202
2358
  for display in sb_config._vd_list:
2203
2359
  if display:
@@ -2212,7 +2368,7 @@ def _perform_pytest_unconfigure_(config):
2212
2368
  shared_utils.make_dir_files_writable("./assets/")
2213
2369
  log_helper.clear_empty_logs()
2214
2370
  # Dashboard post-processing: Disable time-based refresh and stamp complete
2215
- if not hasattr(sb_config, "dashboard") or not sb_config.dashboard:
2371
+ if not getattr(sb_config, "dashboard", None):
2216
2372
  html_report_path = None
2217
2373
  the_html_r = None
2218
2374
  abs_path = os.path.abspath(".")
@@ -2229,7 +2385,7 @@ def _perform_pytest_unconfigure_(config):
2229
2385
  and html_report_path
2230
2386
  and os.path.exists(html_report_path)
2231
2387
  ):
2232
- with open(html_report_path, "r", encoding="utf-8") as f:
2388
+ with open(html_report_path, mode="r", encoding="utf-8") as f:
2233
2389
  the_html_r = f.read()
2234
2390
  assets_chunk = "if (assets.length === 1) {"
2235
2391
  remove_media = "container.classList.remove('media-container')"
@@ -2275,18 +2431,18 @@ def _perform_pytest_unconfigure_(config):
2275
2431
  the_html_r = (
2276
2432
  the_html_r[:rc_loc] + new_time + the_html_r[end_rc_loc:]
2277
2433
  )
2278
- with open(html_report_path, "w", encoding="utf-8") as f:
2434
+ with open(html_report_path, mode="w", encoding="utf-8") as f:
2279
2435
  f.write(the_html_r) # Finalize the HTML report
2280
2436
  with suppress(Exception):
2281
2437
  shared_utils.make_writable(html_report_path)
2282
- with open(html_report_path_copy, "w", encoding="utf-8") as f:
2438
+ with open(html_report_path_copy, mode="w", encoding="utf-8") as f:
2283
2439
  f.write(the_html_r) # Finalize the HTML report copy
2284
2440
  with suppress(Exception):
2285
2441
  shared_utils.make_writable(html_report_path_copy)
2286
2442
  assets_style = "./assets/style.css"
2287
2443
  if os.path.exists(assets_style):
2288
2444
  html_style = None
2289
- with open(assets_style, "r", encoding="utf-8") as f:
2445
+ with open(assets_style, mode="r", encoding="utf-8") as f:
2290
2446
  html_style = f.read()
2291
2447
  if html_style:
2292
2448
  html_style = html_style.replace("top: -50px;", "top: 2px;")
@@ -2298,7 +2454,7 @@ def _perform_pytest_unconfigure_(config):
2298
2454
  html_style = html_style.replace(".collapsible", ".oldc")
2299
2455
  html_style = html_style.replace(" (hide details)", "")
2300
2456
  html_style = html_style.replace(" (show details)", "")
2301
- with open(assets_style, "w", encoding="utf-8") as f:
2457
+ with open(assets_style, mode="w", encoding="utf-8") as f:
2302
2458
  f.write(html_style)
2303
2459
  with suppress(Exception):
2304
2460
  shared_utils.make_writable(assets_style)
@@ -2333,7 +2489,7 @@ def _perform_pytest_unconfigure_(config):
2333
2489
  # Part 1: Finalizing the dashboard / integrating html report
2334
2490
  if os.path.exists(dashboard_path):
2335
2491
  the_html_d = None
2336
- with open(dashboard_path, "r", encoding="utf-8") as f:
2492
+ with open(dashboard_path, mode="r", encoding="utf-8") as f:
2337
2493
  the_html_d = f.read()
2338
2494
  if sb_config._multithreaded and "-c" in sys_argv:
2339
2495
  # Threads have "-c" in sys.argv, except for the last
@@ -2344,7 +2500,7 @@ def _perform_pytest_unconfigure_(config):
2344
2500
  if os.path.exists(pie_path):
2345
2501
  import json
2346
2502
 
2347
- with open(pie_path, "r") as f:
2503
+ with open(pie_path, mode="r") as f:
2348
2504
  dash_pie = f.read().strip()
2349
2505
  sb_config._saved_dashboard_pie = json.loads(dash_pie)
2350
2506
  # If the test run doesn't complete by itself, stop refresh
@@ -2375,20 +2531,20 @@ def _perform_pytest_unconfigure_(config):
2375
2531
  if sb_config._dash_final_summary:
2376
2532
  the_html_d += sb_config._dash_final_summary
2377
2533
  time.sleep(0.1) # Add time for "livejs" to detect changes
2378
- with open(dashboard_path, "w", encoding="utf-8") as f:
2534
+ with open(dashboard_path, mode="w", encoding="utf-8") as f:
2379
2535
  f.write(the_html_d) # Finalize the dashboard
2380
2536
  time.sleep(0.1) # Add time for "livejs" to detect changes
2381
2537
  the_html_d = the_html_d.replace(
2382
2538
  "</head>", "</head><!-- Dashboard Report Done -->"
2383
2539
  )
2384
- with open(dashboard_path, "w", encoding="utf-8") as f:
2540
+ with open(dashboard_path, mode="w", encoding="utf-8") as f:
2385
2541
  f.write(the_html_d) # Finalize the dashboard
2386
2542
  with suppress(Exception):
2387
2543
  shared_utils.make_writable(dashboard_path)
2388
2544
  assets_style = "./assets/style.css"
2389
2545
  if os.path.exists(assets_style):
2390
2546
  html_style = None
2391
- with open(assets_style, "r", encoding="utf-8") as f:
2547
+ with open(assets_style, mode="r", encoding="utf-8") as f:
2392
2548
  html_style = f.read()
2393
2549
  if html_style:
2394
2550
  html_style = html_style.replace("top: -50px;", "top: 2px;")
@@ -2400,7 +2556,7 @@ def _perform_pytest_unconfigure_(config):
2400
2556
  html_style = html_style.replace(".collapsible", ".oldc")
2401
2557
  html_style = html_style.replace(" (hide details)", "")
2402
2558
  html_style = html_style.replace(" (show details)", "")
2403
- with open(assets_style, "w", encoding="utf-8") as f:
2559
+ with open(assets_style, mode="w", encoding="utf-8") as f:
2404
2560
  f.write(html_style)
2405
2561
  with suppress(Exception):
2406
2562
  shared_utils.make_writable(assets_style)
@@ -2422,7 +2578,7 @@ def _perform_pytest_unconfigure_(config):
2422
2578
  ):
2423
2579
  # Add the dashboard pie to the pytest html report
2424
2580
  the_html_r = None
2425
- with open(html_report_path, "r", encoding="utf-8") as f:
2581
+ with open(html_report_path, mode="r", encoding="utf-8") as f:
2426
2582
  the_html_r = f.read()
2427
2583
  if sb_config._saved_dashboard_pie:
2428
2584
  h_r_name = sb_config._html_report_name
@@ -2485,11 +2641,13 @@ def _perform_pytest_unconfigure_(config):
2485
2641
  the_html_r = (
2486
2642
  the_html_r[:rc_loc] + new_time + the_html_r[end_rc_loc:]
2487
2643
  )
2488
- with open(html_report_path, "w", encoding="utf-8") as f:
2644
+ with open(html_report_path, mode="w", encoding="utf-8") as f:
2489
2645
  f.write(the_html_r) # Finalize the HTML report
2490
2646
  with suppress(Exception):
2491
2647
  shared_utils.make_writable(html_report_path)
2492
- with open(html_report_path_copy, "w", encoding="utf-8") as f:
2648
+ with open(
2649
+ html_report_path_copy, mode="w", encoding="utf-8"
2650
+ ) as f:
2493
2651
  f.write(the_html_r) # Finalize the HTML report copy
2494
2652
  with suppress(Exception):
2495
2653
  shared_utils.make_writable(html_report_path_copy)
@@ -2506,15 +2664,20 @@ def pytest_unconfigure(config):
2506
2664
  reporter = config.pluginmanager.get_plugin("terminalreporter")
2507
2665
  if (
2508
2666
  not hasattr(reporter, "_sessionstarttime")
2509
- and not hasattr(reporter, "_session_start")
2510
- and not hasattr(reporter._session_start, "time")
2667
+ and (
2668
+ not hasattr(reporter, "_session_start")
2669
+ or (
2670
+ hasattr(reporter, "_session_start")
2671
+ and not hasattr(reporter._session_start, "time")
2672
+ )
2673
+ )
2511
2674
  ):
2512
2675
  return
2513
- if hasattr(sb_config, "_multithreaded") and sb_config._multithreaded:
2676
+ if getattr(sb_config, "_multithreaded", None):
2514
2677
  import fasteners
2515
2678
 
2516
2679
  dash_lock = fasteners.InterProcessLock(constants.Dashboard.LOCKFILE)
2517
- if hasattr(sb_config, "dashboard") and sb_config.dashboard:
2680
+ if getattr(sb_config, "dashboard", None):
2518
2681
  # Multi-threaded tests with the Dashboard
2519
2682
  abs_path = os.path.abspath(".")
2520
2683
  dash_lock_file = constants.Dashboard.LOCKFILE
@@ -2529,7 +2692,9 @@ def pytest_unconfigure(config):
2529
2692
  ):
2530
2693
  # Dash is HTML Report (Multithreaded)
2531
2694
  sb_config._dash_is_html_report = True
2532
- with open(dashboard_path, "w", encoding="utf-8") as f:
2695
+ with open(
2696
+ dashboard_path, mode="w", encoding="utf-8"
2697
+ ) as f:
2533
2698
  f.write(sb_config._dash_html)
2534
2699
  # Dashboard Multithreaded
2535
2700
  _perform_pytest_unconfigure_(config)