Skip to content

pytest and multi-threading updates #3325

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/tour_examples/bootstrap_google_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def test_google_tour(self):
self.wait_for_element("#searchboxinput", timeout=20)
self.wait_for_element("#minimap", timeout=20)
self.wait_for_element("#zoom", timeout=20)
self.wait_for_element("#widget-zoom-out")
self.wait_for_element('[jsaction*="minimap.main;"]')

self.create_bootstrap_tour()
self.add_tour_step("Welcome to Google Maps", title="SeleniumBase Tour")
Expand Down
2 changes: 2 additions & 0 deletions examples/tour_examples/driverjs_maps_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ def test_create_tour(self):
self.wait_for_element("#searchboxinput", timeout=20)
self.wait_for_element("#minimap", timeout=20)
self.wait_for_element("#zoom", timeout=20)
self.wait_for_element("#widget-zoom-out")
self.wait_for_element('[jsaction*="minimap.main;"]')

# Create a website tour using the DriverJS library
# Same as: self.create_driverjs_tour()
Expand Down
2 changes: 2 additions & 0 deletions examples/tour_examples/google_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ def test_google_tour(self):
self.wait_for_element("#searchboxinput")
self.wait_for_element("#minimap")
self.wait_for_element("#zoom")
self.wait_for_element("#widget-zoom-out")
self.wait_for_element('[jsaction*="minimap.main;"]')

# Create a website tour using the IntroJS library
# Same as: self.create_introjs_tour()
Expand Down
2 changes: 2 additions & 0 deletions examples/tour_examples/hopscotch_google_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def test_google_tour(self):
self.wait_for_element("#searchboxinput", timeout=20)
self.wait_for_element("#minimap", timeout=20)
self.wait_for_element("#zoom", timeout=20)
self.wait_for_element("#widget-zoom-out")
self.wait_for_element('[jsaction*="minimap.main;"]')

self.create_hopscotch_tour()
self.add_tour_step("Welcome to Google Maps", title="SeleniumBase Tour")
Expand Down
2 changes: 2 additions & 0 deletions examples/tour_examples/introjs_google_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def test_google_tour(self):
self.wait_for_element("#searchboxinput", timeout=20)
self.wait_for_element("#minimap", timeout=20)
self.wait_for_element("#zoom", timeout=20)
self.wait_for_element("#widget-zoom-out")
self.wait_for_element('[jsaction*="minimap.main;"]')

self.set_introjs_colors("#f26721", "#db5409")
self.create_introjs_tour()
Expand Down
2 changes: 2 additions & 0 deletions examples/tour_examples/maps_introjs_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ def test_google_maps_tour(self):
self.wait_for_element("#searchboxinput", timeout=20)
self.wait_for_element("#minimap", timeout=20)
self.wait_for_element("#zoom", timeout=20)
self.wait_for_element("#widget-zoom-out")
self.wait_for_element('[jsaction*="minimap.main;"]')

self.create_tour(theme="introjs")
self.add_tour_step(
Expand Down
2 changes: 2 additions & 0 deletions examples/tour_examples/shepherd_google_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def test_google_tour(self):
self.wait_for_element("#searchboxinput", timeout=20)
self.wait_for_element("#minimap", timeout=20)
self.wait_for_element("#zoom", timeout=20)
self.wait_for_element("#widget-zoom-out")
self.wait_for_element('[jsaction*="minimap.main;"]')

self.create_shepherd_tour(theme="dark")
self.add_tour_step("Welcome to Google Maps!")
Expand Down
2 changes: 1 addition & 1 deletion mkdocs_build/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ lxml==5.3.0
pyquery==2.0.1
readtime==3.0.0
mkdocs==1.6.1
mkdocs-material==9.5.47
mkdocs-material==9.5.48
mkdocs-exclude-search==0.6.6
mkdocs-simple-hooks==0.1.5
mkdocs-material-extensions==1.3.1
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ rich==13.9.4
# ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.)

coverage>=7.6.1;python_version<"3.9"
coverage>=7.6.8;python_version>="3.9"
coverage>=7.6.9;python_version>="3.9"
pytest-cov>=5.0.0;python_version<"3.9"
pytest-cov>=6.0.0;python_version>="3.9"
flake8==5.0.4;python_version<"3.9"
Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/__version__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# seleniumbase package
__version__ = "4.33.5"
__version__ = "4.33.6"
82 changes: 57 additions & 25 deletions seleniumbase/core/browser_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,26 +97,12 @@ def log_d(message):
print(message)


def make_writable(file_path):
# Set permissions to: "If you can read it, you can write it."
mode = os.stat(file_path).st_mode
mode |= (mode & 0o444) >> 1 # copy R bits to W
os.chmod(file_path, mode)


def make_executable(file_path):
# Set permissions to: "If you can read it, you can execute it."
mode = os.stat(file_path).st_mode
mode |= (mode & 0o444) >> 2 # copy R bits to X
os.chmod(file_path, mode)


def make_driver_executable_if_not(driver_path):
# Verify driver has executable permissions. If not, add them.
permissions = oct(os.stat(driver_path)[0])[-3:]
if "4" in permissions or "6" in permissions:
# We want at least a '5' or '7' to make sure it's executable
make_executable(driver_path)
shared_utils.make_executable(driver_path)


def extend_driver(driver):
Expand Down Expand Up @@ -566,6 +552,10 @@ def uc_open_with_cdp_mode(driver, url=None):
for tab in driver.cdp_base.tabs[-1::-1]:
if "chrome-extension://" not in str(tab):
with gui_lock:
with suppress(Exception):
shared_utils.make_writable(
constants.MultiBrowser.PYAUTOGUILOCK
)
loop.run_until_complete(tab.activate())
break

Expand All @@ -580,11 +570,17 @@ def uc_open_with_cdp_mode(driver, url=None):
if page_tab:
loop.run_until_complete(page_tab.aopen())
with gui_lock:
with suppress(Exception):
shared_utils.make_writable(
constants.MultiBrowser.PYAUTOGUILOCK
)
loop.run_until_complete(page_tab.activate())

loop.run_until_complete(driver.cdp_base.update_targets())
page = loop.run_until_complete(driver.cdp_base.get(url))
with gui_lock:
with suppress(Exception):
shared_utils.make_writable(constants.MultiBrowser.PYAUTOGUILOCK)
loop.run_until_complete(page.activate())
loop.run_until_complete(page.wait())
if not safe_url:
Expand Down Expand Up @@ -883,17 +879,12 @@ def install_pyautogui_if_missing(driver):
with pip_find_lock:
pass
except Exception:
# Need write permissions
with suppress(Exception):
make_writable(constants.PipInstall.FINDLOCK)
try:
with pip_find_lock:
pass
except Exception:
# Since missing permissions, skip the locks
__install_pyautogui_if_missing()
return
# Since missing permissions, skip the locks
__install_pyautogui_if_missing()
return
with pip_find_lock: # Prevent issues with multiple processes
with suppress(Exception):
shared_utils.make_writable(constants.PipInstall.FINDLOCK)
__install_pyautogui_if_missing()


Expand Down Expand Up @@ -1789,6 +1780,8 @@ def _add_chrome_proxy_extension(
if zip_it:
proxy_zip_lock = fasteners.InterProcessLock(PROXY_ZIP_LOCK)
with proxy_zip_lock:
with suppress(Exception):
shared_utils.make_writable(PROXY_ZIP_LOCK)
if multi_proxy:
_set_proxy_filenames()
if not os.path.exists(proxy_helper.PROXY_ZIP_PATH):
Expand All @@ -1800,6 +1793,8 @@ def _add_chrome_proxy_extension(
else:
proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
with proxy_dir_lock:
with suppress(Exception):
shared_utils.make_writable(PROXY_DIR_LOCK)
if multi_proxy:
_set_proxy_filenames()
if not os.path.exists(proxy_helper.PROXY_DIR_PATH):
Expand All @@ -1825,6 +1820,8 @@ def is_using_uc(undetectable, browser_name):
def _unzip_to_new_folder(zip_file, folder):
proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
with proxy_dir_lock:
with suppress(Exception):
shared_utils.make_writable(PROXY_DIR_LOCK)
if not os.path.exists(folder):
import zipfile
zip_ref = zipfile.ZipFile(zip_file, "r")
Expand Down Expand Up @@ -2934,6 +2931,8 @@ def get_remote_driver(
constants.PipInstall.FINDLOCK
)
with pip_find_lock: # Prevent issues with multiple processes
with suppress(Exception):
shared_utils.make_writable(constants.PipInstall.FINDLOCK)
try:
from seleniumwire import webdriver
import blinker
Expand Down Expand Up @@ -3371,6 +3370,8 @@ def get_local_driver(
constants.PipInstall.FINDLOCK
)
with pip_find_lock: # Prevent issues with multiple processes
with suppress(Exception):
shared_utils.make_writable(constants.PipInstall.FINDLOCK)
try:
from seleniumwire import webdriver
import blinker
Expand Down Expand Up @@ -3434,6 +3435,10 @@ def get_local_driver(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
with geckodriver_fixing_lock:
with suppress(Exception):
shared_utils.make_writable(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
if not geckodriver_on_path():
sys_args = sys.argv # Save a copy of sys args
log_d(
Expand Down Expand Up @@ -3736,6 +3741,10 @@ def get_local_driver(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
with edgedriver_fixing_lock:
with suppress(Exception):
shared_utils.make_writable(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
msg = "Microsoft Edge Driver not found."
if edgedriver_upgrade_needed:
msg = "Microsoft Edge Driver update needed."
Expand Down Expand Up @@ -4119,6 +4128,10 @@ def get_local_driver(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
with edgedriver_fixing_lock:
with suppress(Exception):
shared_utils.make_writable(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
with suppress(Exception):
if not _was_driver_repaired():
_repair_edgedriver(edge_version)
Expand Down Expand Up @@ -4501,6 +4514,10 @@ def get_local_driver(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
with chromedriver_fixing_lock:
with suppress(Exception):
shared_utils.make_writable(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
msg = "chromedriver update needed. Getting it now:"
if not path_chromedriver:
msg = "chromedriver not found. Getting it now:"
Expand Down Expand Up @@ -4592,6 +4609,10 @@ def get_local_driver(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
with uc_lock: # Avoid multithreaded issues
with suppress(Exception):
shared_utils.make_writable(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
if make_uc_driver_from_chromedriver:
if os.path.exists(LOCAL_CHROMEDRIVER):
with suppress(Exception):
Expand Down Expand Up @@ -4851,6 +4872,10 @@ def get_local_driver(
if not os.path.exists(cf_lock_path):
# Avoid multithreaded issues
with cf_lock:
with suppress(Exception):
shared_utils.make_writable(
cf_lock_path
)
# Install Python Certificates (MAC)
os.system(
r"bash /Applications/Python*/"
Expand Down Expand Up @@ -4994,6 +5019,10 @@ def get_local_driver(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
with chromedriver_fixing_lock:
with suppress(Exception):
shared_utils.make_writable(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
if not _was_driver_repaired():
_repair_chromedriver(
chrome_options, headless_options, mcv
Expand Down Expand Up @@ -5192,7 +5221,10 @@ def get_local_driver(
chromedr_fixing_lock = fasteners.InterProcessLock(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
D_F_L = constants.MultiBrowser.DRIVER_FIXING_LOCK
with chromedr_fixing_lock:
with suppress(Exception):
shared_utils.make_writable(D_F_L)
if not _was_driver_repaired():
with suppress(Exception):
_repair_chromedriver(
Expand Down
14 changes: 8 additions & 6 deletions seleniumbase/core/log_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,9 +339,10 @@ def log_skipped_test_data(test, test_logpath, driver, browser, reason):
data_to_save.append(" * Skip Reason: %s" % reason)
data_to_save.append("")
file_path = os.path.join(test_logpath, "skip_reason.txt")
log_file = codecs.open(file_path, "w+", encoding="utf-8")
log_file.writelines("\r\n".join(data_to_save))
log_file.close()
with suppress(Exception):
log_file = codecs.open(file_path, "w+", encoding="utf-8")
log_file.writelines("\r\n".join(data_to_save))
log_file.close()


def log_page_source(test_logpath, driver, source=None):
Expand All @@ -368,9 +369,10 @@ def log_page_source(test_logpath, driver, source=None):
if not os.path.exists(test_logpath):
os.makedirs(test_logpath)
html_file_path = os.path.join(test_logpath, html_file_name)
html_file = codecs.open(html_file_path, "w+", encoding="utf-8")
html_file.write(page_source)
html_file.close()
with suppress(Exception):
html_file = codecs.open(html_file_path, "w+", encoding="utf-8")
html_file.write(page_source)
html_file.close()


def get_test_id(test):
Expand Down
12 changes: 12 additions & 0 deletions seleniumbase/core/sb_cdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,10 @@ def set_attributes(self, selector, attribute, value):
with suppress(Exception):
self.loop.run_until_complete(self.page.evaluate(js_code))

def __make_sure_pyautogui_lock_is_writable(self):
with suppress(Exception):
shared_utils.make_writable(constants.MultiBrowser.PYAUTOGUILOCK)

def __verify_pyautogui_has_a_headed_browser(self):
"""PyAutoGUI requires a headed browser so that it can
focus on the correct element when performing actions."""
Expand All @@ -1039,6 +1043,8 @@ def __install_pyautogui_if_missing(self):
constants.PipInstall.FINDLOCK
)
with pip_find_lock: # Prevent issues with multiple processes
with suppress(Exception):
shared_utils.make_writable(constants.PipInstall.FINDLOCK)
try:
import pyautogui
with suppress(Exception):
Expand Down Expand Up @@ -1124,6 +1130,7 @@ def gui_press_key(self, key):
constants.MultiBrowser.PYAUTOGUILOCK
)
with gui_lock:
self.__make_sure_pyautogui_lock_is_writable()
pyautogui.press(key)
time.sleep(0.044)
self.__slow_mode_pause_if_set()
Expand All @@ -1137,6 +1144,7 @@ def gui_press_keys(self, keys):
constants.MultiBrowser.PYAUTOGUILOCK
)
with gui_lock:
self.__make_sure_pyautogui_lock_is_writable()
for key in keys:
pyautogui.press(key)
time.sleep(0.044)
Expand All @@ -1151,6 +1159,7 @@ def gui_write(self, text):
constants.MultiBrowser.PYAUTOGUILOCK
)
with gui_lock:
self.__make_sure_pyautogui_lock_is_writable()
pyautogui.write(text)
self.__slow_mode_pause_if_set()
self.loop.run_until_complete(self.page.wait())
Expand All @@ -1171,6 +1180,7 @@ def __gui_click_x_y(self, x, y, timeframe=0.25, uc_lock=False):
constants.MultiBrowser.PYAUTOGUILOCK
)
with gui_lock: # Prevent issues with multiple processes
self.__make_sure_pyautogui_lock_is_writable()
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
if timeframe >= 0.25:
time.sleep(0.056) # Wait if moving at human-speed
Expand All @@ -1191,6 +1201,7 @@ def gui_click_x_y(self, x, y, timeframe=0.25):
constants.MultiBrowser.PYAUTOGUILOCK
)
with gui_lock: # Prevent issues with multiple processes
self.__make_sure_pyautogui_lock_is_writable()
self.__install_pyautogui_if_missing()
import pyautogui
pyautogui = self.__get_configured_pyautogui(pyautogui)
Expand Down Expand Up @@ -1408,6 +1419,7 @@ def gui_hover_and_click(self, hover_selector, click_selector):
constants.MultiBrowser.PYAUTOGUILOCK
)
with gui_lock:
self.__make_sure_pyautogui_lock_is_writable()
self.bring_active_window_to_front()
self.gui_hover_element(hover_selector)
time.sleep(0.15)
Expand Down
Loading
Loading