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
4 changes: 2 additions & 2 deletions mkdocs_build/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
# Minimum Python version: 3.10 (for generating docs only)

regex>=2025.11.3
pymdown-extensions>=10.19.1
pymdown-extensions>=10.20
pipdeptree>=2.30.0
python-dateutil>=2.8.2
Markdown==3.10
click==8.3.1
ghp-import==2.1.0
watchdog==6.0.0
cairocffi==1.7.1
pathspec==0.12.1
pathspec==1.0.1
Babel==2.17.0
paginate==0.5.7
mkdocs==1.6.1
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ setuptools~=70.2;python_version<"3.10"
setuptools>=80.9.0;python_version>="3.10"
wheel>=0.45.1
attrs>=25.4.0
certifi>=2025.11.12
certifi>=2026.1.4
exceptiongroup>=1.3.1
websockets>=15.0.1
filelock~=3.19.1;python_version<"3.10"
filelock>=3.20.1;python_version>="3.10"
filelock>=3.20.2;python_version>="3.10"
fasteners>=0.20
mycdp>=1.3.2
pynose>=1.5.5
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.45.8"
__version__ = "4.45.9"
77 changes: 63 additions & 14 deletions seleniumbase/undetected/cdp_driver/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from __future__ import annotations
import asyncio
import atexit
import fasteners
import http.cookiejar
import json
import logging
Expand All @@ -15,7 +16,10 @@
import urllib.request
import warnings
from collections import defaultdict
from contextlib import suppress
from seleniumbase import config as sb_config
from seleniumbase.fixtures import constants
from seleniumbase.fixtures import shared_utils
from typing import List, Optional, Set, Tuple, Union
import mycdp as cdp
from . import cdp_util as util
Expand All @@ -34,7 +38,7 @@ def get_registered_instances():
def deconstruct_browser():
for _ in __registered__instances__:
if not _.stopped:
_.stop()
_.stop(deconstruct=True)
for attempt in range(5):
try:
if _.config and not _.config.uses_custom_data_dir:
Expand Down Expand Up @@ -732,8 +736,11 @@ async def tile_windows(self, windows=None, max_columns: int = 0):
try:
import mss
except Exception:
from seleniumbase.fixtures import shared_utils
shared_utils.pip_install("mss")
pip_find_lock = fasteners.InterProcessLock(
constants.PipInstall.FINDLOCK
)
with pip_find_lock: # Prevent issues with multiple processes
shared_utils.pip_install("mss")
import mss
m = mss.mss()
screen, screen_width, screen_height = 3 * (None,)
Expand Down Expand Up @@ -842,11 +849,17 @@ def __next__(self):
else:
del self._i

def stop(self):
def stop(self, deconstruct=False):
if (
not hasattr(sb_config, "_closed_connection_ids")
or not isinstance(sb_config._closed_connection_ids, list)
):
sb_config._closed_connection_ids = []
connection_id = None
with suppress(Exception):
connection_id = self.connection.websocket.id.hex
close_success = False
try:
# asyncio.get_running_loop().create_task(
# self.connection.send(cdp.browser.close())
# )
if self.connection:
asyncio.get_event_loop().create_task(self.connection.aclose())
logger.debug(
Expand All @@ -855,19 +868,22 @@ def stop(self):
except RuntimeError:
if self.connection:
try:
# asyncio.run(self.connection.send(cdp.browser.close()))
asyncio.run(self.connection.aclose())
logger.debug("Closed the connection using asyncio.run()")
except Exception:
pass
for _ in range(3):
try:
self._process.terminate()
logger.debug(
"Terminated browser with pid %d successfully."
% self._process.pid
)
break
if connection_id not in sb_config._closed_connection_ids:
self._process.terminate()
logger.debug(
"Terminated browser with pid %d successfully."
% self._process.pid
)
if connection_id:
sb_config._closed_connection_ids.append(connection_id)
close_success = True
break
except (Exception,):
try:
self._process.kill()
Expand Down Expand Up @@ -902,6 +918,39 @@ def stop(self):
raise
self._process = None
self._process_pid = None
if (
hasattr(sb_config, "_xvfb_users")
and isinstance(sb_config._xvfb_users, int)
and close_success
and hasattr(sb_config, "_virtual_display")
and sb_config._virtual_display
):
sb_config._xvfb_users -= 1
if sb_config._xvfb_users < 0:
sb_config._xvfb_users = 0
if (
shared_utils.is_linux()
and (
hasattr(sb_config, "_virtual_display")
and sb_config._virtual_display
and hasattr(sb_config._virtual_display, "stop")
)
and sb_config._xvfb_users == 0
):
try:
sb_config._virtual_display.stop()
sb_config._virtual_display = None
sb_config.headless_active = False
except AttributeError:
pass
except Exception:
pass
if (
deconstruct
and connection_id
and connection_id in sb_config._closed_connection_ids
):
sb_config._closed_connection_ids.remove(connection_id)

def quit(self):
self.stop()
Expand Down
15 changes: 15 additions & 0 deletions seleniumbase/undetected/cdp_driver/cdp_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,27 @@ def __activate_virtual_display_as_needed(
headless, headed, xvfb, xvfb_metrics
):
"""This is only needed on Linux."""
reset_virtual_display = False
if IS_LINUX and (not headed or xvfb):
if (
not hasattr(sb_config, "_closed_connection_ids")
or not isinstance(sb_config._closed_connection_ids, list)
):
sb_config._closed_connection_ids = []
if (
not hasattr(sb_config, "_xvfb_users")
or not isinstance(sb_config._xvfb_users, int)
):
reset_virtual_display = True
sb_config._xvfb_users = 0
sb_config._xvfb_users += 1
if (
IS_LINUX
and (not headed or xvfb)
and (
not hasattr(sb_config, "_virtual_display")
or not sb_config._virtual_display
or reset_virtual_display
)
):
from sbvirtualdisplay import Display
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,11 @@
'setuptools>=80.9.0;python_version>="3.10"',
'wheel>=0.45.1',
'attrs>=25.4.0',
'certifi>=2025.11.12',
'certifi>=2026.1.4',
'exceptiongroup>=1.3.1',
'websockets>=15.0.1',
'filelock~=3.19.1;python_version<"3.10"',
'filelock>=3.20.1;python_version>="3.10"',
'filelock>=3.20.2;python_version>="3.10"',
'fasteners>=0.20',
'mycdp>=1.3.2',
'pynose>=1.5.5',
Expand Down Expand Up @@ -268,7 +268,7 @@
# (An optional library for image-processing.)
"pillow": [
'Pillow>=11.3.0;python_version<"3.10"',
'Pillow>=12.0.0;python_version>="3.10"',
'Pillow>=12.1.0;python_version>="3.10"',
],
# pip install -e .[pip-system-certs]
# (If you see [SSL: CERTIFICATE_VERIFY_FAILED], then get this.)
Expand Down