Skip to content

Commit

Permalink
Auto-install chromedriver from start_chrome(...)
Browse files Browse the repository at this point in the history
  • Loading branch information
mherrmann committed Jul 5, 2023
1 parent 4c6546c commit 2cf202f
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 14 deletions.
18 changes: 10 additions & 8 deletions helium/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ def start_chrome(
start_chrome()
If this doesn't work for you, then it may be that Helium's copy of
ChromeDriver is not compatible with your version of Chrome. To fix this,
place a copy of ChromeDriver on your `PATH`.
You can optionally open a URL::
start_chrome("google.com")
Expand All @@ -66,10 +62,16 @@ def start_chrome(
capabilities["goog:loggingPrefs"] = {'performance': 'ALL'}
start_chrome(capabilities=capabilities)
On shutdown of the Python interpreter, Helium cleans up all resources used
for controlling the browser (such as the ChromeDriver process), but does
not close the browser itself. If you want to terminate the browser at the
end of your script, use the following command::
When no compatible ChromeDriver is found on your `PATH`, then `start_chrome`
automatically downloads it into the following directories::
* Windows: `%LOCALAPPDATA%\Cache\Helium`
* macOS: `~/Library/Caches/Helium`
* Linux: `$XDG_CACHE_HOME/helium` or `~/.cache/Helium`
On shutdown of the Python interpreter, Helium terminates the ChromeDriver
process but does not close the browser itself. If you want to close the
browser at the end of your script, use the following command::
kill_browser()
"""
Expand Down
14 changes: 10 additions & 4 deletions helium/_impl/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from copy import copy
from helium._impl.chromedriver import install_matching_chromedriver
from helium._impl.match_type import PREFIX_IGNORE_CASE
from helium._impl.selenium_wrappers import WebElementWrapper, \
WebDriverWrapper, FrameIterator, FramesChangedWhileIterating
Expand All @@ -16,7 +17,8 @@
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support.ui import Select
from selenium.webdriver import Chrome, ChromeOptions, Firefox, FirefoxOptions, FirefoxProfile
from selenium.webdriver import Chrome, ChromeOptions, Firefox, FirefoxOptions, \
FirefoxProfile
from time import sleep, time

import atexit
Expand Down Expand Up @@ -74,7 +76,9 @@ class APIImpl:
" * set_driver(...)"
def __init__(self):
self.driver = None
def start_firefox_impl(self, url=None, headless=False, options=None, profile=None):
def start_firefox_impl(
self, url=None, headless=False, options=None, profile=None
):
firefox_driver = self._start_firefox_driver(headless, options, profile)
return self._start(firefox_driver, url)
def _start_firefox_driver(self, headless, options, profile):
Expand Down Expand Up @@ -105,10 +109,12 @@ def start_chrome_impl(
def _start_chrome_driver(self, headless, maximize, options, capabilities):
chrome_options = self._get_chrome_options(headless, maximize, options)
try:
result = Chrome(options=chrome_options, desired_capabilities=capabilities)
result = Chrome(
options=chrome_options, desired_capabilities=capabilities
)
except WebDriverException:
# This usually happens when chromedriver is not on the PATH.
driver_path = self._use_included_web_driver('chromedriver')
driver_path = install_matching_chromedriver()
result = Chrome(
options=chrome_options, desired_capabilities=capabilities,
executable_path=driver_path
Expand Down
42 changes: 42 additions & 0 deletions helium/_impl/chromedriver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from get_chrome_driver import GetChromeDriver
from os.path import join, expanduser

import os
import platform

def install_matching_chromedriver():
get_driver = GetChromeDriver()
chrome_version = get_driver._GetChromeDriver__get_installed_chrome_version()
cache_directory = _get_cache_directory()
cached_chrome_version = \
StringStoredInFile(join(cache_directory, 'chrome.version'))
if chrome_version != cached_chrome_version.read():
get_driver.auto_download(cache_directory, True)
cached_chrome_version.write(chrome_version)
return join(
cache_directory,
'chromedriver' + '.exe' if platform.system() == 'Windows' else ''
)

def _get_cache_directory():
system = platform.system()
if system == 'Windows':
return join(os.getenv('LOCALAPPDATA'), 'Cache', 'Helium')
elif system == 'Darwin':
return expanduser('~/Library/Caches/Helium')
else:
cache_home = os.getenv('XDG_CACHE_HOME', expanduser('~/.cache'))
return join(cache_home, 'helium')

class StringStoredInFile:
def __init__(self, path):
self.path = path
def read(self):
try:
with open(self.path) as f:
return f.read()
except FileNotFoundError:
return None
def write(self, value):
with open(self.path, 'w') as f:
f.write(value)
Binary file removed helium/_impl/webdrivers/linux/chromedriver
Binary file not shown.
Binary file removed helium/_impl/webdrivers/mac/chromedriver
Binary file not shown.
Binary file removed helium/_impl/webdrivers/windows/chromedriver.exe
Binary file not shown.
4 changes: 3 additions & 1 deletion requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
selenium==3.141.0

# Selenium 3 is incompatible with urllib3 >= 2:
urllib3<2
urllib3<2

get-chrome-driver==1.3.12
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
# Also update requirements/base.txt when you make changes here.
'selenium==3.141.0',
# Selenium 3 is incompatible with urllib3 >= 2:
'urllib3<2'
'urllib3<2',
'get-chrome-driver==1.3.12'
],
package_data = {
'helium._impl': ['webdrivers/**/*']
Expand Down
24 changes: 24 additions & 0 deletions tests/unit/test__impl/test_chromedriver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from helium._impl import chromedriver
from helium._impl.chromedriver import install_matching_chromedriver
from os import access, X_OK
from os.path import getctime
from tempfile import TemporaryDirectory
from unittest import TestCase


class InstallMatchingChromeDriverTest(TestCase):
def test_install_matching_chromedriver(self):
driver_path = install_matching_chromedriver()
self.assertTrue(access(driver_path, X_OK))
def test_caching(self):
driver_path = install_matching_chromedriver()
ctime = getctime(driver_path)
self.assertEqual(install_matching_chromedriver(), driver_path)
self.assertEqual(ctime, getctime(driver_path))
def setUp(self):
self.temp_dir = TemporaryDirectory()
self._original_get_cache_directory = chromedriver._get_cache_directory
chromedriver._get_cache_directory = lambda: self.temp_dir.name
def tearDown(self):
chromedriver._get_cache_directory = self._original_get_cache_directory
self.temp_dir.cleanup()

0 comments on commit 2cf202f

Please sign in to comment.