Skip to content

Commit 85ca8c1

Browse files
authored
Merge pull request #4457 from seleniumbase/cdp-mode-patch-127
CDP Mode: Patch 127
2 parents 58995fc + e5c1807 commit 85ca8c1

9 files changed

Lines changed: 88 additions & 28 deletions

File tree

examples/boilerplates/samples/google_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def test_google_dot_com(self):
2020
self.assert_title_contains("Google")
2121
self.sleep(0.05)
2222
self.save_screenshot_to_logs() # ("./latest_logs" folder)
23-
self.press_keys(HomePage.search_box, "GitHub")
23+
self.type(HomePage.search_box, "GitHub")
2424
self.assert_element(HomePage.search_button)
2525
self.assert_element(HomePage.feeling_lucky_button)
2626
self.click(HomePage.search_button)

examples/raw_google.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
sb.activate_cdp_mode()
55
sb.goto("https://google.com/ncr")
66
sb.click_if_visible('button:contains("Accept all")')
7-
sb.press_keys('[name="q"]', "SeleniumBase GitHub page")
7+
sb.type('[name="q"]', "SeleniumBase GitHub page")
88
sb.click('[value="Google Search"]')
99
sb.sleep(4) # The "AI Overview" sometimes loads
1010
print(sb.get_page_title())

examples/test_download_files.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def test_download_files_from_pypi(self):
2929
self.assert_element("span#pip-command")
3030
self.assert_text("Download files", "div#files h2.page-title")
3131
self.assert_text("Download files", "a#files-tab")
32-
pkg_header = self.get_text("h1.package-header__name").strip()
32+
pkg_header = self.get_text('h1[class*="header__name"]').strip()
3333
pkg_name = pkg_header.replace(" ", "-")
3434
whl_file = pkg_name + "-py3-none-any.whl"
3535
tar_gz_file = pkg_name + ".tar.gz"

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pytest-xdist==3.8.0
6868
parameterized==0.9.0
6969
behave==1.2.6
7070
soupsieve~=2.8.4;python_version<"3.10"
71-
soupsieve~=2.9.1;python_version>="3.10"
71+
soupsieve~=2.9.2;python_version>="3.10"
7272
beautifulsoup4~=4.15.0
7373
pyotp~=2.10.0
7474
python-xlib==0.33;platform_system=="Linux"
@@ -82,7 +82,7 @@ rich>=15.0.0,<16
8282
# ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.)
8383

8484
coverage>=7.10.7;python_version<"3.10"
85-
coverage>=7.15.3;python_version>="3.10"
85+
coverage>=7.15.4;python_version>="3.10"
8686
pytest-cov>=7.1.0
8787
flake8==7.3.0
8888
mccabe==0.7.0

seleniumbase/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "4.51.10"
2+
__version__ = "4.51.11"

seleniumbase/undetected/__init__.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,22 +292,23 @@ def __init__(
292292
)
293293
browser = subprocess.Popen(
294294
[options.binary_location, *options.arguments],
295-
stdin=subprocess.PIPE,
296-
stdout=subprocess.PIPE,
297-
stderr=subprocess.PIPE,
295+
stdin=subprocess.DEVNULL,
296+
stdout=subprocess.DEVNULL,
297+
stderr=subprocess.DEVNULL,
298298
close_fds=IS_POSIX,
299299
creationflags=creationflags,
300300
)
301301
self.browser_pid = browser.pid
302-
self._process_pid = browser.pid
302+
self.browser_process = browser
303+
self._process_pid = self.browser_pid
303304
try:
304305
self._process_create_time = (
305306
psutil.Process(self._process_pid).create_time()
306307
)
307308
except Exception:
308309
self._process_create_time = None
309310
service_ = None
310-
log_output = subprocess.PIPE
311+
log_output = subprocess.DEVNULL
311312
if patch_driver:
312313
service_ = selenium.webdriver.chrome.service.Service(
313314
executable_path=self.patcher.executable_path,
@@ -328,6 +329,16 @@ def __init__(
328329
setattr(service_, "creation_flags", creationflags)
329330
try:
330331
super().__init__(options=options, service=service_)
332+
if (
333+
hasattr(self, "service")
334+
and getattr(self.service, "process", None)
335+
):
336+
self._service_process = self.service.process
337+
if (
338+
self._service_process.stdin
339+
and not self._service_process.stdin.closed
340+
):
341+
self._service_process.stdin.close()
331342
except OSError as e:
332343
if IS_MAC and "Bad CPU type in executable" in str(e):
333344
print(str(e))
@@ -665,6 +676,16 @@ def __del__(self):
665676
super().quit()
666677
with suppress(Exception):
667678
self.quit()
679+
with suppress(Exception):
680+
if hasattr(self, "_service_process") and self._service_process:
681+
if self._service_process.poll() is None:
682+
self._service_process.kill()
683+
self._service_process.wait(timeout=1)
684+
with suppress(Exception):
685+
if hasattr(self, "browser_process") and self.browser_process:
686+
if self.browser_process.poll() is None:
687+
self.browser_process.kill()
688+
self.browser_process.wait(timeout=1)
668689

669690
def __enter__(self):
670691
return self

seleniumbase/undetected/cdp_driver/browser.py

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -656,9 +656,9 @@ async def start(self=None) -> Browser:
656656
await asyncio.create_subprocess_exec(
657657
exe,
658658
*params,
659-
stdin=asyncio.subprocess.PIPE,
660-
stdout=asyncio.subprocess.PIPE,
661-
stderr=asyncio.subprocess.PIPE,
659+
stdin=asyncio.subprocess.DEVNULL,
660+
stdout=asyncio.subprocess.DEVNULL,
661+
stderr=asyncio.subprocess.DEVNULL,
662662
close_fds=is_posix,
663663
)
664664
)
@@ -934,7 +934,22 @@ def stop(self, deconstruct=False):
934934
close_success = False
935935
try:
936936
if self.connection:
937-
loop = asyncio.get_running_loop()
937+
loop = None
938+
for obj in (self, self.connection, getattr(
939+
self.connection, "websocket", None)
940+
):
941+
if hasattr(obj, "loop"):
942+
loop = obj.loop
943+
break
944+
if hasattr(obj, "_loop"):
945+
loop = obj._loop
946+
break
947+
if not loop:
948+
with suppress(Exception):
949+
loop = asyncio.get_event_loop_policy().get_event_loop()
950+
if not loop:
951+
with suppress(Exception):
952+
loop = asyncio.get_event_loop()
938953
if loop.is_running():
939954
loop.create_task(self.connection.aclose())
940955
logger.debug("Closed connection with create_task()")
@@ -952,8 +967,9 @@ def stop(self, deconstruct=False):
952967
for _ in range(3):
953968
try:
954969
if connection_id not in sb_config._closed_connection_ids:
970+
the_proc = psutil.Process(self._process.pid)
955971
self._process.terminate()
956-
procs.append(psutil.Process(self._process.pid))
972+
procs.append(the_proc)
957973
logger.debug(
958974
"Terminated browser with pid %d successfully."
959975
% self._process.pid
@@ -964,8 +980,9 @@ def stop(self, deconstruct=False):
964980
break
965981
except (Exception,):
966982
try:
983+
the_proc = psutil.Process(self._process.pid)
967984
self._process.kill()
968-
procs.append(psutil.Process(self._process.pid))
985+
procs.append(the_proc)
969986
logger.debug(
970987
"Killed browser with pid %d successfully."
971988
% self._process.pid
@@ -1016,6 +1033,27 @@ def stop(self, deconstruct=False):
10161033
logger.debug("Process has been terminated: %d." % p.pid)
10171034
for p in alive:
10181035
logger.debug("Process is still alive: %d." % p.pid)
1036+
if "loop" in locals() and loop and not loop.is_closed():
1037+
with suppress(Exception):
1038+
if not loop.is_running():
1039+
# Time to flush websocket closes and child watcher signals
1040+
loop.run_until_complete(asyncio.sleep(0.05))
1041+
# Cancel lingering tasks
1042+
pending = [
1043+
t for t in asyncio.all_tasks(loop) if not t.done()
1044+
]
1045+
for task in pending:
1046+
task.cancel()
1047+
if pending:
1048+
loop.run_until_complete(
1049+
asyncio.gather(*pending, return_exceptions=True)
1050+
)
1051+
# Shutdown and close
1052+
loop.run_until_complete(loop.shutdown_asyncgens())
1053+
loop.close()
1054+
# Clear the closed loop from the thread policy
1055+
# so future tests get a fresh loop
1056+
asyncio.set_event_loop(asyncio.new_event_loop())
10191057
if self.config.user_data_dir and not self.config.uses_custom_data_dir:
10201058
for _ in range(3):
10211059
try:

seleniumbase/undetected/cdp_driver/element.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -528,8 +528,8 @@ async def mouse_click_async(
528528
with suppress(Exception):
529529
await self.mouse_move_async()
530530
await asyncio.sleep(random.uniform(0.0036, 0.0046))
531-
x = center[0] + random.uniform(-0.85, 0.85)
532-
y = center[1] + random.uniform(-0.85, 0.85)
531+
x = center[0] + random.uniform(-0.875, 0.875)
532+
y = center[1] + random.uniform(-0.875, 0.875)
533533
asyncio.create_task(
534534
self._tab.send(
535535
cdp.input_.dispatch_mouse_event(
@@ -546,23 +546,23 @@ async def mouse_click_async(
546546
)
547547
if not timeframe or timeframe <= 0:
548548
# If 0 (or less), hold for a small amount of time
549-
await asyncio.sleep(random.uniform(0.0142, 0.0152))
550-
x += random.uniform(-0.12, 0.12)
551-
y += random.uniform(-0.12, 0.12)
549+
await asyncio.sleep(random.uniform(0.015, 0.016))
550+
x += random.uniform(-0.115, 0.115)
551+
y += random.uniform(-0.115, 0.115)
552552
else:
553553
end_time = asyncio.get_running_loop().time() + timeframe
554554
while asyncio.get_running_loop().time() < end_time:
555555
await self._tab.send(
556556
cdp.input_.dispatch_mouse_event(
557557
type_="mouseMoved",
558-
x=x + random.uniform(-0.12, 0.12),
559-
y=y + random.uniform(-0.12, 0.12),
558+
x=x + random.uniform(-0.115, 0.115),
559+
y=y + random.uniform(-0.115, 0.115),
560560
button=cdp.input_.MouseButton(button),
561561
buttons=buttons,
562562
force=0.5,
563563
)
564564
)
565-
await asyncio.sleep(random.uniform(0.120, 0.280))
565+
await asyncio.sleep(random.uniform(0.15, 0.25))
566566
asyncio.create_task(
567567
self._tab.send(
568568
cdp.input_.dispatch_mouse_event(
@@ -919,6 +919,7 @@ async def send_keys_async(self, text: str):
919919
windows_virtual_key_code=vk,
920920
)
921921
)
922+
await asyncio.sleep(random.uniform(0.002, 0.003))
922923
# 3. Trigger keypress DOM event AND insert text
923924
if text_val:
924925
await self._tab.send(
@@ -932,7 +933,7 @@ async def send_keys_async(self, text: str):
932933
)
933934
)
934935
# 4. Trigger keyup DOM event
935-
await asyncio.sleep(random.uniform(0.0126, 0.0152))
936+
await asyncio.sleep(random.uniform(0.011, 0.014))
936937
await self._tab.send(
937938
cdp.input_.dispatch_key_event(
938939
type_="keyUp",

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@
232232
'parameterized==0.9.0',
233233
'behave==1.2.6', # Newer ones had issues
234234
'soupsieve~=2.8.4;python_version<"3.10"',
235-
'soupsieve~=2.9.1;python_version>="3.10"',
235+
'soupsieve~=2.9.2;python_version>="3.10"',
236236
'beautifulsoup4~=4.15.0',
237237
'pyotp~=2.10.0',
238238
'python-xlib==0.33;platform_system=="Linux"',
@@ -255,7 +255,7 @@
255255
# Usage: coverage run -m pytest; coverage html; coverage report
256256
"coverage": [
257257
'coverage>=7.10.7;python_version<"3.10"',
258-
'coverage>=7.15.3;python_version>="3.10"',
258+
'coverage>=7.15.4;python_version>="3.10"',
259259
'pytest-cov>=7.1.0',
260260
],
261261
# pip install -e .[flake8]

0 commit comments

Comments
 (0)