This script requires at least Python 2.6. You can check the version
of Python that you have installed with 'swc-installation-test-1.py'.
+
+By default, this script will test for all the dependencies your
+instructor thinks you need. If you want to test for a different set
+of packages, you can list them on the command line. For example:
+
+ python swc-installation-test-2.py git virtual-editor
+
+This is useful if the original test told you to install a more recent
+version of a particular dependency, and you just want to re-test that
+dependency.
"""
+# Some details about the implementation:
+
+# The dependencies are divided into a hierarchy of classes rooted on
+# Dependency class. You can refer to the code to see which package
+# comes under which type of dependency.
+
+# The CHECKER dictionary stores information about all the dependencies
+# and CHECKS stores list of the dependencies which are to be checked in
+# the current workshop.
+
+# In the "__name__ == '__main__'" block, we launch all the checks with
+# check() function, which prints information about the tests as they run
+# and details about the failures after the tests complete. In case of
+# failure, the functions print_system_info() and print_suggestions()
+# are called after this, where the former prints information about the
+# user's system for debugging purposes while the latter prints some
+# suggestions to follow.
+
+
from __future__ import print_function # for Python 2.6 compatibility
import distutils.ccompiler as _distutils_ccompiler
+import fnmatch as _fnmatch
try: # Python 2.7 and 3.x
import importlib as _importlib
except ImportError: # Python 2.6 and earlier
import os as _os
import platform as _platform
import re as _re
+import shlex as _shlex
import subprocess as _subprocess
import sys as _sys
+try: # Python 3.x
+ import urllib.parse as _urllib_parse
+except ImportError: # Python 2.x
+ import urllib as _urllib_parse # for quote()
+import xml.etree.ElementTree as _element_tree
+
+try:
+ import distro as _distro
+except ImportError:
+ _distro = None
+
+
+if not hasattr(_shlex, 'quote'): # Python versions older than 3.3
+ # Use the undocumented pipes.quote()
+ import pipes as _pipes
+ _shlex.quote = _pipes.quote
__version__ = '0.1'
# Version control
'git',
'hg', # Command line tool
- 'mercurial', # Python package
+ #'mercurial', # Python package
+ 'EasyMercurial',
# Build tools and packaging
'make',
- 'easy_install',
+ 'virtual-pypi-installer',
'setuptools',
+ #'xcode',
# Testing
'nosetests', # Command line tool
'nose', # Python package
+ 'py.test', # Command line tool
+ 'pytest', # Python package
# SQL
'sqlite3', # Command line tool
'sqlite3-python', # Python package
# Python
'python',
- 'IPython',
+ 'ipython', # Command line tool
+ 'IPython', # Python package
+ 'argparse', # Useful for utility scripts
'numpy',
'scipy',
'matplotlib',
- 'sympy',
- 'Cython',
- 'networkx',
- 'mayavi.mlab',
+ 'pandas',
+ #'sympy',
+ #'Cython',
+ #'networkx',
+ #'mayavi.mlab',
]
CHECKER = {}
+_ROOT_PATH = _os.sep
+if _platform.system() == 'win32':
+ _ROOT_PATH = 'c:\\'
+
+
+class InvalidCheck (KeyError):
+ def __init__(self, check):
+ super(InvalidCheck, self).__init__(check)
+ self.check = check
+
+ def __str__(self):
+ return self.check
+
class DependencyError (Exception):
+ _default_url = 'http://software-carpentry.org/setup/'
+ _setup_urls = { # (system, version, package) glob pairs
+ ('*', '*', 'Cython'): 'http://docs.cython.org/src/quickstart/install.html',
+ ('Linux', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-linux',
+ ('Darwin', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-mac',
+ ('Windows', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-windows',
+ ('*', '*', 'EasyMercurial'): 'http://easyhg.org/download.html',
+ ('*', '*', 'argparse'): 'https://pypi.python.org/pypi/argparse#installation',
+ ('*', '*', 'ash'): 'http://www.in-ulm.de/~mascheck/various/ash/',
+ ('*', '*', 'bash'): 'http://www.gnu.org/software/bash/manual/html_node/Basic-Installation.html#Basic-Installation',
+ ('Linux', '*', 'chromium'): 'http://code.google.com/p/chromium/wiki/LinuxBuildInstructions',
+ ('Darwin', '*', 'chromium'): 'http://code.google.com/p/chromium/wiki/MacBuildInstructions',
+ ('Windows', '*', 'chromium'): 'http://www.chromium.org/developers/how-tos/build-instructions-windows',
+ ('*', '*', 'chromium'): 'http://www.chromium.org/developers/how-tos',
+ ('Windows', '*', 'emacs'): 'http://www.gnu.org/software/emacs/windows/Installing-Emacs.html',
+ ('*', '*', 'emacs'): 'http://www.gnu.org/software/emacs/#Obtaining',
+ ('*', '*', 'firefox'): 'http://www.mozilla.org/en-US/firefox/new/',
+ ('Linux', '*', 'gedit'): 'http://www.linuxfromscratch.org/blfs/view/svn/gnome/gedit.html',
+ ('*', '*', 'git'): 'http://git-scm.com/downloads',
+ ('*', '*', 'google-chrome'): 'https://www.google.com/intl/en/chrome/browser/',
+ ('*', '*', 'hg'): 'http://mercurial.selenic.com/',
+ ('*', '*', 'mercurial'): 'http://mercurial.selenic.com/',
+ ('*', '*', 'IPython'): 'http://ipython.org/install.html',
+ ('*', '*', 'ipython'): 'http://ipython.org/install.html',
+ ('*', '*', 'jinja'): 'http://jinja.pocoo.org/docs/intro/#installation',
+ ('*', '*', 'kate'): 'http://kate-editor.org/get-it/',
+ ('*', '*', 'make'): 'http://www.gnu.org/software/make/',
+ ('Darwin', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#building-on-osx',
+ ('Windows', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#installing-on-windows',
+ ('*', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#installing',
+ ('*', '*', 'mayavi.mlab'): 'http://docs.enthought.com/mayavi/mayavi/installation.html',
+ ('*', '*', 'nano'): 'http://www.nano-editor.org/dist/latest/faq.html#3',
+ ('*', '*', 'networkx'): 'http://networkx.github.com/documentation/latest/install.html#installing',
+ ('*', '*', 'nose'): 'https://nose.readthedocs.org/en/latest/#installation-and-quick-start',
+ ('*', '*', 'nosetests'): 'https://nose.readthedocs.org/en/latest/#installation-and-quick-start',
+ ('*', '*', 'notepad++'): 'http://notepad-plus-plus.org/download/v6.3.html',
+ ('*', '*', 'numpy'): 'http://docs.scipy.org/doc/numpy/user/install.html',
+ ('*', '*', 'pandas'): 'http://pandas.pydata.org/pandas-docs/stable/install.html',
+ ('*', '*', 'pip'): 'http://www.pip-installer.org/en/latest/installing.html',
+ ('*', '*', 'pytest'): 'http://pytest.org/latest/getting-started.html',
+ ('*', '*', 'python'): 'http://www.python.org/download/releases/2.7.3/#download',
+ ('*', '*', 'pyzmq'): 'https://github.com/zeromq/pyzmq/wiki/Building-and-Installing-PyZMQ',
+ ('*', '*', 'py.test'): 'http://pytest.org/latest/getting-started.html',
+ ('Linux', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Linux',
+ ('Darwin', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Mac_OS_X',
+ ('Windows', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Windows',
+ ('*', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy',
+ ('*', '*', 'setuptools'): 'https://pypi.python.org/pypi/setuptools#installation-instructions',
+ ('*', '*', 'sqlite3'): 'http://www.sqlite.org/download.html',
+ ('*', '*', 'sublime-text'): 'http://www.sublimetext.com/2',
+ ('*', '*', 'sympy'): 'http://docs.sympy.org/dev/install.html',
+ ('Darwin', '*', 'textmate'): 'http://macromates.com/',
+ ('Darwin', '*', 'textwrangler'): 'http://www.barebones.com/products/textwrangler/download.html',
+ ('*', '*', 'tornado'): 'http://www.tornadoweb.org/',
+ ('*', '*', 'vim'): 'http://www.vim.org/download.php',
+ ('Darwin', '*', 'xcode'): 'https://developer.apple.com/xcode/',
+ ('*', '*', 'xemacs'): 'http://www.us.xemacs.org/Install/',
+ ('*', '*', 'zsh'): 'http://www.zsh.org/',
+ }
+
def _get_message(self):
return self._message
def _set_message(self, message):
self._message = message
message = property(_get_message, _set_message)
- def __init__(self, checker, message):
+ def __init__(self, checker, message, causes=None):
super(DependencyError, self).__init__(message)
self.checker = checker
self.message = message
+ if causes is None:
+ causes = []
+ self.causes = causes
+
+ def get_url(self):
+ system = _platform.system()
+ version = None
+ if system == 'Linux' and _distro:
+ version = _distro.id()
+ else:
+ for pversion in (
+ 'linux_distribution',
+ 'mac_ver',
+ 'win32_ver',
+ ):
+ value = getattr(_platform, pversion)()
+ if value[0]:
+ version = value[0]
+ break
+ if not version:
+ version = '*'
+ package = self.checker.name
+ for (s,v,p),url in self._setup_urls.items():
+ if (_fnmatch.fnmatch(system, s) and
+ _fnmatch.fnmatch(version, v) and
+ _fnmatch.fnmatch(package, p)):
+ return url
+ return self._default_url
def __str__(self):
- url = 'http://software-carpentry.org/setup/' # TODO: per-package URL
- return 'check for {0} failed:\n{1}\n{2}\n{3}'.format(
- self.checker.full_name(), self.message,
- 'For instructions on installing an up-to-date version, see',
- url)
+ url = self.get_url()
+ lines = [
+ 'check for {0} failed:'.format(self.checker.full_name()),
+ ' ' + self.message,
+ ' For instructions on installing an up-to-date version, see',
+ ' ' + url,
+ ]
+ if self.causes:
+ lines.append(' causes:')
+ for cause in self.causes:
+ lines.extend(' ' + line for line in str(cause).splitlines())
+ return '\n'.join(lines)
def check(checks=None):
if not checks:
checks = CHECKS
for check in checks:
- checker = CHECKER[check]
+ try:
+ checker = CHECKER[check]
+ except KeyError as e:
+ raise InvalidCheck(check)# from e
_sys.stdout.write('check {0}...\t'.format(checker.full_name()))
try:
version = checker.check()
for dependency in self.and_dependencies:
if not hasattr(dependency, 'check'):
dependency = CHECKER[dependency]
- dependency.check()
- self.or_pass = or_error = None
+ try:
+ dependency.check()
+ except DependencyError as e:
+ raise DependencyError(
+ checker=self,
+ message=(
+ 'some dependencies for {0} were not satisfied'
+ ).format(self.full_name()),
+ causes=[e])
+ self.or_pass = None
+ or_errors = []
for dependency in self.or_dependencies:
if not hasattr(dependency, 'check'):
dependency = CHECKER[dependency]
try:
version = dependency.check()
except DependencyError as e:
- or_error = e
+ or_errors.append(e)
else:
self.or_pass = {
'dependency': dependency,
}
break # no need to test other dependencies
if self.or_dependencies and not self.or_pass:
- raise or_error
+ raise DependencyError(
+ checker=self,
+ message=(
+ '{0} requires at least one of the following dependencies'
+ ).format(self.full_name()),
+ causes=or_errors)
def _check(self):
version = self._get_version()
def _get_version(self):
raise NotImplementedError(self)
+ def _minimum_version_string(self):
+ return self.version_delimiter.join(
+ str(part) for part in self.minimum_version)
+
def _check_version(self, version, parsed_version=None):
if not parsed_version:
parsed_version = self._parse_version(version=version)
raise DependencyError(
checker=self,
message='outdated version of {0}: {1} (need >= {2})'.format(
- self.full_name(), version,
- self.version_delimiter.join(
- str(part) for part in self.minimum_version)))
+ self.full_name(), version, self._minimum_version_string()))
def _parse_version(self, version):
if not version:
try:
parsed_version.append(int(part))
except ValueError as e:
- raise NotImplementedError((version, part))# from e
+ raise DependencyError(
+ checker=self,
+ message=(
+ 'unparsable {0!r} in version {1} of {2}, (need >= {3})'
+ ).format(
+ part, version, self.full_name(),
+ self._minimum_version_string()))# from e
return tuple(parsed_version)
-class PythonDependency (Dependency):
- def __init__(self, name='python', long_name='Python version',
- minimum_version=(2, 6), **kwargs):
- super(PythonDependency, self).__init__(
- name=name, long_name=long_name, minimum_version=minimum_version,
- **kwargs)
-
- def _get_version(self):
- return _sys.version
-
- def _get_parsed_version(self):
- return _sys.version_info
-
-
-CHECKER['python'] = PythonDependency()
+class VirtualDependency (Dependency):
+ def _check(self):
+ return '{0} {1}'.format(
+ self.or_pass['dependency'].full_name(),
+ self.or_pass['version'])
class CommandDependency (Dependency):
exe_extension = _distutils_ccompiler.new_compiler().exe_extension
- def __init__(self, command, version_option='--version',
- version_regexp=None, version_stream='stdout', **kwargs):
+ def __init__(self, command, paths=None, version_options=('--version',),
+ stdin=None, version_regexp=None, version_stream='stdout',
+ **kwargs):
if 'name' not in kwargs:
kwargs['name'] = command
super(CommandDependency, self).__init__(**kwargs)
self.command = command
- self.version_option = version_option
+ self.paths = paths
+ self.version_options = version_options
+ self.stdin = None
if not version_regexp:
regexp = r'([\d][\d{0}]*[\d])'.format(self.version_delimiter)
version_regexp = _re.compile(regexp)
self.version_regexp = version_regexp
self.version_stream = version_stream
- def _get_version_stream(self, expect=(0,)):
- command = self.command + (self.exe_extension or '')
+ def _get_command_version_stream(self, command=None, stdin=None,
+ expect=(0,)):
+ if command is None:
+ command = self.command + (self.exe_extension or '')
+ if not stdin:
+ stdin = self.stdin
+ if stdin:
+ popen_stdin = _subprocess.PIPE
+ else:
+ popen_stdin = None
try:
p = _subprocess.Popen(
- [command, self.version_option],
+ [command] + list(self.version_options), stdin=popen_stdin,
stdout=_subprocess.PIPE, stderr=_subprocess.PIPE,
- close_fds=True, shell=False, universal_newlines=True)
+ universal_newlines=True)
except OSError as e:
raise DependencyError(
checker=self,
message="could not find '{0}' executable".format(command),
)# from e
- stdout,stderr = p.communicate()
+ stdout,stderr = p.communicate(stdin)
status = p.wait()
if status not in expect:
lines = [
- "failed to execute '{0} {1}':".format(
- command, self.version_option),
+ "failed to execute: {0} {1}".format(
+ command,
+ ' '.join(_shlex.quote(arg)
+ for arg in self.version_options)),
'status: {0}'.format(status),
]
for name,string in [('stdout', stdout), ('stderr', stderr)]:
raise DependencyError(checker=self, message='\n'.join(lines))
for name,string in [('stdout', stdout), ('stderr', stderr)]:
if name == self.version_stream:
+ if not string:
+ raise DependencyError(
+ checker=self,
+ message='empty version stream on {0} for {1}'.format(
+ self.version_stream, command))
return string
raise NotImplementedError(self.version_stream)
+ def _get_version_stream(self, **kwargs):
+ paths = [self.command + (self.exe_extension or '')]
+ if self.exe_extension:
+ paths.append(self.command) # also look at the extension-less path
+ if self.paths:
+ paths.extend(self.paths)
+ or_errors = []
+ for path in paths:
+ try:
+ return self._get_command_version_stream(command=path, **kwargs)
+ except DependencyError as e:
+ or_errors.append(e)
+ raise DependencyError(
+ checker=self,
+ message='errors finding {0} version'.format(
+ self.full_name()),
+ causes=or_errors)
+
def _get_version(self):
version_stream = self._get_version_stream()
match = self.version_regexp.search(version_stream)
return match.group(1)
-for command,long_name,minimum_version in [
- ('sh', 'Bourne Shell', None),
- ('ash', 'Almquist Shell', None),
- ('bash', 'Bourne Again Shell', None),
- ('csh', 'C Shell', None),
- ('ksh', 'KornShell', None),
- ('dash', 'Debian Almquist Shell', None),
- ('tcsh', 'TENEX C Shell', None),
- ('zsh', 'Z Shell', None),
- ('git', 'Git', (1, 7, 0)),
- ('hg', 'Mercurial', (2, 0, 0)),
- ('make', None, None),
- ('sqlite3', 'SQLite 3', None),
- ('nosetests', 'Nose', (1, 0, 0)),
- ('emacs', 'Emacs', None),
- ('xemacs', 'XEmacs', None),
- ('vim', 'Vim', None),
- ('vi', None, None),
- ('nano', 'Nano', None),
- ('kate', 'Kate', None),
- ('notepad++', 'Notepad++', None),
- ('firefox', 'Firefox', None),
- ('google-chrome', 'Google Chrome', None),
- ('chromium', 'Chromium', None),
- ]:
- if not long_name:
- long_name = command
- CHECKER[command] = CommandDependency(
- command=command, long_name=long_name, minimum_version=minimum_version)
-del command, long_name, minimum_version # cleanup namespace
+class VersionPlistCommandDependency (CommandDependency):
+ """A command that doesn't support --version or equivalent options
+
+ On OS X, a command's executable may be hard to find, or not exist
+ in the PATH. Work around that by looking up the version
+ information in the package's version.plist file.
+ """
+ def __init__(self, key='CFBundleShortVersionString', **kwargs):
+ super(VersionPlistCommandDependency, self).__init__(**kwargs)
+ self.key = key
+
+ def _get_command_version_stream(self, *args, **kwargs):
+ raise NotImplementedError()
+
+ def _get_version_stream(self, *args, **kwargs):
+ raise NotImplementedError()
+
+ @staticmethod
+ def _get_parent(root, element):
+ """Returns the parent of this element or None for the root element
+ """
+ for node in root.iter():
+ if element in node:
+ return node
+ raise ValueError((root, element))
+
+ @classmethod
+ def _get_next(cls, root, element):
+ """Returns the following sibling of this element or None
+ """
+ parent = cls._get_parent(root=root, element=element)
+ siblings = iter(parent)
+ for node in siblings:
+ if node == element:
+ try:
+ return next(siblings)
+ except StopIteration:
+ return None
+ return None
+
+ def _get_version_from_plist(self, path):
+ """Parse the plist and return the value string for self.key
+ """
+ tree = _element_tree.parse(source=path)
+ data = {}
+ for key in tree.findall('.//key'):
+ value = self._get_next(root=tree, element=key)
+ if value.tag != 'string':
+ raise ValueError((tree, key, value))
+ data[key.text] = value.text
+ return data[self.key]
+
+ def _get_version(self):
+ for path in self.paths:
+ if _os.path.exists(path):
+ return self._get_version_from_plist(path=path)
+ raise DependencyError(
+ checker=self,
+ message=(
+ 'nothing exists at any of the expected paths for {0}:\n {1}'
+ ).format(
+ self.full_name(),
+ '\n '.join(p for p in self.paths)))
+
+
+class UserTaskDependency (Dependency):
+ "Prompt the user to complete a task and check for success"
+ def __init__(self, prompt, **kwargs):
+ super(UserTaskDependency, self).__init__(**kwargs)
+ self.prompt = prompt
+
+ def _check(self):
+ if _sys.version_info >= (3, ):
+ result = input(self.prompt)
+ else: # Python 2.x
+ result = raw_input(self.prompt)
+ return self._check_result(result)
+
+ def _check_result(self, result):
+ raise NotImplementedError()
+
+
+class EditorTaskDependency (UserTaskDependency):
+ def __init__(self, **kwargs):
+ self.path = _os.path.expanduser(_os.path.join(
+ '~', 'swc-installation-test.txt'))
+ self.contents = 'Hello, world!'
+ super(EditorTaskDependency, self).__init__(
+ prompt=(
+ 'Open your favorite text editor and create the file\n'
+ ' {0}\n'
+ 'containing the line:\n'
+ ' {1}\n'
+ 'Press enter here after you have done this.\n'
+ 'You may remove the file after you have finished testing.'
+ ).format(self.path, self.contents),
+ **kwargs)
+
+ def _check_result(self, result):
+ message = None
+ try:
+ with open(self.path, 'r') as f:
+ contents = f.read()
+ except IOError as e:
+ raise DependencyError(
+ checker=self,
+ message='could not open {0!r}: {1}'.format(self.path, e)
+ )# from e
+ if contents.strip() != self.contents:
+ raise DependencyError(
+ checker=self,
+ message=(
+ 'file contents ({0!r}) did not match the expected {1!r}'
+ ).format(contents, self.contents))
+
+
+class MakeDependency (CommandDependency):
+ makefile = '\n'.join([
+ 'all:',
+ '\t@echo "MAKE_VERSION=$(MAKE_VERSION)"',
+ '\t@echo "MAKE=$(MAKE)"',
+ '',
+ ])
+
+ def _get_version(self):
+ try:
+ return super(MakeDependency, self)._get_version()
+ except DependencyError as e:
+ version_options = self.version_options
+ self.version_options = ['-f', '-']
+ try:
+ stream = self._get_version_stream(stdin=self.makefile)
+ info = {}
+ for line in stream.splitlines():
+ try:
+ key,value = line.split('=', 1)
+ except ValueError as ve:
+ raise e# from NotImplementedError(stream)
+ info[key] = value
+ if info.get('MAKE_VERSION', None):
+ return info['MAKE_VERSION']
+ elif info.get('MAKE', None):
+ return None
+ raise e
+ finally:
+ self.version_options = version_options
class EasyInstallDependency (CommandDependency):
self.version_stream = version_stream
-CHECKER['easy_install'] = EasyInstallDependency(
- command='easy_install', long_name='Setuptools easy_install',
- minimum_version=None)
+class PythonDependency (Dependency):
+ def __init__(self, name='python', long_name='Python version',
+ minimum_version=(2, 6), **kwargs):
+ super(PythonDependency, self).__init__(
+ name=name, long_name=long_name, minimum_version=minimum_version,
+ **kwargs)
+
+ def _get_version(self):
+ return _sys.version
+
+ def _get_parsed_version(self):
+ return _sys.version_info
class PythonPackageDependency (Dependency):
return version
-for package,name,long_name,minimum_version in [
- ('nose', None, 'Nose Python package',
- CHECKER['nosetests'].minimum_version),
- ('IPython', None, None, None),
- ('numpy', None, 'NumPy', None),
- ('scipy', None, 'SciPy', None),
- ('matplotlib', None, 'Matplotlib', None),
- ('sympy', None, 'SymPy', None),
- ('Cython', None, None, None),
- ('networkx', None, 'NetworkX', None),
- ('mayavi.mlab', None, 'MayaVi', None),
- ('setuptools', None, 'Setuptools', None),
- ]:
- if not name:
- name = package
- if not long_name:
- long_name = name
- CHECKER[name] = PythonPackageDependency(
- package=package, name=name, long_name=long_name,
- minimum_version=minimum_version)
-del package, name, long_name, minimum_version # cleanup namespace
-
-
class MercurialPythonPackage (PythonPackageDependency):
def _get_version(self):
try: # mercurial >= 1.2
return package.version()
-CHECKER['mercurial'] = MercurialPythonPackage(
- package='mercurial.util', name='mercurial',
- long_name='Mercurial Python package',
- minimum_version=CHECKER['hg'].minimum_version)
+class TornadoPythonPackage (PythonPackageDependency):
+ def _get_version_from_package(self, package):
+ return package.version
+
+ def _get_parsed_version(self):
+ package = self._get_package(self.package)
+ return package.version_info
class SQLitePythonPackage (PythonPackageDependency):
return _sys.version_info
+def _program_files_paths(*args):
+ "Utility for generating MS Windows search paths"
+ pf = _os.environ.get('ProgramFiles', '/usr/bin')
+ pfx86 = _os.environ.get('ProgramFiles(x86)', pf)
+ paths = [_os.path.join(pf, *args)]
+ if pfx86 != pf:
+ paths.append(_os.path.join(pfx86, *args))
+ return paths
+
+
+CHECKER['python'] = PythonDependency()
+
+
+for command,long_name,minimum_version,paths in [
+ ('sh', 'Bourne Shell', None, None),
+ ('ash', 'Almquist Shell', None, None),
+ ('bash', 'Bourne Again Shell', None, None),
+ ('csh', 'C Shell', None, None),
+ ('ksh', 'KornShell', None, None),
+ ('dash', 'Debian Almquist Shell', None, None),
+ ('tcsh', 'TENEX C Shell', None, None),
+ ('zsh', 'Z Shell', None, None),
+ ('git', 'Git', (1, 7, 0), None),
+ ('hg', 'Mercurial', (2, 0, 0), None),
+ ('EasyMercurial', None, (1, 3), None),
+ ('pip', None, None, None),
+ ('sqlite3', 'SQLite 3', None, None),
+ ('nosetests', 'Nose', (1, 0, 0), None),
+ ('ipython', 'IPython script', (1, 0), None),
+ ('emacs', 'Emacs', None, None),
+ ('xemacs', 'XEmacs', None, None),
+ ('vim', 'Vim', None, None),
+ ('vi', None, None, None),
+ ('nano', 'Nano', None, None),
+ ('gedit', None, None, None),
+ ('kate', 'Kate', None, None),
+ ('notepad++', 'Notepad++', None,
+ _program_files_paths('Notepad++', 'notepad++.exe')),
+ ('firefox', 'Firefox', None,
+ _program_files_paths('Mozilla Firefox', 'firefox.exe')),
+ ('google-chrome', 'Google Chrome', None,
+ _program_files_paths('Google', 'Chrome', 'Application', 'chrome.exe')
+ ),
+ ('chromium', 'Chromium', None, None),
+ ]:
+ if not long_name:
+ long_name = command
+ CHECKER[command] = CommandDependency(
+ command=command, paths=paths, long_name=long_name,
+ minimum_version=minimum_version)
+del command, long_name, minimum_version, paths # cleanup namespace
+
+
+CHECKER['make'] = MakeDependency(command='make', minimum_version=None)
+
+
+CHECKER['easy_install'] = EasyInstallDependency(
+ command='easy_install', long_name='Setuptools easy_install',
+ minimum_version=None)
+
+
+CHECKER['py.test'] = CommandDependency(
+ command='py.test', version_stream='stderr',
+ minimum_version=None)
+
+
+for paths,name,long_name in [
+ ([_os.path.join(_ROOT_PATH, 'Applications', 'Sublime Text 2.app',
+ 'Contents', 'version.plist')],
+ 'sublime-text', 'Sublime Text'),
+ ([_os.path.join(_ROOT_PATH, 'Applications', 'TextMate.app',
+ 'Contents', 'version.plist')],
+ 'textmate', 'TextMate'),
+ ([_os.path.join(_ROOT_PATH, 'Applications', 'TextWrangler.app',
+ 'Contents', 'version.plist')],
+ 'textwrangler', 'TextWrangler'),
+ ([_os.path.join(_ROOT_PATH, 'Applications', 'Safari.app',
+ 'Contents', 'version.plist')],
+ 'safari', 'Safari'),
+ ([_os.path.join(_ROOT_PATH, 'Applications', 'Xcode.app',
+ 'Contents', 'version.plist'), # OS X >=1.7
+ _os.path.join(_ROOT_PATH, 'Developer', 'Applications', 'Xcode.app',
+ 'Contents', 'version.plist'), # OS X 1.6,
+ ],
+ 'xcode', 'Xcode'),
+ ]:
+ if not long_name:
+ long_name = name
+ CHECKER[name] = VersionPlistCommandDependency(
+ command=None, paths=paths, name=name, long_name=long_name)
+del paths, name, long_name # cleanup namespace
+
+
+for package,name,long_name,minimum_version,and_dependencies in [
+ ('nose', None, 'Nose Python package',
+ CHECKER['nosetests'].minimum_version, None),
+ ('pytest', None, 'pytest Python package',
+ CHECKER['py.test'].minimum_version, None),
+ ('jinja2', 'jinja', 'Jinja', (2, 6), None),
+ ('zmq', 'pyzmq', 'PyZMQ', (2, 1, 4), None),
+ ('IPython', None, 'IPython Python package',
+ CHECKER['ipython'].minimum_version, [
+ 'jinja',
+ 'tornado',
+ 'pyzmq',
+ VirtualDependency(
+ name='virtual-browser-ipython',
+ long_name='IPython-compatible web browser',
+ or_dependencies=[
+ CommandDependency(
+ command=CHECKER['firefox'].command,
+ paths=CHECKER['firefox'].paths,
+ name='{0}-for-ipython'.format(
+ CHECKER['firefox'].name),
+ long_name='{0} for IPython'.format(
+ CHECKER['firefox'].long_name),
+ minimum_version=(6, 0)),
+ CommandDependency(
+ command=CHECKER['google-chrome'].command,
+ paths=CHECKER['google-chrome'].paths,
+ name='{0}-for-ipython'.format(
+ CHECKER['google-chrome'].name),
+ long_name='{0} for IPython'.format(
+ CHECKER['google-chrome'].long_name),
+ minimum_version=(13, 0)),
+ CommandDependency(
+ command=CHECKER['chromium'].command,
+ paths=CHECKER['chromium'].paths,
+ name='{0}-for-ipython'.format(
+ CHECKER['chromium'].name),
+ long_name='{0} for IPython'.format(
+ CHECKER['chromium'].long_name),
+ minimum_version=(13, 0)),
+ VersionPlistCommandDependency(
+ command=CHECKER['safari'].command,
+ paths=CHECKER['safari'].paths,
+ key=CHECKER['safari'].key,
+ name='{0}-for-ipython'.format(
+ CHECKER['safari'].name),
+ long_name='{0} for IPython'.format(
+ CHECKER['safari'].long_name),
+ minimum_version=(5, 0)),
+ ]),
+ ]),
+ ('argparse', None, 'Argparse', None, None),
+ ('numpy', None, 'NumPy', None, None),
+ ('scipy', None, 'SciPy', None, None),
+ ('matplotlib', None, 'Matplotlib', None, None),
+ ('pandas', None, 'Pandas', (0, 8), None),
+ ('sympy', None, 'SymPy', None, None),
+ ('Cython', None, None, None, None),
+ ('networkx', None, 'NetworkX', None, None),
+ ('mayavi.mlab', None, 'MayaVi', None, None),
+ ('setuptools', None, 'Setuptools', None, None),
+ ]:
+ if not name:
+ name = package
+ if not long_name:
+ long_name = name
+ kwargs = {}
+ if and_dependencies:
+ kwargs['and_dependencies'] = and_dependencies
+ CHECKER[name] = PythonPackageDependency(
+ package=package, name=name, long_name=long_name,
+ minimum_version=minimum_version, **kwargs)
+# cleanup namespace
+del package, name, long_name, minimum_version, and_dependencies, kwargs
+
+
+CHECKER['mercurial'] = MercurialPythonPackage(
+ package='mercurial.util', name='mercurial',
+ long_name='Mercurial Python package',
+ minimum_version=CHECKER['hg'].minimum_version)
+
+
+CHECKER['tornado'] = TornadoPythonPackage(
+ package='tornado', name='tornado', long_name='Tornado', minimum_version=(2, 0))
+
+
CHECKER['sqlite3-python'] = SQLitePythonPackage(
package='sqlite3', name='sqlite3-python',
long_name='SQLite Python package',
minimum_version=CHECKER['sqlite3'].minimum_version)
-class VirtualDependency (Dependency):
- def _check(self):
- return '{0} {1}'.format(
- self.or_pass['dependency'].full_name(),
- self.or_pass['version'])
+CHECKER['other-editor'] = EditorTaskDependency(
+ name='other-editor', long_name='')
-for name,dependencies in [
- ('virtual-shell', (
+for name,long_name,dependencies in [
+ ('virtual-shell', 'command line shell', (
'bash',
'dash',
'ash',
'tcsh',
'sh',
)),
- ('virtual-editor', (
+ ('virtual-editor', 'text/code editor', (
'emacs',
'xemacs',
'vim',
'vi',
'nano',
+ 'gedit',
'kate',
'notepad++',
+ 'sublime-text',
+ 'textmate',
+ 'textwrangler',
+ 'other-editor', # last because it requires user interaction
)),
- ('virtual-browser', (
+ ('virtual-browser', 'web browser', (
'firefox',
'google-chrome',
'chromium',
+ 'safari',
+ )),
+ ('virtual-pypi-installer', 'PyPI installer', (
+ 'pip',
+ 'easy_install',
)),
]:
CHECKER[name] = VirtualDependency(
- name=name, long_name=name, or_dependencies=dependencies)
-del name, dependencies # cleanup namespace
+ name=name, long_name=long_name, or_dependencies=dependencies)
+del name, long_name, dependencies # cleanup namespace
-def _print_info(key, value, indent=13):
+def _print_info(key, value, indent=19):
print('{0}{1}: {2}'.format(key, ' '*(indent-len(key)), value))
def print_system_info():
print('System information')
print('==================')
_print_info('os.name', _os.name)
- try:
- _print_info('os.uname', _os.uname())
- except:
- pass
+ _print_info('os.uname', _platform.uname())
_print_info('platform', _sys.platform)
_print_info('platform+', _platform.platform())
+ system = _platform.system()
+ if system == 'Linux' and _distro:
+ _print_info(
+ 'linux_distribution',
+ _distro.name(pretty=True) or _distro.linux_distribution())
+ else:
+ for pversion in (
+ 'linux_distribution',
+ 'mac_ver',
+ 'win32_ver',
+ ):
+ value = getattr(_platform, pversion)()
+ if value[0]:
+ _print_info(pversion, value)
_print_info('prefix', _sys.prefix)
_print_info('exec_prefix', _sys.exec_prefix)
_print_info('executable', _sys.executable)
if __name__ == '__main__':
- if not check(_sys.argv[1:]):
- print()
- print_system_info()
- print_suggestions(instructor_fallback=True)
+ import optparse as _optparse
+
+ parser = _optparse.OptionParser(usage='%prog [options] [check...]')
+ epilog = __doc__
+ parser.format_epilog = lambda formatter: '\n' + epilog
+ parser.add_option(
+ '-v', '--verbose', action='store_true',
+ help=('print additional information to help troubleshoot '
+ 'installation issues'))
+ options,args = parser.parse_args()
+ try:
+ passed = check(args)
+ except InvalidCheck as e:
+ print("I don't know how to check for {0!r}".format(e.check))
+ print('I do know how to check for:')
+ for key,checker in sorted(CHECKER.items()):
+ if checker.long_name != checker.name:
+ print(' {0} {1}({2})'.format(
+ key, ' '*(20-len(key)), checker.long_name))
+ else:
+ print(' {0}'.format(key))
+ _sys.exit(1)
+ if not passed:
+ if options.verbose:
+ print()
+ print_system_info()
+ print_suggestions(instructor_fallback=True)
+ _sys.exit(1)