This commit is contained in:
UltrafunkAmsterdam 2021-11-14 12:32:22 +01:00
parent 1e363b18be
commit 9f9bd66d79
2 changed files with 133 additions and 41 deletions

View File

@ -0,0 +1,72 @@
import multiprocessing
import os
import platform
import sys
from subprocess import PIPE
from subprocess import Popen
import atexit
import traceback
import logging
import signal
CREATE_NEW_PROCESS_GROUP = 0x00000200
DETACHED_PROCESS = 0x00000008
REGISTERED = []
def start_detached(executable, *args):
"""
Starts a fully independent subprocess (with no parent)
:param executable: executable
:param args: arguments to the executable, eg: ['--param1_key=param1_val', '-vvv' ...]
:return: pid of the grandchild process
"""
# create pipe
reader, writer = multiprocessing.Pipe(False)
# do not keep reference
multiprocessing.Process(target=_start_detached, args=(executable, *args), kwargs={'writer': writer},
daemon=True).start()
# receive pid from pipe
pid = reader.recv()
REGISTERED.append(pid)
# close pipes
writer.close()
reader.close()
return pid
def _start_detached(executable, *args, writer: multiprocessing.Pipe = None):
# configure launch
kwargs = {}
if platform.system() == 'Windows':
kwargs.update(creationflags=DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
elif sys.version_info < (3, 2):
# assume posix
kwargs.update(preexec_fn=os.setsid)
else: # Python 3.2+ and Unix
kwargs.update(start_new_session=True)
# run
p = Popen([executable, *args], stdin=PIPE, stdout=PIPE, stderr=PIPE, **kwargs)
# send pid to pipe
writer.send(p.pid)
exit()
def _cleanup():
for pid in REGISTERED:
try:
logging.getLogger(__name__).debug('cleaning up pid %d ' % pid)
os.kill(pid, signal.SIGTERM)
except: # noqa
traceback.print_exc()
atexit.register(_cleanup)

View File

@ -3,24 +3,27 @@
from __future__ import annotations from __future__ import annotations
import asyncio
import json import json
import logging import logging
import os import os
import re import re
import shutil import shutil
import subprocess
import sys import sys
import tempfile import tempfile
import time import time
import requests
import selenium.webdriver.chrome.service import selenium.webdriver.chrome.service
import selenium.webdriver.chrome.webdriver import selenium.webdriver.chrome.webdriver
import selenium.webdriver.common.service import selenium.webdriver.common.service
import selenium.webdriver.remote.webdriver import selenium.webdriver.remote.webdriver
import websockets
from .cdp import CDP from .cdp import CDP
from .options import ChromeOptions from .options import ChromeOptions
from .patcher import IS_POSIX, Patcher from .patcher import IS_POSIX
from .patcher import Patcher
from .reactor import Reactor from .reactor import Reactor
__all__ = ( __all__ = (
@ -35,6 +38,8 @@ __all__ = (
logger = logging.getLogger("uc") logger = logging.getLogger("uc")
logger.setLevel(logging.getLogger().getEffectiveLevel()) logger.setLevel(logging.getLogger().getEffectiveLevel())
from .dprocess import start_detached
class Chrome(selenium.webdriver.chrome.webdriver.WebDriver): class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
""" """
@ -167,7 +172,6 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
try: try:
if hasattr(options, "_session") and options._session is not None: if hasattr(options, "_session") and options._session is not None:
# prevent reuse of options, # prevent reuse of options,
# as it just appends arguments, not replace them # as it just appends arguments, not replace them
# you'll get conflicts starting chrome # you'll get conflicts starting chrome
@ -291,14 +295,15 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
if not desired_capabilities: if not desired_capabilities:
desired_capabilities = options.to_capabilities() desired_capabilities = options.to_capabilities()
self.browser_pid = start_detached(options.binary_location, *options.arguments)
self.browser = subprocess.Popen( # self.browser = subprocess.Popen(
[options.binary_location, *options.arguments], # [options.binary_location, *options.arguments],
stdin=subprocess.PIPE, # stdin=subprocess.PIPE,
stdout=subprocess.PIPE, # stdout=subprocess.PIPE,
stderr=subprocess.PIPE, # stderr=subprocess.PIPE,
close_fds=IS_POSIX, # close_fds=IS_POSIX,
) # )
super(Chrome, self).__init__( super(Chrome, self).__init__(
executable_path=patcher.executable_path, executable_path=patcher.executable_path,
@ -523,6 +528,22 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
def __dir__(self): def __dir__(self):
return object.__dir__(self) return object.__dir__(self)
def get(self, url):
tabs = requests.get('http://{0}:{1}/json'.format(*self.options.debugger_address.split(':'))).json()
for tab in tabs:
if tab['type'] == 'page':
break
async def _get():
wsurl = tab['webSocketDebuggerUrl']
async with websockets.connect(wsurl) as ws:
await ws.send(json.dumps({"method": "Page.navigate", "params": {"url": url}, "id": 1}))
return await ws.recv()
with self:
return asyncio.get_event_loop().run_until_complete(_get())
def add_cdp_listener(self, event_name, callback): def add_cdp_listener(self, event_name, callback):
if ( if (
self.reactor self.reactor
@ -577,7 +598,6 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
capabilities = self.options.to_capabilities() capabilities = self.options.to_capabilities()
super(Chrome, self).start_session(capabilities, browser_profile) super(Chrome, self).start_session(capabilities, browser_profile)
def quit(self): def quit(self):
logger.debug("closing webdriver") logger.debug("closing webdriver")
self.service.process.kill() self.service.process.kill()
@ -588,8 +608,9 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
pass pass
try: try:
logger.debug("killing browser") logger.debug("killing browser")
self.browser.terminate() os.kill(self.browser_pid)
self.browser.wait(1) # self.browser.terminate()
# self.browser.wait(1)
except TimeoutError as e: except TimeoutError as e:
logger.debug(e, exc_info=True) logger.debug(e, exc_info=True)
@ -637,7 +658,6 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
return hash(self.options.debugger_address) return hash(self.options.debugger_address)
def find_chrome_executable(): def find_chrome_executable():
""" """
Finds the chrome, chrome beta, chrome canary, chromium executable Finds the chrome, chrome beta, chrome canary, chromium executable