#!/usr/bin/env python """Test script to check for required functionality. Execute this code at the command line by typing: python swc-installation-test-2.py Run the script and follow the instructions it prints at the end. 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 class _Importlib (object): """Minimal workarounds for functions we need """ @staticmethod def import_module(name): module = __import__(name) for n in name.split('.')[1:]: module = getattr(module, n) return module _importlib = _Importlib() import logging as _logging 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 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' # Comment out any entries you don't need CHECKS = [ # Shell 'virtual-shell', # Editors 'virtual-editor', # Browsers 'virtual-browser', # Version control 'git', # 'hg', # Command line tool #'mercurial', # Python package # 'EasyMercurial', # Build tools and packaging # 'make', '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', # Command line tool 'IPython', # Python package 'argparse', # Useful for utility scripts 'numpy', # 'scipy', 'matplotlib', # '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, 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 for pversion in ( 'linux_distribution', 'mac_ver', 'win32_ver', ): value = getattr(_platform, pversion)() if value[0]: version = value[0] break 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 = 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): successes = [] failures = [] if not checks: checks = CHECKS for check in checks: 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() except DependencyError as e: failures.append(e) _sys.stdout.write('fail\n') else: _sys.stdout.write('pass\n') successes.append((checker, version)) if successes: print('\nSuccesses:\n') for checker,version in successes: print('{0} {1}'.format( checker.full_name(), version or 'unknown')) if failures: print('\nFailures:') printed = [] for failure in failures: if failure not in printed: print() print(failure) printed.append(failure) return False return True class Dependency (object): def __init__(self, name, long_name=None, minimum_version=None, version_delimiter='.', and_dependencies=None, or_dependencies=None): self.name = name self.long_name = long_name or name self.minimum_version = minimum_version self.version_delimiter = version_delimiter if not and_dependencies: and_dependencies = [] self.and_dependencies = and_dependencies if not or_dependencies: or_dependencies = [] self.or_dependencies = or_dependencies self._check_error = None def __str__(self): return '<{0} {1}>'.format(type(self).__name__, self.name) def full_name(self): if self.name == self.long_name: return self.name else: return '{0} ({1})'.format(self.long_name, self.name) def check(self): if self._check_error: raise self._check_error try: self._check_dependencies() return self._check() except DependencyError as e: self._check_error = e # cache for future calls raise def _check_dependencies(self): for dependency in self.and_dependencies: if not hasattr(dependency, 'check'): dependency = CHECKER[dependency] 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_errors.append(e) else: self.or_pass = { 'dependency': dependency, 'version': version, } break # no need to test other dependencies if self.or_dependencies and not self.or_pass: 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() parsed_version = None if hasattr(self, '_get_parsed_version'): parsed_version = self._get_parsed_version() if self.minimum_version: self._check_version(version=version, parsed_version=parsed_version) return 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) if not parsed_version or parsed_version < self.minimum_version: raise DependencyError( checker=self, message='outdated version of {0}: {1} (need >= {2})'.format( self.full_name(), version, self._minimum_version_string())) def _parse_version(self, version): if not version: return None parsed_version = [] for part in version.split(self.version_delimiter): try: parsed_version.append(int(part)) except ValueError as 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 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, 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.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_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] + list(self.version_options), stdin=popen_stdin, stdout=_subprocess.PIPE, stderr=_subprocess.PIPE, 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(stdin) status = p.wait() if status not in expect: lines = [ "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)]: if string: lines.extend([name + ':', string]) 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) if not match: raise DependencyError( checker=self, message='no version string in output:\n{0}'.format( version_stream)) return match.group(1) 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): def _get_version(self): try: return super(EasyInstallDependency, self)._get_version() except DependencyError as e: version_stream = self.version_stream try: self.version_stream = 'stderr' stream = self._get_version_stream(expect=(1,)) if 'option --version not recognized' in stream: return 'unknown (possibly Setuptools?)' finally: self.version_stream = version_stream 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): def __init__(self, package, **kwargs): if 'name' not in kwargs: kwargs['name'] = package if 'and_dependencies' not in kwargs: kwargs['and_dependencies'] = [] if 'python' not in kwargs['and_dependencies']: kwargs['and_dependencies'].append('python') super(PythonPackageDependency, self).__init__(**kwargs) self.package = package def _get_version(self): package = self._get_package(self.package) return self._get_version_from_package(package) def _get_package(self, package): try: return _importlib.import_module(package) except ImportError as e: raise DependencyError( checker=self, message="could not import the '{0}' package for {1}".format( package, self.full_name()), )# from e def _get_version_from_package(self, package): try: version = package.__version__ except AttributeError: version = None return version class MercurialPythonPackage (PythonPackageDependency): def _get_version(self): try: # mercurial >= 1.2 package = _importlib.import_module('mercurial.util') except ImportError as e: # mercurial <= 1.1.2 package = self._get_package('mercurial.version') return package.get_version() else: return package.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): def _get_version_from_package(self, package): return _sys.version def _get_parsed_version(self): 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) CHECKER['other-editor'] = EditorTaskDependency( name='other-editor', long_name='') for name,long_name,dependencies in [ ('virtual-shell', 'command line shell', ( 'bash', 'dash', 'ash', 'zsh', 'ksh', 'csh', 'tcsh', 'sh', )), ('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', 'web browser', ( 'firefox', 'google-chrome', 'chromium', 'safari', )), ('virtual-pypi-installer', 'PyPI installer', ( 'pip', 'easy_install', )), ]: CHECKER[name] = VirtualDependency( name=name, long_name=long_name, or_dependencies=dependencies) del name, long_name, dependencies # cleanup namespace def _print_info(key, value, indent=19): print('{0}{1}: {2}'.format(key, ' '*(indent-len(key)), value)) def print_system_info(): print("If you do not understand why the above failures occurred,") print("copy and send the *entire* output (all info above and summary") print("below) to the instructor for help.") print() print('==================') print('System information') print('==================') _print_info('os.name', _os.name) _print_info('os.uname', _platform.uname()) _print_info('platform', _sys.platform) _print_info('platform+', _platform.platform()) 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) _print_info('version_info', _sys.version_info) _print_info('version', _sys.version) _print_info('environment', '') for key,value in sorted(_os.environ.items()): print(' {0}={1}'.format(key, value)) print('==================') def print_suggestions(instructor_fallback=True): print() print('For suggestions on installing missing packages, see') print('http://software-carpentry.org/setup/') print('') print('For instructings on installing a particular package,') print('see the failure message for that package printed above.') if instructor_fallback: print('') print('For help, email the *entire* output of this script to') print('your instructor.') if __name__ == '__main__': 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)