Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
peterhudec committed Mar 11, 2015
0 parents commit e041652
Show file tree
Hide file tree
Showing 7 changed files with 333 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
@@ -0,0 +1,6 @@
.tox/
e/
.idea/
build/
chromedriver_installer.egg-info/
*.pyc
3 changes: 3 additions & 0 deletions MANIFEST
@@ -0,0 +1,3 @@
# file GENERATED by distutils, do NOT edit
setup.py
chromedriver_installer/__init__.py
Empty file added README.rst
Empty file.
1 change: 1 addition & 0 deletions chromedriver_installer/__init__.py
@@ -0,0 +1 @@
__author__ = 'phudec'
207 changes: 207 additions & 0 deletions setup.py
@@ -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)
)

107 changes: 107 additions & 0 deletions tests.py
@@ -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))
9 changes: 9 additions & 0 deletions tox.ini
@@ -0,0 +1,9 @@
[tox]
envlist=py26, py27, py34

[testenv]
skip_install=True
deps=
pytest
commands=
py.test -vv tests.py

0 comments on commit e041652

Please sign in to comment.