Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e041652
Showing
7 changed files
with
333 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.tox/ | ||
e/ | ||
.idea/ | ||
build/ | ||
chromedriver_installer.egg-info/ | ||
*.pyc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# file GENERATED by distutils, do NOT edit | ||
setup.py | ||
chromedriver_installer/__init__.py |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__author__ = 'phudec' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
from distutils.command.build import build | ||
from distutils.command.install import install | ||
from distutils.command.build_scripts import build_scripts | ||
from distutils.core import setup | ||
import hashlib | ||
import os | ||
import platform | ||
import re | ||
import tempfile | ||
import urllib | ||
import sys | ||
import zipfile | ||
|
||
try: | ||
from urllib import request | ||
except ImportError: | ||
import urllib as request | ||
|
||
|
||
CHROMEDRIVER_INFO_URL = ( | ||
'https://sites.google.com/a/chromium.org/chromedriver/downloads' | ||
) | ||
CHROMEDRIVER_URL_TEMPLATE = ( | ||
'http://chromedriver.storage.googleapis.com/{version}/chromedriver_{os_}' | ||
'{architecture}.zip' | ||
) | ||
|
||
CHROMEDRIVER_VERSION_PATTERN = re.compile(r'^\d+\.\d+$') | ||
CROMEDRIVER_LATEST_VERSION_PATTERN = re.compile( | ||
r'Latest-Release:-ChromeDriver-(\d+\.\d+)' | ||
) | ||
|
||
# Global variables | ||
chromedriver_version = None | ||
chromedriver_checksums = None | ||
|
||
|
||
def get_chromedriver_version(): | ||
"""Retrieves the most recent chromedriver version.""" | ||
global chromedriver_version | ||
|
||
response = request.urlopen(CHROMEDRIVER_INFO_URL) | ||
content = response.read() | ||
match = CROMEDRIVER_LATEST_VERSION_PATTERN.search(content) | ||
if match: | ||
return match.group(1) | ||
else: | ||
raise Exception('Unable to get latest chromedriver version from {0}' | ||
.format(CHROMEDRIVER_INFO_URL)) | ||
|
||
|
||
class BuildScripts(build_scripts): | ||
"""Downloads and unzips the requested chromedriver executable.""" | ||
|
||
def _download(self, zip_path): | ||
plat = platform.platform().lower() | ||
architecture = 32 | ||
if plat.startswith('darwin'): | ||
os_ = 'mac' | ||
elif plat.startswith('linux'): | ||
os_ = 'linux' | ||
architecture = platform.architecture()[0][:-3] | ||
elif plat.startswith('win'): | ||
os_ = 'win' | ||
else: | ||
raise Exception('Unsupported platform: {0}'.format(plat)) | ||
|
||
url = CHROMEDRIVER_URL_TEMPLATE.format(version=chromedriver_version, | ||
os_=os_, | ||
architecture=architecture) | ||
|
||
download_report_template = ("\t - downloading from '{0}' to '{1}'" | ||
.format(url, zip_path)) | ||
|
||
def reporthoook(x, y, z): | ||
global download_ok | ||
|
||
percent_downloaded = '{0:.0%}'.format((x * y) / float(z)) | ||
sys.stdout.write('\r') | ||
sys.stdout.write("{0} [{1}]".format(download_report_template, | ||
percent_downloaded)) | ||
download_ok = percent_downloaded == '100%' | ||
if download_ok: | ||
sys.stdout.write(' OK') | ||
sys.stdout.flush() | ||
|
||
request.urlretrieve(url, zip_path, reporthoook) | ||
|
||
print('') | ||
if not download_ok: | ||
print('\t - download failed!') | ||
|
||
def _unzip(self, zip_path): | ||
zf = zipfile.ZipFile(zip_path) | ||
print("\t - extracting '{0}' to '{1}'.".format(zip_path, self.build_dir)) | ||
zf.extractall(self.build_dir) | ||
|
||
def _validate(self, zip_path): | ||
checksum = hashlib.md5(open(zip_path, 'rb').read()).hexdigest() | ||
return checksum in chromedriver_checksums | ||
|
||
def run(self): | ||
global chromedriver_version, chromedriver_checksums | ||
|
||
validate = False | ||
|
||
if chromedriver_version: | ||
if chromedriver_checksums: | ||
validate = True | ||
else: | ||
chromedriver_version = get_chromedriver_version() | ||
|
||
file_name = 'chromedriver_{0}.zip'.format(chromedriver_version) | ||
zip_path = os.path.join(tempfile.gettempdir(), file_name) | ||
cached = os.path.exists(zip_path) | ||
if os.path.exists(zip_path): | ||
print("\t - requested file '{0}' found at '{1}'." | ||
.format(file_name, zip_path)) | ||
|
||
if not (validate and cached and self._validate(zip_path)): | ||
if cached and not self._validate(zip_path): | ||
print("\t - cached file '{0}' is not valid!".format(zip_path)) | ||
|
||
self._download(zip_path) | ||
if validate: | ||
if self._validate(zip_path): | ||
print('\t - checksum OK') | ||
else: | ||
raise Exception("The checksum of the downloaded file '{0}' " | ||
"matches none of the checksums {1}!" | ||
.format(zip_path, | ||
', '.join(chromedriver_checksums))) | ||
else: | ||
print("\t - cached file '{0}' is valid.".format(zip_path)) | ||
|
||
self._unzip(zip_path) | ||
self.scripts = [os.path.join(self.build_dir, script) for script in | ||
os.listdir(self.build_dir)] | ||
build_scripts.run(self) | ||
|
||
def finalize_options(self): | ||
build_scripts.finalize_options(self) | ||
self.scripts = [] | ||
|
||
|
||
class Install(install): | ||
"""Used to get chromedriver version and checksums from install options""" | ||
|
||
user_options = install.user_options + [ | ||
('chromedriver-version=', None, 'Chromedriver version'), | ||
('chromedriver-checksums=', None, 'Chromedriver checksums'), | ||
] | ||
|
||
def initialize_options(self): | ||
self.chromedriver_version = None | ||
self.chromedriver_checksums = [] | ||
install.initialize_options(self) | ||
|
||
def run(self): | ||
global chromedriver_version, chromedriver_checksums | ||
|
||
if self.chromedriver_version: | ||
if not CHROMEDRIVER_VERSION_PATTERN.match(self.chromedriver_version): | ||
raise Exception('Invalid --chromedriver-version={0}! ' | ||
'Must match /{1}/' | ||
.format(self.chromedriver_version, | ||
CHROMEDRIVER_VERSION_PATTERN.pattern)) | ||
|
||
chromedriver_version = self.chromedriver_version | ||
chromedriver_checksums = self.chromedriver_checksums | ||
if chromedriver_checksums: | ||
chromedriver_checksums = [ch.strip() for ch in | ||
self.chromedriver_checksums.split(',')] | ||
|
||
install.run(self) | ||
|
||
|
||
setup( | ||
name='chromedriver_installer', | ||
version='0.0.0', | ||
author='Peter Hudec', | ||
author_email='peterhudec@peterhudec.com', | ||
description='Chromedriver Installer', | ||
long_description=open(os.path.join(os.path.dirname(__file__), 'README.rst')) | ||
.read(), | ||
keywords='chromedriver installer', | ||
url='https://github.com/peterhudec/chromedriver_installer', | ||
classifiers=[ | ||
'Development Status :: 3 - Alpha', | ||
'Environment :: Web Environment', | ||
'Intended Audience :: Developers', | ||
'License :: OSI Approved :: MIT License', | ||
'Operating System :: OS Independent', | ||
'Programming Language :: Python', | ||
'Topic :: Internet :: WWW/HTTP', | ||
'Topic :: Software Development :: Libraries :: Python Modules', | ||
'Topic :: Software Development :: Libraries :: Python Modules', | ||
'Topic :: System :: Installation/Setup', | ||
], | ||
license='MIT', | ||
package_data={'': ['*.txt', '*.rst']}, | ||
# If packages is empty, contents of ./build/lib will not be copied! | ||
packages=['chromedriver_installer'], | ||
scripts=['if', 'empty', 'BuildScripts', 'will', 'be', 'ignored'], | ||
cmdclass=dict(build_scripts=BuildScripts, install=Install) | ||
) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import os | ||
import shlex | ||
import subprocess | ||
|
||
import pytest | ||
|
||
|
||
VERSION = '2.13' | ||
PROJECT_DIR = os.path.abspath(os.path.dirname(__file__)) | ||
VIRTUALENV_DIR = os.environ['VIRTUAL_ENV'] | ||
INSTALL_COMMAND_BASE = 'pip install --egg {0} '.format(PROJECT_DIR) | ||
|
||
|
||
VERSIONS = { | ||
'2.10': ( | ||
'4fecc99b066cb1a346035bf022607104', | ||
'058cd8b7b4b9688507701b5e648fd821', | ||
'fd0dafc3ada3619edda2961f2beadc5c', | ||
'082e91e5c8994a7879710caeed62e334' | ||
), | ||
'2.11': ( | ||
'bf0d731cd34fd07e22f4641c9aec8483', | ||
'7a7336caea140f6ac1cb8fae8df50d36', | ||
'447ebc91ac355fc11e960c95f2c15622', | ||
'44443738344b887ff1fe94710a8d45dc' | ||
) | ||
} | ||
|
||
|
||
@pytest.fixture(params=VERSIONS) | ||
def version_info(request): | ||
return request.param | ||
|
||
|
||
class Base(object): | ||
def _uninstall(self): | ||
try: | ||
subprocess.check_call(shlex.split('pip uninstall chromedriver_installer -y')) | ||
except subprocess.CalledProcessError: | ||
pass | ||
|
||
chromedriver_executable = os.path.join(VIRTUALENV_DIR, | ||
'bin', 'chromedriver') | ||
|
||
if os.path.exists(chromedriver_executable): | ||
print('REMOVING chromedriver executable: ' + chromedriver_executable) | ||
os.remove(chromedriver_executable) | ||
|
||
def teardown(self): | ||
self._uninstall() | ||
|
||
def _not_available(self): | ||
with pytest.raises(OSError): | ||
subprocess.check_call(shlex.split('chromedriver --version')) | ||
|
||
|
||
class TestFailure(Base): | ||
def test_bad_checksum(self): | ||
self._not_available() | ||
|
||
command = INSTALL_COMMAND_BASE + ( | ||
'--install-option="--chromedriver-version=2.10" ' | ||
'--install-option="--chromedriver-checksums=foo,bar,baz"' | ||
) | ||
|
||
error_message = subprocess.Popen( | ||
shlex.split(command), | ||
stdout=subprocess.PIPE, | ||
stderr=subprocess.PIPE, | ||
).communicate()[0] | ||
|
||
assert 'failed with error code 1' in str(error_message) | ||
|
||
|
||
class VersionBase(Base): | ||
|
||
def test_version(self, version_info): | ||
self.version = version_info | ||
self.checksums = VERSIONS[version_info] | ||
|
||
# Chromedriver executable should not be available. | ||
self._not_available() | ||
|
||
subprocess.check_call(shlex.split(self._get_install_command())) | ||
|
||
# ...the chromedriver executable should be available... | ||
expected_version = subprocess.Popen( | ||
shlex.split('chromedriver -v'), | ||
stdout=subprocess.PIPE | ||
).communicate()[0] | ||
|
||
# ...and should be of the right version. | ||
assert self.version in str(expected_version) | ||
|
||
|
||
class TestVersionOnly(VersionBase): | ||
def _get_install_command(self): | ||
return INSTALL_COMMAND_BASE + \ | ||
'--install-option="--chromedriver-version={0}"'.format(self.version) | ||
|
||
|
||
class TestVersionAndChecksums(VersionBase): | ||
def _get_install_command(self): | ||
return INSTALL_COMMAND_BASE + ( | ||
'--install-option="--chromedriver-version={0}" ' | ||
'--install-option="--chromedriver-checksums={1}"' | ||
).format(self.version, ','.join(self.checksums)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[tox] | ||
envlist=py26, py27, py34 | ||
|
||
[testenv] | ||
skip_install=True | ||
deps= | ||
pytest | ||
commands= | ||
py.test -vv tests.py |