3 """Test script to check for required functionality.
5 Execute this code at the command line by typing:
7 python swc-installation-test-2.py
9 Run the script and follow the instructions it prints at the end.
11 This script requires at least Python 2.6. You can check the version
12 of Python that you have installed with 'swc-installation-test-1.py'.
14 By default, this script will test for all the dependencies your
15 instructor thinks you need. If you want to test for a different set
16 of packages, you can list them on the command line. For example:
18 python swc-installation-test-2.py git virtual-editor
20 This is useful if the original test told you to install a more recent
21 version of a particular dependency, and you just want to re-test that
25 from __future__ import print_function # for Python 2.6 compatibility
27 import distutils.ccompiler as _distutils_ccompiler
28 import fnmatch as _fnmatch
29 try: # Python 2.7 and 3.x
30 import importlib as _importlib
31 except ImportError: # Python 2.6 and earlier
32 class _Importlib (object):
33 """Minimal workarounds for functions we need
36 def import_module(name):
37 module = __import__(name)
38 for n in name.split('.')[1:]:
39 module = getattr(module, n)
41 _importlib = _Importlib()
42 import logging as _logging
44 import platform as _platform
46 import shlex as _shlex
47 import subprocess as _subprocess
50 import urllib.parse as _urllib_parse
51 except ImportError: # Python 2.x
52 import urllib as _urllib_parse # for quote()
53 import xml.etree.ElementTree as _element_tree
56 if not hasattr(_shlex, 'quote'): # Python versions older than 3.3
57 # Use the undocumented pipes.quote()
58 import pipes as _pipes
59 _shlex.quote = _pipes.quote
64 # Comment out any entries you don't need
74 'hg', # Command line tool
75 #'mercurial', # Python package
77 # Build tools and packaging
79 'virtual-pypi-installer',
83 'nosetests', # Command line tool
84 'nose', # Python package
85 'py.test', # Command line tool
86 'pytest', # Python package
88 'sqlite3', # Command line tool
89 'sqlite3-python', # Python package
92 'ipython', # Command line tool
93 'IPython', # Python package
94 'argparse', # Useful for utility scripts
108 if _platform.system() == 'win32':
112 class InvalidCheck (KeyError):
113 def __init__(self, check):
114 super(InvalidCheck, self).__init__(check)
121 class DependencyError (Exception):
122 _default_url = 'http://software-carpentry.org/setup/'
123 _setup_urls = { # (system, version, package) glob pairs
124 ('*', '*', 'Cython'): 'http://docs.cython.org/src/quickstart/install.html',
125 ('Linux', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-linux',
126 ('Darwin', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-mac',
127 ('Windows', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-windows',
128 ('*', '*', 'EasyMercurial'): 'http://easyhg.org/download.html',
129 ('*', '*', 'argparse'): 'https://pypi.python.org/pypi/argparse#installation',
130 ('*', '*', 'ash'): 'http://www.in-ulm.de/~mascheck/various/ash/',
131 ('*', '*', 'bash'): 'http://www.gnu.org/software/bash/manual/html_node/Basic-Installation.html#Basic-Installation',
132 ('Linux', '*', 'chromium'): 'http://code.google.com/p/chromium/wiki/LinuxBuildInstructions',
133 ('Darwin', '*', 'chromium'): 'http://code.google.com/p/chromium/wiki/MacBuildInstructions',
134 ('Windows', '*', 'chromium'): 'http://www.chromium.org/developers/how-tos/build-instructions-windows',
135 ('*', '*', 'chromium'): 'http://www.chromium.org/developers/how-tos',
136 ('Windows', '*', 'emacs'): 'http://www.gnu.org/software/emacs/windows/Installing-Emacs.html',
137 ('*', '*', 'emacs'): 'http://www.gnu.org/software/emacs/#Obtaining',
138 ('*', '*', 'firefox'): 'http://www.mozilla.org/en-US/firefox/new/',
139 ('Linux', '*', 'gedit'): 'http://www.linuxfromscratch.org/blfs/view/svn/gnome/gedit.html',
140 ('*', '*', 'git'): 'http://git-scm.com/downloads',
141 ('*', '*', 'google-chrome'): 'https://www.google.com/intl/en/chrome/browser/',
142 ('*', '*', 'hg'): 'http://mercurial.selenic.com/',
143 ('*', '*', 'mercurial'): 'http://mercurial.selenic.com/',
144 ('*', '*', 'IPython'): 'http://ipython.org/install.html',
145 ('*', '*', 'ipython'): 'http://ipython.org/install.html',
146 ('*', '*', 'jinja'): 'http://jinja.pocoo.org/docs/intro/#installation',
147 ('*', '*', 'kate'): 'http://kate-editor.org/get-it/',
148 ('*', '*', 'make'): 'http://www.gnu.org/software/make/',
149 ('Darwin', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#building-on-osx',
150 ('Windows', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#installing-on-windows',
151 ('*', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#installing',
152 ('*', '*', 'mayavi.mlab'): 'http://docs.enthought.com/mayavi/mayavi/installation.html',
153 ('*', '*', 'nano'): 'http://www.nano-editor.org/dist/latest/faq.html#3',
154 ('*', '*', 'networkx'): 'http://networkx.github.com/documentation/latest/install.html#installing',
155 ('*', '*', 'nose'): 'https://nose.readthedocs.org/en/latest/#installation-and-quick-start',
156 ('*', '*', 'nosetests'): 'https://nose.readthedocs.org/en/latest/#installation-and-quick-start',
157 ('*', '*', 'notepad++'): 'http://notepad-plus-plus.org/download/v6.3.html',
158 ('*', '*', 'numpy'): 'http://docs.scipy.org/doc/numpy/user/install.html',
159 ('*', '*', 'pandas'): 'http://pandas.pydata.org/pandas-docs/stable/install.html',
160 ('*', '*', 'pip'): 'http://www.pip-installer.org/en/latest/installing.html',
161 ('*', '*', 'pytest'): 'http://pytest.org/latest/getting-started.html',
162 ('*', '*', 'python'): 'http://www.python.org/download/releases/2.7.3/#download',
163 ('*', '*', 'pyzmq'): 'https://github.com/zeromq/pyzmq/wiki/Building-and-Installing-PyZMQ',
164 ('*', '*', 'py.test'): 'http://pytest.org/latest/getting-started.html',
165 ('Linux', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Linux',
166 ('Darwin', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Mac_OS_X',
167 ('Windows', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Windows',
168 ('*', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy',
169 ('*', '*', 'setuptools'): 'https://pypi.python.org/pypi/setuptools#installation-instructions',
170 ('*', '*', 'sqlite3'): 'http://www.sqlite.org/download.html',
171 ('*', '*', 'sublime-text'): 'http://www.sublimetext.com/2',
172 ('*', '*', 'sympy'): 'http://docs.sympy.org/dev/install.html',
173 ('Darwin', '*', 'textmate'): 'http://macromates.com/',
174 ('Darwin', '*', 'textwrangler'): 'http://www.barebones.com/products/textwrangler/download.html',
175 ('*', '*', 'tornado'): 'http://www.tornadoweb.org/',
176 ('*', '*', 'vim'): 'http://www.vim.org/download.php',
177 ('Darwin', '*', 'xcode'): 'https://developer.apple.com/xcode/',
178 ('*', '*', 'xemacs'): 'http://www.us.xemacs.org/Install/',
179 ('*', '*', 'zsh'): 'http://www.zsh.org/',
182 def _get_message(self):
184 def _set_message(self, message):
185 self._message = message
186 message = property(_get_message, _set_message)
188 def __init__(self, checker, message, causes=None):
189 super(DependencyError, self).__init__(message)
190 self.checker = checker
191 self.message = message
197 system = _platform.system()
200 'linux_distribution',
204 value = getattr(_platform, pversion)()
208 package = self.checker.name
209 for (s,v,p),url in self._setup_urls.items():
210 if (_fnmatch.fnmatch(system, s) and
211 _fnmatch.fnmatch(version, v) and
212 _fnmatch.fnmatch(package, p)):
214 return self._default_url
219 'check for {0} failed:'.format(self.checker.full_name()),
221 ' For instructions on installing an up-to-date version, see',
225 lines.append(' causes:')
226 for cause in self.causes:
227 lines.extend(' ' + line for line in str(cause).splitlines())
228 return '\n'.join(lines)
231 def check(checks=None):
238 checker = CHECKER[check]
239 except KeyError as e:
240 raise InvalidCheck(check)# from e
241 _sys.stdout.write('check {0}...\t'.format(checker.full_name()))
243 version = checker.check()
244 except DependencyError as e:
246 _sys.stdout.write('fail\n')
248 _sys.stdout.write('pass\n')
249 successes.append((checker, version))
251 print('\nSuccesses:\n')
252 for checker,version in successes:
253 print('{0} {1}'.format(
255 version or 'unknown'))
259 for failure in failures:
260 if failure not in printed:
263 printed.append(failure)
268 class Dependency (object):
269 def __init__(self, name, long_name=None, minimum_version=None,
270 version_delimiter='.', and_dependencies=None,
271 or_dependencies=None):
273 self.long_name = long_name or name
274 self.minimum_version = minimum_version
275 self.version_delimiter = version_delimiter
276 if not and_dependencies:
277 and_dependencies = []
278 self.and_dependencies = and_dependencies
279 if not or_dependencies:
281 self.or_dependencies = or_dependencies
282 self._check_error = None
285 return '<{0} {1}>'.format(type(self).__name__, self.name)
288 if self.name == self.long_name:
291 return '{0} ({1})'.format(self.long_name, self.name)
294 if self._check_error:
295 raise self._check_error
297 self._check_dependencies()
299 except DependencyError as e:
300 self._check_error = e # cache for future calls
303 def _check_dependencies(self):
304 for dependency in self.and_dependencies:
305 if not hasattr(dependency, 'check'):
306 dependency = CHECKER[dependency]
309 except DependencyError as e:
310 raise DependencyError(
313 'some dependencies for {0} were not satisfied'
314 ).format(self.full_name()),
318 for dependency in self.or_dependencies:
319 if not hasattr(dependency, 'check'):
320 dependency = CHECKER[dependency]
322 version = dependency.check()
323 except DependencyError as e:
327 'dependency': dependency,
330 break # no need to test other dependencies
331 if self.or_dependencies and not self.or_pass:
332 raise DependencyError(
335 '{0} requires at least one of the following dependencies'
336 ).format(self.full_name()),
340 version = self._get_version()
341 parsed_version = None
342 if hasattr(self, '_get_parsed_version'):
343 parsed_version = self._get_parsed_version()
344 if self.minimum_version:
345 self._check_version(version=version, parsed_version=parsed_version)
348 def _get_version(self):
349 raise NotImplementedError(self)
351 def _minimum_version_string(self):
352 return self.version_delimiter.join(
353 str(part) for part in self.minimum_version)
355 def _check_version(self, version, parsed_version=None):
356 if not parsed_version:
357 parsed_version = self._parse_version(version=version)
358 if not parsed_version or parsed_version < self.minimum_version:
359 raise DependencyError(
361 message='outdated version of {0}: {1} (need >= {2})'.format(
362 self.full_name(), version, self._minimum_version_string()))
364 def _parse_version(self, version):
368 for part in version.split(self.version_delimiter):
370 parsed_version.append(int(part))
371 except ValueError as e:
372 raise DependencyError(
375 'unparsable {0!r} in version {1} of {2}, (need >= {3})'
377 part, version, self.full_name(),
378 self._minimum_version_string()))# from e
379 return tuple(parsed_version)
382 class VirtualDependency (Dependency):
384 return '{0} {1}'.format(
385 self.or_pass['dependency'].full_name(),
386 self.or_pass['version'])
389 class CommandDependency (Dependency):
390 exe_extension = _distutils_ccompiler.new_compiler().exe_extension
392 def __init__(self, command, paths=None, version_options=('--version',),
393 stdin=None, version_regexp=None, version_stream='stdout',
395 if 'name' not in kwargs:
396 kwargs['name'] = command
397 super(CommandDependency, self).__init__(**kwargs)
398 self.command = command
400 self.version_options = version_options
402 if not version_regexp:
403 regexp = r'([\d][\d{0}]*[\d])'.format(self.version_delimiter)
404 version_regexp = _re.compile(regexp)
405 self.version_regexp = version_regexp
406 self.version_stream = version_stream
408 def _get_command_version_stream(self, command=None, stdin=None,
411 command = self.command + (self.exe_extension or '')
415 popen_stdin = _subprocess.PIPE
419 p = _subprocess.Popen(
420 [command] + list(self.version_options), stdin=popen_stdin,
421 stdout=_subprocess.PIPE, stderr=_subprocess.PIPE,
422 universal_newlines=True)
424 raise DependencyError(
426 message="could not find '{0}' executable".format(command),
428 stdout,stderr = p.communicate(stdin)
430 if status not in expect:
432 "failed to execute: {0} {1}".format(
434 ' '.join(_shlex.quote(arg)
435 for arg in self.version_options)),
436 'status: {0}'.format(status),
438 for name,string in [('stdout', stdout), ('stderr', stderr)]:
440 lines.extend([name + ':', string])
441 raise DependencyError(checker=self, message='\n'.join(lines))
442 for name,string in [('stdout', stdout), ('stderr', stderr)]:
443 if name == self.version_stream:
445 raise DependencyError(
447 message='empty version stream on {0} for {1}'.format(
448 self.version_stream, command))
450 raise NotImplementedError(self.version_stream)
452 def _get_version_stream(self, **kwargs):
453 paths = [self.command + (self.exe_extension or '')]
454 if self.exe_extension:
455 paths.append(self.command) # also look at the extension-less path
457 paths.extend(self.paths)
461 return self._get_command_version_stream(command=path, **kwargs)
462 except DependencyError as e:
464 raise DependencyError(
466 message='errors finding {0} version'.format(
470 def _get_version(self):
471 version_stream = self._get_version_stream()
472 match = self.version_regexp.search(version_stream)
474 raise DependencyError(
476 message='no version string in output:\n{0}'.format(
478 return match.group(1)
481 class VersionPlistCommandDependency (CommandDependency):
482 """A command that doesn't support --version or equivalent options
484 On OS X, a command's executable may be hard to find, or not exist
485 in the PATH. Work around that by looking up the version
486 information in the package's version.plist file.
488 def __init__(self, key='CFBundleShortVersionString', **kwargs):
489 super(VersionPlistCommandDependency, self).__init__(**kwargs)
492 def _get_command_version_stream(self, *args, **kwargs):
493 raise NotImplementedError()
495 def _get_version_stream(self, *args, **kwargs):
496 raise NotImplementedError()
499 def _get_parent(root, element):
500 """Returns the parent of this element or None for the root element
502 for node in root.iter():
505 raise ValueError((root, element))
508 def _get_next(cls, root, element):
509 """Returns the following sibling of this element or None
511 parent = cls._get_parent(root=root, element=element)
512 siblings = iter(parent)
513 for node in siblings:
516 return next(siblings)
517 except StopIteration:
521 def _get_version_from_plist(self, path):
522 """Parse the plist and return the value string for self.key
524 tree = _element_tree.parse(source=path)
526 for key in tree.findall('.//key'):
527 value = self._get_next(root=tree, element=key)
528 if value.tag != 'string':
529 raise ValueError((tree, key, value))
530 data[key.text] = value.text
531 return data[self.key]
533 def _get_version(self):
534 for path in self.paths:
535 if _os.path.exists(path):
536 return self._get_version_from_plist(path=path)
537 raise DependencyError(
540 'nothing exists at any of the expected paths for {0}:\n {1}'
543 '\n '.join(p for p in self.paths)))
546 class UserTaskDependency (Dependency):
547 "Prompt the user to complete a task and check for success"
548 def __init__(self, prompt, **kwargs):
549 super(UserTaskDependency, self).__init__(**kwargs)
553 if _sys.version_info >= (3, ):
554 result = input(self.prompt)
556 result = raw_input(self.prompt)
557 return self._check_result(result)
559 def _check_result(self, result):
560 raise NotImplementedError()
563 class EditorTaskDependency (UserTaskDependency):
564 def __init__(self, **kwargs):
565 self.path = _os.path.expanduser(_os.path.join(
566 '~', 'swc-installation-test.txt'))
567 self.contents = 'Hello, world!'
568 super(EditorTaskDependency, self).__init__(
570 'Open your favorite text editor and create the file\n'
572 'containing the line:\n'
574 'Press enter here after you have done this.\n'
575 'You may remove the file after you have finished testing.'
576 ).format(self.path, self.contents),
579 def _check_result(self, result):
582 with open(self.path, 'r') as f:
585 raise DependencyError(
587 message='could not open {0!r}: {1}'.format(self.path, e)
589 if contents.strip() != self.contents:
590 raise DependencyError(
593 'file contents ({0!r}) did not match the expected {1!r}'
594 ).format(contents, self.contents))
597 class MakeDependency (CommandDependency):
598 makefile = '\n'.join([
600 '\t@echo "MAKE_VERSION=$(MAKE_VERSION)"',
601 '\t@echo "MAKE=$(MAKE)"',
605 def _get_version(self):
607 return super(MakeDependency, self)._get_version()
608 except DependencyError as e:
609 version_options = self.version_options
610 self.version_options = ['-f', '-']
612 stream = self._get_version_stream(stdin=self.makefile)
614 for line in stream.splitlines():
616 key,value = line.split('=', 1)
617 except ValueError as ve:
618 raise e# from NotImplementedError(stream)
620 if info.get('MAKE_VERSION', None):
621 return info['MAKE_VERSION']
622 elif info.get('MAKE', None):
626 self.version_options = version_options
629 class EasyInstallDependency (CommandDependency):
630 def _get_version(self):
632 return super(EasyInstallDependency, self)._get_version()
633 except DependencyError as e:
634 version_stream = self.version_stream
636 self.version_stream = 'stderr'
637 stream = self._get_version_stream(expect=(1,))
638 if 'option --version not recognized' in stream:
639 return 'unknown (possibly Setuptools?)'
641 self.version_stream = version_stream
644 class PythonDependency (Dependency):
645 def __init__(self, name='python', long_name='Python version',
646 minimum_version=(2, 6), **kwargs):
647 super(PythonDependency, self).__init__(
648 name=name, long_name=long_name, minimum_version=minimum_version,
651 def _get_version(self):
654 def _get_parsed_version(self):
655 return _sys.version_info
658 class PythonPackageDependency (Dependency):
659 def __init__(self, package, **kwargs):
660 if 'name' not in kwargs:
661 kwargs['name'] = package
662 if 'and_dependencies' not in kwargs:
663 kwargs['and_dependencies'] = []
664 if 'python' not in kwargs['and_dependencies']:
665 kwargs['and_dependencies'].append('python')
666 super(PythonPackageDependency, self).__init__(**kwargs)
667 self.package = package
669 def _get_version(self):
670 package = self._get_package(self.package)
671 return self._get_version_from_package(package)
673 def _get_package(self, package):
675 return _importlib.import_module(package)
676 except ImportError as e:
677 raise DependencyError(
679 message="could not import the '{0}' package for {1}".format(
680 package, self.full_name()),
683 def _get_version_from_package(self, package):
685 version = package.__version__
686 except AttributeError:
691 class MercurialPythonPackage (PythonPackageDependency):
692 def _get_version(self):
693 try: # mercurial >= 1.2
694 package = _importlib.import_module('mercurial.util')
695 except ImportError as e: # mercurial <= 1.1.2
696 package = self._get_package('mercurial.version')
697 return package.get_version()
699 return package.version()
702 class TornadoPythonPackage (PythonPackageDependency):
703 def _get_version_from_package(self, package):
704 return package.version
706 def _get_parsed_version(self):
707 package = self._get_package(self.package)
708 return package.version_info
711 class SQLitePythonPackage (PythonPackageDependency):
712 def _get_version_from_package(self, package):
715 def _get_parsed_version(self):
716 return _sys.version_info
719 def _program_files_paths(*args):
720 "Utility for generating MS Windows search paths"
721 pf = _os.environ.get('ProgramFiles', '/usr/bin')
722 pfx86 = _os.environ.get('ProgramFiles(x86)', pf)
723 paths = [_os.path.join(pf, *args)]
725 paths.append(_os.path.join(pfx86, *args))
729 CHECKER['python'] = PythonDependency()
732 for command,long_name,minimum_version,paths in [
733 ('sh', 'Bourne Shell', None, None),
734 ('ash', 'Almquist Shell', None, None),
735 ('bash', 'Bourne Again Shell', None, None),
736 ('csh', 'C Shell', None, None),
737 ('ksh', 'KornShell', None, None),
738 ('dash', 'Debian Almquist Shell', None, None),
739 ('tcsh', 'TENEX C Shell', None, None),
740 ('zsh', 'Z Shell', None, None),
741 ('git', 'Git', (1, 7, 0), None),
742 ('hg', 'Mercurial', (2, 0, 0), None),
743 ('EasyMercurial', None, (1, 3), None),
744 ('pip', None, None, None),
745 ('sqlite3', 'SQLite 3', None, None),
746 ('nosetests', 'Nose', (1, 0, 0), None),
747 ('ipython', 'IPython script', (1, 0), None),
748 ('emacs', 'Emacs', None, None),
749 ('xemacs', 'XEmacs', None, None),
750 ('vim', 'Vim', None, None),
751 ('vi', None, None, None),
752 ('nano', 'Nano', None, None),
753 ('gedit', None, None, None),
754 ('kate', 'Kate', None, None),
755 ('notepad++', 'Notepad++', None,
756 _program_files_paths('Notepad++', 'notepad++.exe')),
757 ('firefox', 'Firefox', None,
758 _program_files_paths('Mozilla Firefox', 'firefox.exe')),
759 ('google-chrome', 'Google Chrome', None,
760 _program_files_paths('Google', 'Chrome', 'Application', 'chrome.exe')
762 ('chromium', 'Chromium', None, None),
766 CHECKER[command] = CommandDependency(
767 command=command, paths=paths, long_name=long_name,
768 minimum_version=minimum_version)
769 del command, long_name, minimum_version, paths # cleanup namespace
772 CHECKER['make'] = MakeDependency(command='make', minimum_version=None)
775 CHECKER['easy_install'] = EasyInstallDependency(
776 command='easy_install', long_name='Setuptools easy_install',
777 minimum_version=None)
780 CHECKER['py.test'] = CommandDependency(
781 command='py.test', version_stream='stderr',
782 minimum_version=None)
785 for paths,name,long_name in [
786 ([_os.path.join(_ROOT_PATH, 'Applications', 'Sublime Text 2.app',
787 'Contents', 'version.plist')],
788 'sublime-text', 'Sublime Text'),
789 ([_os.path.join(_ROOT_PATH, 'Applications', 'TextMate.app',
790 'Contents', 'version.plist')],
791 'textmate', 'TextMate'),
792 ([_os.path.join(_ROOT_PATH, 'Applications', 'TextWrangler.app',
793 'Contents', 'version.plist')],
794 'textwrangler', 'TextWrangler'),
795 ([_os.path.join(_ROOT_PATH, 'Applications', 'Safari.app',
796 'Contents', 'version.plist')],
798 ([_os.path.join(_ROOT_PATH, 'Applications', 'Xcode.app',
799 'Contents', 'version.plist'), # OS X >=1.7
800 _os.path.join(_ROOT_PATH, 'Developer', 'Applications', 'Xcode.app',
801 'Contents', 'version.plist'), # OS X 1.6,
807 CHECKER[name] = VersionPlistCommandDependency(
808 command=None, paths=paths, name=name, long_name=long_name)
809 del paths, name, long_name # cleanup namespace
812 for package,name,long_name,minimum_version,and_dependencies in [
813 ('nose', None, 'Nose Python package',
814 CHECKER['nosetests'].minimum_version, None),
815 ('pytest', None, 'pytest Python package',
816 CHECKER['py.test'].minimum_version, None),
817 ('jinja2', 'jinja', 'Jinja', (2, 6), None),
818 ('zmq', 'pyzmq', 'PyZMQ', (2, 1, 4), None),
819 ('IPython', None, 'IPython Python package',
820 CHECKER['ipython'].minimum_version, [
825 name='virtual-browser-ipython',
826 long_name='IPython-compatible web browser',
829 command=CHECKER['firefox'].command,
830 paths=CHECKER['firefox'].paths,
831 name='{0}-for-ipython'.format(
832 CHECKER['firefox'].name),
833 long_name='{0} for IPython'.format(
834 CHECKER['firefox'].long_name),
835 minimum_version=(6, 0)),
837 command=CHECKER['google-chrome'].command,
838 paths=CHECKER['google-chrome'].paths,
839 name='{0}-for-ipython'.format(
840 CHECKER['google-chrome'].name),
841 long_name='{0} for IPython'.format(
842 CHECKER['google-chrome'].long_name),
843 minimum_version=(13, 0)),
845 command=CHECKER['chromium'].command,
846 paths=CHECKER['chromium'].paths,
847 name='{0}-for-ipython'.format(
848 CHECKER['chromium'].name),
849 long_name='{0} for IPython'.format(
850 CHECKER['chromium'].long_name),
851 minimum_version=(13, 0)),
852 VersionPlistCommandDependency(
853 command=CHECKER['safari'].command,
854 paths=CHECKER['safari'].paths,
855 key=CHECKER['safari'].key,
856 name='{0}-for-ipython'.format(
857 CHECKER['safari'].name),
858 long_name='{0} for IPython'.format(
859 CHECKER['safari'].long_name),
860 minimum_version=(5, 0)),
863 ('argparse', None, 'Argparse', None, None),
864 ('numpy', None, 'NumPy', None, None),
865 ('scipy', None, 'SciPy', None, None),
866 ('matplotlib', None, 'Matplotlib', None, None),
867 ('pandas', None, 'Pandas', (0, 8), None),
868 ('sympy', None, 'SymPy', None, None),
869 ('Cython', None, None, None, None),
870 ('networkx', None, 'NetworkX', None, None),
871 ('mayavi.mlab', None, 'MayaVi', None, None),
872 ('setuptools', None, 'Setuptools', None, None),
880 kwargs['and_dependencies'] = and_dependencies
881 CHECKER[name] = PythonPackageDependency(
882 package=package, name=name, long_name=long_name,
883 minimum_version=minimum_version, **kwargs)
885 del package, name, long_name, minimum_version, and_dependencies, kwargs
888 CHECKER['mercurial'] = MercurialPythonPackage(
889 package='mercurial.util', name='mercurial',
890 long_name='Mercurial Python package',
891 minimum_version=CHECKER['hg'].minimum_version)
894 CHECKER['tornado'] = TornadoPythonPackage(
895 package='tornado', name='tornado', long_name='Tornado', minimum_version=(2, 0))
898 CHECKER['sqlite3-python'] = SQLitePythonPackage(
899 package='sqlite3', name='sqlite3-python',
900 long_name='SQLite Python package',
901 minimum_version=CHECKER['sqlite3'].minimum_version)
904 CHECKER['other-editor'] = EditorTaskDependency(
905 name='other-editor', long_name='')
908 for name,long_name,dependencies in [
909 ('virtual-shell', 'command line shell', (
919 ('virtual-editor', 'text/code editor', (
931 'other-editor', # last because it requires user interaction
933 ('virtual-browser', 'web browser', (
939 ('virtual-pypi-installer', 'PyPI installer', (
944 CHECKER[name] = VirtualDependency(
945 name=name, long_name=long_name, or_dependencies=dependencies)
946 del name, long_name, dependencies # cleanup namespace
949 def _print_info(key, value, indent=19):
950 print('{0}{1}: {2}'.format(key, ' '*(indent-len(key)), value))
952 def print_system_info():
953 print("If you do not understand why the above failures occurred,")
954 print("copy and send the *entire* output (all info above and summary")
955 print("below) to the instructor for help.")
957 print('==================')
958 print('System information')
959 print('==================')
960 _print_info('os.name', _os.name)
961 _print_info('os.uname', _platform.uname())
962 _print_info('platform', _sys.platform)
963 _print_info('platform+', _platform.platform())
965 'linux_distribution',
969 value = getattr(_platform, pversion)()
971 _print_info(pversion, value)
972 _print_info('prefix', _sys.prefix)
973 _print_info('exec_prefix', _sys.exec_prefix)
974 _print_info('executable', _sys.executable)
975 _print_info('version_info', _sys.version_info)
976 _print_info('version', _sys.version)
977 _print_info('environment', '')
978 for key,value in sorted(_os.environ.items()):
979 print(' {0}={1}'.format(key, value))
980 print('==================')
982 def print_suggestions(instructor_fallback=True):
984 print('For suggestions on installing missing packages, see')
985 print('http://software-carpentry.org/setup/')
987 print('For instructings on installing a particular package,')
988 print('see the failure message for that package printed above.')
989 if instructor_fallback:
991 print('For help, email the *entire* output of this script to')
992 print('your instructor.')
995 if __name__ == '__main__':
996 import optparse as _optparse
998 parser = _optparse.OptionParser(usage='%prog [options] [check...]')
1000 parser.format_epilog = lambda formatter: '\n' + epilog
1002 '-v', '--verbose', action='store_true',
1003 help=('print additional information to help troubleshoot '
1004 'installation issues'))
1005 options,args = parser.parse_args()
1007 passed = check(args)
1008 except InvalidCheck as e:
1009 print("I don't know how to check for {0!r}".format(e.check))
1010 print('I do know how to check for:')
1011 for key,checker in sorted(CHECKER.items()):
1012 if checker.long_name != checker.name:
1013 print(' {0} {1}({2})'.format(
1014 key, ' '*(20-len(key)), checker.long_name))
1016 print(' {0}'.format(key))
1022 print_suggestions(instructor_fallback=True)