Skip to content

Commit

Permalink
Merge pull request #3325 from seleniumbase/pytest-and-threading-updates
Browse files Browse the repository at this point in the history
`pytest` and multi-threading updates
  • Loading branch information
mdmintz authored Dec 8, 2024
2 parents a353c53 + d1c7fd0 commit d200f49
Show file tree
Hide file tree
Showing 18 changed files with 177 additions and 48 deletions.
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

0 comments on commit d200f49

Please sign in to comment.