Skip to content
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: 1 addition & 1 deletion examples/boilerplates/samples/google_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def test_google_dot_com(self):
self.assert_title_contains("Google")
self.sleep(0.05)
self.save_screenshot_to_logs() # ("./latest_logs" folder)
self.press_keys(HomePage.search_box, "GitHub")
self.type(HomePage.search_box, "GitHub")
self.assert_element(HomePage.search_button)
self.assert_element(HomePage.feeling_lucky_button)
self.click(HomePage.search_button)
Expand Down
2 changes: 1 addition & 1 deletion examples/raw_google.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
sb.activate_cdp_mode()
sb.goto("https://google.com/ncr")
sb.click_if_visible('button:contains("Accept all")')
sb.press_keys('[name="q"]', "SeleniumBase GitHub page")
sb.type('[name="q"]', "SeleniumBase GitHub page")
sb.click('[value="Google Search"]')
sb.sleep(4) # The "AI Overview" sometimes loads
print(sb.get_page_title())
Expand Down
2 changes: 1 addition & 1 deletion examples/test_download_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_download_files_from_pypi(self):
self.assert_element("span#pip-command")
self.assert_text("Download files", "div#files h2.page-title")
self.assert_text("Download files", "a#files-tab")
pkg_header = self.get_text("h1.package-header__name").strip()
pkg_header = self.get_text('h1[class*="header__name"]').strip()
pkg_name = pkg_header.replace(" ", "-")
whl_file = pkg_name + "-py3-none-any.whl"
tar_gz_file = pkg_name + ".tar.gz"
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pytest-xdist==3.8.0
parameterized==0.9.0
behave==1.2.6
soupsieve~=2.8.4;python_version<"3.10"
soupsieve~=2.9.1;python_version>="3.10"
soupsieve~=2.9.2;python_version>="3.10"
beautifulsoup4~=4.15.0
pyotp~=2.10.0
python-xlib==0.33;platform_system=="Linux"
Expand All @@ -82,7 +82,7 @@ rich>=15.0.0,<16
# ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.)

coverage>=7.10.7;python_version<"3.10"
coverage>=7.15.3;python_version>="3.10"
coverage>=7.15.4;python_version>="3.10"
pytest-cov>=7.1.0
flake8==7.3.0
mccabe==0.7.0
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.51.10"
__version__ = "4.51.11"
31 changes: 26 additions & 5 deletions seleniumbase/undetected/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,22 +292,23 @@ def __init__(
)
browser = subprocess.Popen(
[options.binary_location, *options.arguments],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
close_fds=IS_POSIX,
creationflags=creationflags,
)
self.browser_pid = browser.pid
self._process_pid = browser.pid
self.browser_process = browser
self._process_pid = self.browser_pid
try:
self._process_create_time = (
psutil.Process(self._process_pid).create_time()
)
except Exception:
self._process_create_time = None
service_ = None
log_output = subprocess.PIPE
log_output = subprocess.DEVNULL
if patch_driver:
service_ = selenium.webdriver.chrome.service.Service(
executable_path=self.patcher.executable_path,
Expand All @@ -328,6 +329,16 @@ def __init__(
setattr(service_, "creation_flags", creationflags)
try:
super().__init__(options=options, service=service_)
if (
hasattr(self, "service")
and getattr(self.service, "process", None)
):
self._service_process = self.service.process
if (
self._service_process.stdin
and not self._service_process.stdin.closed
):
self._service_process.stdin.close()
except OSError as e:
if IS_MAC and "Bad CPU type in executable" in str(e):
print(str(e))
Expand Down Expand Up @@ -665,6 +676,16 @@ def __del__(self):
super().quit()
with suppress(Exception):
self.quit()
with suppress(Exception):
if hasattr(self, "_service_process") and self._service_process:
if self._service_process.poll() is None:
self._service_process.kill()
self._service_process.wait(timeout=1)
with suppress(Exception):
if hasattr(self, "browser_process") and self.browser_process:
if self.browser_process.poll() is None:
self.browser_process.kill()
self.browser_process.wait(timeout=1)

def __enter__(self):
return self
Expand Down
50 changes: 44 additions & 6 deletions seleniumbase/undetected/cdp_driver/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -656,9 +656,9 @@ async def start(self=None) -> Browser:
await asyncio.create_subprocess_exec(
exe,
*params,
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
stdin=asyncio.subprocess.DEVNULL,
stdout=asyncio.subprocess.DEVNULL,
stderr=asyncio.subprocess.DEVNULL,
close_fds=is_posix,
)
)
Expand Down Expand Up @@ -934,7 +934,22 @@ def stop(self, deconstruct=False):
close_success = False
try:
if self.connection:
loop = asyncio.get_running_loop()
loop = None
for obj in (self, self.connection, getattr(
self.connection, "websocket", None)
):
if hasattr(obj, "loop"):
loop = obj.loop
break
if hasattr(obj, "_loop"):
loop = obj._loop
break
if not loop:
with suppress(Exception):
loop = asyncio.get_event_loop_policy().get_event_loop()
if not loop:
with suppress(Exception):
loop = asyncio.get_event_loop()
if loop.is_running():
loop.create_task(self.connection.aclose())
logger.debug("Closed connection with create_task()")
Expand All @@ -952,8 +967,9 @@ def stop(self, deconstruct=False):
for _ in range(3):
try:
if connection_id not in sb_config._closed_connection_ids:
the_proc = psutil.Process(self._process.pid)
self._process.terminate()
procs.append(psutil.Process(self._process.pid))
procs.append(the_proc)
logger.debug(
"Terminated browser with pid %d successfully."
% self._process.pid
Expand All @@ -964,8 +980,9 @@ def stop(self, deconstruct=False):
break
except (Exception,):
try:
the_proc = psutil.Process(self._process.pid)
self._process.kill()
procs.append(psutil.Process(self._process.pid))
procs.append(the_proc)
logger.debug(
"Killed browser with pid %d successfully."
% self._process.pid
Expand Down Expand Up @@ -1016,6 +1033,27 @@ def stop(self, deconstruct=False):
logger.debug("Process has been terminated: %d." % p.pid)
for p in alive:
logger.debug("Process is still alive: %d." % p.pid)
if "loop" in locals() and loop and not loop.is_closed():
with suppress(Exception):
if not loop.is_running():
# Time to flush websocket closes and child watcher signals
loop.run_until_complete(asyncio.sleep(0.05))
# Cancel lingering tasks
pending = [
t for t in asyncio.all_tasks(loop) if not t.done()
]
for task in pending:
task.cancel()
if pending:
loop.run_until_complete(
asyncio.gather(*pending, return_exceptions=True)
)
# Shutdown and close
loop.run_until_complete(loop.shutdown_asyncgens())
loop.close()
# Clear the closed loop from the thread policy
# so future tests get a fresh loop
asyncio.set_event_loop(asyncio.new_event_loop())
if self.config.user_data_dir and not self.config.uses_custom_data_dir:
for _ in range(3):
try:
Expand Down
19 changes: 10 additions & 9 deletions seleniumbase/undetected/cdp_driver/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,8 +528,8 @@ async def mouse_click_async(
with suppress(Exception):
await self.mouse_move_async()
await asyncio.sleep(random.uniform(0.0036, 0.0046))
x = center[0] + random.uniform(-0.85, 0.85)
y = center[1] + random.uniform(-0.85, 0.85)
x = center[0] + random.uniform(-0.875, 0.875)
y = center[1] + random.uniform(-0.875, 0.875)
asyncio.create_task(
self._tab.send(
cdp.input_.dispatch_mouse_event(
Expand All @@ -546,23 +546,23 @@ async def mouse_click_async(
)
if not timeframe or timeframe <= 0:
# If 0 (or less), hold for a small amount of time
await asyncio.sleep(random.uniform(0.0142, 0.0152))
x += random.uniform(-0.12, 0.12)
y += random.uniform(-0.12, 0.12)
await asyncio.sleep(random.uniform(0.015, 0.016))
x += random.uniform(-0.115, 0.115)
y += random.uniform(-0.115, 0.115)
else:
end_time = asyncio.get_running_loop().time() + timeframe
while asyncio.get_running_loop().time() < end_time:
await self._tab.send(
cdp.input_.dispatch_mouse_event(
type_="mouseMoved",
x=x + random.uniform(-0.12, 0.12),
y=y + random.uniform(-0.12, 0.12),
x=x + random.uniform(-0.115, 0.115),
y=y + random.uniform(-0.115, 0.115),
button=cdp.input_.MouseButton(button),
buttons=buttons,
force=0.5,
)
)
await asyncio.sleep(random.uniform(0.120, 0.280))
await asyncio.sleep(random.uniform(0.15, 0.25))
asyncio.create_task(
self._tab.send(
cdp.input_.dispatch_mouse_event(
Expand Down Expand Up @@ -919,6 +919,7 @@ async def send_keys_async(self, text: str):
windows_virtual_key_code=vk,
)
)
await asyncio.sleep(random.uniform(0.002, 0.003))
# 3. Trigger keypress DOM event AND insert text
if text_val:
await self._tab.send(
Expand All @@ -932,7 +933,7 @@ async def send_keys_async(self, text: str):
)
)
# 4. Trigger keyup DOM event
await asyncio.sleep(random.uniform(0.0126, 0.0152))
await asyncio.sleep(random.uniform(0.011, 0.014))
await self._tab.send(
cdp.input_.dispatch_key_event(
type_="keyUp",
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@
'parameterized==0.9.0',
'behave==1.2.6', # Newer ones had issues
'soupsieve~=2.8.4;python_version<"3.10"',
'soupsieve~=2.9.1;python_version>="3.10"',
'soupsieve~=2.9.2;python_version>="3.10"',
'beautifulsoup4~=4.15.0',
'pyotp~=2.10.0',
'python-xlib==0.33;platform_system=="Linux"',
Expand All @@ -255,7 +255,7 @@
# Usage: coverage run -m pytest; coverage html; coverage report
"coverage": [
'coverage>=7.10.7;python_version<"3.10"',
'coverage>=7.15.3;python_version>="3.10"',
'coverage>=7.15.4;python_version>="3.10"',
'pytest-cov>=7.1.0',
],
# pip install -e .[flake8]
Expand Down