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()
55 if not hasattr(_shlex, 'quote'): # Python versions older than 3.3
56 # Use the undocumented pipes.quote()
57 import pipes as _pipes
58 _shlex.quote = _pipes.quote
63 # Comment out any entries you don't need
73 'hg', # Command line tool
74 #'mercurial', # Python package
76 # Build tools and packaging
78 'virtual-pypi-installer',
82 'nosetests', # Command line tool
83 'nose', # Python package
84 'py.test', # Command line tool
85 'pytest', # Python package
87 'sqlite3', # Command line tool
88 'sqlite3-python', # Python package
91 'ipython', # Command line tool
92 'IPython', # Python package
93 'argparse', # Useful for utility scripts
107 if _platform.system() == 'win32':
111 class InvalidCheck (KeyError):
112 def __init__(self, check):
113 super(InvalidCheck, self).__init__(check)
120 class DependencyError (Exception):
121 _default_url = 'http://software-carpentry.org/setup/'
122 _setup_urls = { # (system, version, package) glob pairs
123 ('*', '*', 'Cython'): 'http://docs.cython.org/src/quickstart/install.html',
124 ('Linux', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-linux',
125 ('Darwin', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-mac',
126 ('Windows', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-windows',
127 ('*', '*', 'EasyMercurial'): 'http://easyhg.org/download.html',
128 ('*', '*', 'argparse'): 'https://pypi.python.org/pypi/argparse#installation',
129 ('*', '*', 'ash'): 'http://www.in-ulm.de/~mascheck/various/ash/',
130 ('*', '*', 'bash'): 'http://www.gnu.org/software/bash/manual/html_node/Basic-Installation.html#Basic-Installation',
131 ('Linux', '*', 'chromium'): 'http://code.google.com/p/chromium/wiki/LinuxBuildInstructions',
132 ('Darwin', '*', 'chromium'): 'http://code.google.com/p/chromium/wiki/MacBuildInstructions',
133 ('Windows', '*', 'chromium'): 'http://www.chromium.org/developers/how-tos/build-instructions-windows',
134 ('*', '*', 'chromium'): 'http://www.chromium.org/developers/how-tos',
135 ('Windows', '*', 'emacs'): 'http://www.gnu.org/software/emacs/windows/Installing-Emacs.html',
136 ('*', '*', 'emacs'): 'http://www.gnu.org/software/emacs/#Obtaining',
137 ('*', '*', 'firefox'): 'http://www.mozilla.org/en-US/firefox/new/',
138 ('Linux', '*', 'gedit'): 'http://www.linuxfromscratch.org/blfs/view/svn/gnome/gedit.html',
139 ('*', '*', 'git'): 'http://git-scm.com/downloads',
140 ('*', '*', 'google-chrome'): 'https://www.google.com/intl/en/chrome/browser/',
141 ('*', '*', 'hg'): 'http://mercurial.selenic.com/',
142 ('*', '*', 'mercurial'): 'http://mercurial.selenic.com/',
143 ('*', '*', 'IPython'): 'http://ipython.org/install.html',
144 ('*', '*', 'ipython'): 'http://ipython.org/install.html',
145 ('*', '*', 'jinja'): 'http://jinja.pocoo.org/docs/intro/#installation',
146 ('*', '*', 'kate'): 'http://kate-editor.org/get-it/',
147 ('*', '*', 'make'): 'http://www.gnu.org/software/make/',
148 ('Darwin', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#building-on-osx',
149 ('Windows', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#installing-on-windows',
150 ('*', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#installing',
151 ('*', '*', 'mayavi.mlab'): 'http://docs.enthought.com/mayavi/mayavi/installation.html',
152 ('*', '*', 'nano'): 'http://www.nano-editor.org/dist/latest/faq.html#3',
153 ('*', '*', 'networkx'): 'http://networkx.github.com/documentation/latest/install.html#installing',
154 ('*', '*', 'nose'): 'https://nose.readthedocs.org/en/latest/#installation-and-quick-start',
155 ('*', '*', 'nosetests'): 'https://nose.readthedocs.org/en/latest/#installation-and-quick-start',
156 ('*', '*', 'notepad++'): 'http://notepad-plus-plus.org/download/v6.3.html',
157 ('*', '*', 'numpy'): 'http://docs.scipy.org/doc/numpy/user/install.html',
158 ('*', '*', 'pandas'): 'http://pandas.pydata.org/pandas-docs/stable/install.html',
159 ('*', '*', 'pip'): 'http://www.pip-installer.org/en/latest/installing.html',
160 ('*', '*', 'pytest'): 'http://pytest.org/latest/getting-started.html',
161 ('*', '*', 'python'): 'http://www.python.org/download/releases/2.7.3/#download',
162 ('*', '*', 'pyzmq'): 'https://github.com/zeromq/pyzmq/wiki/Building-and-Installing-PyZMQ',
163 ('*', '*', 'py.test'): 'http://pytest.org/latest/getting-started.html',
164 ('Linux', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Linux',
165 ('Darwin', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Mac_OS_X',
166 ('Windows', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Windows',
167 ('*', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy',
168 ('*', '*', 'setuptools'): 'https://pypi.python.org/pypi/setuptools#installation-instructions',
169 ('*', '*', 'sqlite3'): 'http://www.sqlite.org/download.html',
170 ('*', '*', 'sublime-text'): 'http://www.sublimetext.com/2',
171 ('*', '*', 'sympy'): 'http://docs.sympy.org/dev/install.html',
172 ('Darwin', '*', 'textmate'): 'http://macromates.com/',
173 ('Darwin', '*', 'textwrangler'): 'http://www.barebones.com/products/textwrangler/download.html',
174 ('*', '*', 'tornado'): 'http://www.tornadoweb.org/',
175 ('*', '*', 'vim'): 'http://www.vim.org/download.php',
176 ('Darwin', '*', 'xcode'): 'https://developer.apple.com/xcode/',
177 ('*', '*', 'xemacs'): 'http://www.us.xemacs.org/Install/',
178 ('*', '*', 'zsh'): 'http://www.zsh.org/',
181 def _get_message(self):
183 def _set_message(self, message):
184 self._message = message
185 message = property(_get_message, _set_message)
187 def __init__(self, checker, message, causes=None):
188 super(DependencyError, self).__init__(message)
189 self.checker = checker
190 self.message = message
196 system = _platform.system()
199 'linux_distribution',
203 value = getattr(_platform, pversion)()
207 package = self.checker.name
208 for (s,v,p),url in self._setup_urls.items():
209 if (_fnmatch.fnmatch(system, s) and
210 _fnmatch.fnmatch(version, v) and
211 _fnmatch.fnmatch(package, p)):
213 return self._default_url
218 'check for {0} failed:'.format(self.checker.full_name()),
220 ' For instructions on installing an up-to-date version, see',
224 lines.append(' causes:')
225 for cause in self.causes:
226 lines.extend(' ' + line for line in str(cause).splitlines())
227 return '\n'.join(lines)
230 def check(checks=None):
237 checker = CHECKER[check]
238 except KeyError as e:
239 raise InvalidCheck(check)# from e
240 _sys.stdout.write('check {0}...\t'.format(checker.full_name()))
242 version = checker.check()
243 except DependencyError as e:
245 _sys.stdout.write('fail\n')
247 _sys.stdout.write('pass\n')
248 successes.append((checker, version))
250 print('\nSuccesses:\n')
251 for checker,version in successes:
252 print('{0} {1}'.format(
254 version or 'unknown'))
258 for failure in failures:
259 if failure not in printed:
262 printed.append(failure)
267 class Dependency (object):
268 def __init__(self, name, long_name=None, minimum_version=None,
269 version_delimiter='.', and_dependencies=None,
270 or_dependencies=None):
272 self.long_name = long_name or name
273 self.minimum_version = minimum_version
274 self.version_delimiter = version_delimiter
275 if not and_dependencies:
276 and_dependencies = []
277 self.and_dependencies = and_dependencies
278 if not or_dependencies:
280 self.or_dependencies = or_dependencies
281 self._check_error = None
284 return '<{0} {1}>'.format(type(self).__name__, self.name)
287 if self.name == self.long_name:
290 return '{0} ({1})'.format(self.long_name, self.name)
293 if self._check_error:
294 raise self._check_error
296 self._check_dependencies()
298 except DependencyError as e:
299 self._check_error = e # cache for future calls
302 def _check_dependencies(self):
303 for dependency in self.and_dependencies:
304 if not hasattr(dependency, 'check'):
305 dependency = CHECKER[dependency]
308 except DependencyError as e:
309 raise DependencyError(
312 'some dependencies for {0} were not satisfied'
313 ).format(self.full_name()),
317 for dependency in self.or_dependencies:
318 if not hasattr(dependency, 'check'):
319 dependency = CHECKER[dependency]
321 version = dependency.check()
322 except DependencyError as e:
326 'dependency': dependency,
329 break # no need to test other dependencies
330 if self.or_dependencies and not self.or_pass:
331 raise DependencyError(
334 '{0} requires at least one of the following dependencies'
335 ).format(self.full_name()),
339 version = self._get_version()
340 parsed_version = None
341 if hasattr(self, '_get_parsed_version'):
342 parsed_version = self._get_parsed_version()
343 if self.minimum_version:
344 self._check_version(version=version, parsed_version=parsed_version)
347 def _get_version(self):
348 raise NotImplementedError(self)
350 def _minimum_version_string(self):
351 return self.version_delimiter.join(
352 str(part) for part in self.minimum_version)
354 def _check_version(self, version, parsed_version=None):
355 if not parsed_version:
356 parsed_version = self._parse_version(version=version)
357 if not parsed_version or parsed_version < self.minimum_version:
358 raise DependencyError(
360 message='outdated version of {0}: {1} (need >= {2})'.format(
361 self.full_name(), version, self._minimum_version_string()))
363 def _parse_version(self, version):
367 for part in version.split(self.version_delimiter):
369 parsed_version.append(int(part))
370 except ValueError as e:
371 raise DependencyError(
374 'unparsable {0!r} in version {1} of {2}, (need >= {3})'
376 part, version, self.full_name(),
377 self._minimum_version_string()))# from e
378 return tuple(parsed_version)
381 class VirtualDependency (Dependency):
383 return '{0} {1}'.format(
384 self.or_pass['dependency'].full_name(),
385 self.or_pass['version'])
388 class CommandDependency (Dependency):
389 exe_extension = _distutils_ccompiler.new_compiler().exe_extension
391 def __init__(self, command, paths=None, version_options=('--version',),
392 stdin=None, version_regexp=None, version_stream='stdout',
394 if 'name' not in kwargs:
395 kwargs['name'] = command
396 super(CommandDependency, self).__init__(**kwargs)
397 self.command = command
399 self.version_options = version_options
401 if not version_regexp:
402 regexp = r'([\d][\d{0}]*[\d])'.format(self.version_delimiter)
403 version_regexp = _re.compile(regexp)
404 self.version_regexp = version_regexp
405 self.version_stream = version_stream
407 def _get_command_version_stream(self, command=None, stdin=None,
410 command = self.command + (self.exe_extension or '')
414 popen_stdin = _subprocess.PIPE
418 p = _subprocess.Popen(
419 [command] + list(self.version_options), stdin=popen_stdin,
420 stdout=_subprocess.PIPE, stderr=_subprocess.PIPE,
421 universal_newlines=True)
423 raise DependencyError(
425 message="could not find '{0}' executable".format(command),
427 stdout,stderr = p.communicate(stdin)
429 if status not in expect:
431 "failed to execute: {0} {1}".format(
433 ' '.join(_shlex.quote(arg)
434 for arg in self.version_options)),
435 'status: {0}'.format(status),
437 for name,string in [('stdout', stdout), ('stderr', stderr)]:
439 lines.extend([name + ':', string])
440 raise DependencyError(checker=self, message='\n'.join(lines))
441 for name,string in [('stdout', stdout), ('stderr', stderr)]:
442 if name == self.version_stream:
444 raise DependencyError(
446 message='empty version stream on {0} for {1}'.format(
447 self.version_stream, command))
449 raise NotImplementedError(self.version_stream)
451 def _get_version_stream(self, **kwargs):
452 paths = [self.command + (self.exe_extension or '')]
453 if self.exe_extension:
454 paths.append(self.command) # also look at the extension-less path
456 paths.extend(self.paths)
460 return self._get_command_version_stream(command=path, **kwargs)
461 except DependencyError as e:
463 raise DependencyError(
465 message='errors finding {0} version'.format(
469 def _get_version(self):
470 version_stream = self._get_version_stream()
471 match = self.version_regexp.search(version_stream)
473 raise DependencyError(
475 message='no version string in output:\n{0}'.format(
477 return match.group(1)
480 class PathCommandDependency (CommandDependency):
481 """A command that doesn't support --version or equivalent options
483 On some operating systems (e.g. OS X), a command's executable may
484 be hard to find, or not exist in the PATH. Work around that by
485 just checking for the existence of a characteristic file or
486 directory. Since the characteristic path may depend on OS,
487 installed version, etc., take a list of paths, and succeed if any
490 def _get_command_version_stream(self, *args, **kwargs):
491 raise NotImplementedError()
493 def _get_version_stream(self, *args, **kwargs):
494 raise NotImplementedError()
496 def _get_version(self):
497 for path in self.paths:
498 if _os.path.exists(path):
500 raise DependencyError(
503 'nothing exists at any of the expected paths for {0}:\n {1}'
506 '\n '.join(p for p in self.paths)))
509 class UserTaskDependency (Dependency):
510 "Prompt the user to complete a task and check for success"
511 def __init__(self, prompt, **kwargs):
512 super(UserTaskDependency, self).__init__(**kwargs)
516 if _sys.version_info >= (3, ):
517 result = input(self.prompt)
519 result = raw_input(self.prompt)
520 return self._check_result(result)
522 def _check_result(self, result):
523 raise NotImplementedError()
526 class EditorTaskDependency (UserTaskDependency):
527 def __init__(self, **kwargs):
528 self.path = _os.path.expanduser(_os.path.join(
529 '~', 'swc-installation-test.txt'))
530 self.contents = 'Hello, world!'
531 super(EditorTaskDependency, self).__init__(
533 'Open your favorite text editor and create the file\n'
535 'containing the line:\n'
537 'Press enter here after you have done this.\n'
538 'You may remove the file after you have finished testing.'
539 ).format(self.path, self.contents),
542 def _check_result(self, result):
545 with open(self.path, 'r') as f:
548 raise DependencyError(
550 message='could not open {0!r}: {1}'.format(self.path, e)
552 if contents.strip() != self.contents:
553 raise DependencyError(
556 'file contents ({0!r}) did not match the expected {1!r}'
557 ).format(contents, self.contents))
560 class MakeDependency (CommandDependency):
561 makefile = '\n'.join([
563 '\t@echo "MAKE_VERSION=$(MAKE_VERSION)"',
564 '\t@echo "MAKE=$(MAKE)"',
568 def _get_version(self):
570 return super(MakeDependency, self)._get_version()
571 except DependencyError as e:
572 version_options = self.version_options
573 self.version_options = ['-f', '-']
575 stream = self._get_version_stream(stdin=self.makefile)
577 for line in stream.splitlines():
579 key,value = line.split('=', 1)
580 except ValueError as ve:
581 raise e# from NotImplementedError(stream)
583 if info.get('MAKE_VERSION', None):
584 return info['MAKE_VERSION']
585 elif info.get('MAKE', None):
589 self.version_options = version_options
592 class EasyInstallDependency (CommandDependency):
593 def _get_version(self):
595 return super(EasyInstallDependency, self)._get_version()
596 except DependencyError as e:
597 version_stream = self.version_stream
599 self.version_stream = 'stderr'
600 stream = self._get_version_stream(expect=(1,))
601 if 'option --version not recognized' in stream:
602 return 'unknown (possibly Setuptools?)'
604 self.version_stream = version_stream
607 class PythonDependency (Dependency):
608 def __init__(self, name='python', long_name='Python version',
609 minimum_version=(2, 6), **kwargs):
610 super(PythonDependency, self).__init__(
611 name=name, long_name=long_name, minimum_version=minimum_version,
614 def _get_version(self):
617 def _get_parsed_version(self):
618 return _sys.version_info
621 class PythonPackageDependency (Dependency):
622 def __init__(self, package, **kwargs):
623 if 'name' not in kwargs:
624 kwargs['name'] = package
625 if 'and_dependencies' not in kwargs:
626 kwargs['and_dependencies'] = []
627 if 'python' not in kwargs['and_dependencies']:
628 kwargs['and_dependencies'].append('python')
629 super(PythonPackageDependency, self).__init__(**kwargs)
630 self.package = package
632 def _get_version(self):
633 package = self._get_package(self.package)
634 return self._get_version_from_package(package)
636 def _get_package(self, package):
638 return _importlib.import_module(package)
639 except ImportError as e:
640 raise DependencyError(
642 message="could not import the '{0}' package for {1}".format(
643 package, self.full_name()),
646 def _get_version_from_package(self, package):
648 version = package.__version__
649 except AttributeError:
654 class MercurialPythonPackage (PythonPackageDependency):
655 def _get_version(self):
656 try: # mercurial >= 1.2
657 package = _importlib.import_module('mercurial.util')
658 except ImportError as e: # mercurial <= 1.1.2
659 package = self._get_package('mercurial.version')
660 return package.get_version()
662 return package.version()
665 class TornadoPythonPackage (PythonPackageDependency):
666 def _get_version_from_package(self, package):
667 return package.version
669 def _get_parsed_version(self):
670 package = self._get_package(self.package)
671 return package.version_info
674 class SQLitePythonPackage (PythonPackageDependency):
675 def _get_version_from_package(self, package):
678 def _get_parsed_version(self):
679 return _sys.version_info
682 def _program_files_paths(*args):
683 "Utility for generating MS Windows search paths"
684 pf = _os.environ.get('ProgramFiles', '/usr/bin')
685 pfx86 = _os.environ.get('ProgramFiles(x86)', pf)
686 paths = [_os.path.join(pf, *args)]
688 paths.append(_os.path.join(pfx86, *args))
692 CHECKER['python'] = PythonDependency()
695 for command,long_name,minimum_version,paths in [
696 ('sh', 'Bourne Shell', None, None),
697 ('ash', 'Almquist Shell', None, None),
698 ('bash', 'Bourne Again Shell', None, None),
699 ('csh', 'C Shell', None, None),
700 ('ksh', 'KornShell', None, None),
701 ('dash', 'Debian Almquist Shell', None, None),
702 ('tcsh', 'TENEX C Shell', None, None),
703 ('zsh', 'Z Shell', None, None),
704 ('git', 'Git', (1, 7, 0), None),
705 ('hg', 'Mercurial', (2, 0, 0), None),
706 ('EasyMercurial', None, (1, 3), None),
707 ('pip', None, None, None),
708 ('sqlite3', 'SQLite 3', None, None),
709 ('nosetests', 'Nose', (1, 0, 0), None),
710 ('ipython', 'IPython script', (1, 0), None),
711 ('emacs', 'Emacs', None, None),
712 ('xemacs', 'XEmacs', None, None),
713 ('vim', 'Vim', None, None),
714 ('vi', None, None, None),
715 ('nano', 'Nano', None, None),
716 ('gedit', None, None, None),
717 ('kate', 'Kate', None, None),
718 ('notepad++', 'Notepad++', None,
719 _program_files_paths('Notepad++', 'notepad++.exe')),
720 ('firefox', 'Firefox', None,
721 _program_files_paths('Mozilla Firefox', 'firefox.exe')),
722 ('google-chrome', 'Google Chrome', None,
723 _program_files_paths('Google', 'Chrome', 'Application', 'chrome.exe')
725 ('chromium', 'Chromium', None, None),
729 CHECKER[command] = CommandDependency(
730 command=command, paths=paths, long_name=long_name,
731 minimum_version=minimum_version)
732 del command, long_name, minimum_version, paths # cleanup namespace
735 CHECKER['make'] = MakeDependency(command='make', minimum_version=None)
738 CHECKER['easy_install'] = EasyInstallDependency(
739 command='easy_install', long_name='Setuptools easy_install',
740 minimum_version=None)
743 CHECKER['py.test'] = CommandDependency(
744 command='py.test', version_stream='stderr',
745 minimum_version=None)
748 for paths,name,long_name in [
749 ([_os.path.join(_ROOT_PATH, 'Applications', 'Sublime Text 2.app')],
750 'sublime-text', 'Sublime Text'),
751 ([_os.path.join(_ROOT_PATH, 'Applications', 'TextMate.app')],
752 'textmate', 'TextMate'),
753 ([_os.path.join(_ROOT_PATH, 'Applications', 'TextWrangler.app')],
754 'textwrangler', 'TextWrangler'),
755 ([_os.path.join(_ROOT_PATH, 'Applications', 'Safari.app')],
757 ([_os.path.join(_ROOT_PATH, 'Applications', 'Xcode.app'), # OS X >=1.7
758 _os.path.join(_ROOT_PATH, 'Developer', 'Applications', 'Xcode.app'
765 CHECKER[name] = PathCommandDependency(
766 command=None, paths=paths, name=name, long_name=long_name)
767 del paths, name, long_name # cleanup namespace
770 for package,name,long_name,minimum_version,and_dependencies in [
771 ('nose', None, 'Nose Python package',
772 CHECKER['nosetests'].minimum_version, None),
773 ('pytest', None, 'pytest Python package',
774 CHECKER['py.test'].minimum_version, None),
775 ('jinja2', 'jinja', 'Jinja', (2, 6), None),
776 ('zmq', 'pyzmq', 'PyZMQ', (2, 1, 4), None),
777 ('IPython', None, 'IPython Python package',
778 CHECKER['ipython'].minimum_version, [
783 name='virtual-browser-ipython',
784 long_name='IPython-compatible web browser',
787 command=CHECKER['firefox'].command,
788 paths=CHECKER['firefox'].paths,
789 name='{0}-for-ipython'.format(
790 CHECKER['firefox'].name),
791 long_name='{0} for IPython'.format(
792 CHECKER['firefox'].long_name),
793 minimum_version=(6, 0)),
795 command=CHECKER['google-chrome'].command,
796 paths=CHECKER['google-chrome'].paths,
797 name='{0}-for-ipython'.format(
798 CHECKER['google-chrome'].name),
799 long_name='{0} for IPython'.format(
800 CHECKER['google-chrome'].long_name),
801 minimum_version=(13, 0)),
803 command=CHECKER['chromium'].command,
804 paths=CHECKER['chromium'].paths,
805 name='{0}-for-ipython'.format(
806 CHECKER['chromium'].name),
807 long_name='{0} for IPython'.format(
808 CHECKER['chromium'].long_name),
809 minimum_version=(13, 0)),
810 PathCommandDependency(
811 command=CHECKER['safari'].command,
812 paths=CHECKER['safari'].paths,
813 name='{0}-for-ipython'.format(
814 CHECKER['safari'].name),
815 long_name='{0} for IPython'.format(
816 CHECKER['safari'].long_name),
817 minimum_version=(5, 0)),
820 ('argparse', None, 'Argparse', None, None),
821 ('numpy', None, 'NumPy', None, None),
822 ('scipy', None, 'SciPy', None, None),
823 ('matplotlib', None, 'Matplotlib', None, None),
824 ('pandas', None, 'Pandas', (0, 8), None),
825 ('sympy', None, 'SymPy', None, None),
826 ('Cython', None, None, None, None),
827 ('networkx', None, 'NetworkX', None, None),
828 ('mayavi.mlab', None, 'MayaVi', None, None),
829 ('setuptools', None, 'Setuptools', None, None),
837 kwargs['and_dependencies'] = and_dependencies
838 CHECKER[name] = PythonPackageDependency(
839 package=package, name=name, long_name=long_name,
840 minimum_version=minimum_version, **kwargs)
842 del package, name, long_name, minimum_version, and_dependencies, kwargs
845 CHECKER['mercurial'] = MercurialPythonPackage(
846 package='mercurial.util', name='mercurial',
847 long_name='Mercurial Python package',
848 minimum_version=CHECKER['hg'].minimum_version)
851 CHECKER['tornado'] = TornadoPythonPackage(
852 package='tornado', name='tornado', long_name='Tornado', minimum_version=(2, 0))
855 CHECKER['sqlite3-python'] = SQLitePythonPackage(
856 package='sqlite3', name='sqlite3-python',
857 long_name='SQLite Python package',
858 minimum_version=CHECKER['sqlite3'].minimum_version)
861 CHECKER['other-editor'] = EditorTaskDependency(
862 name='other-editor', long_name='')
865 for name,long_name,dependencies in [
866 ('virtual-shell', 'command line shell', (
876 ('virtual-editor', 'text/code editor', (
888 'other-editor', # last because it requires user interaction
890 ('virtual-browser', 'web browser', (
896 ('virtual-pypi-installer', 'PyPI installer', (
901 CHECKER[name] = VirtualDependency(
902 name=name, long_name=long_name, or_dependencies=dependencies)
903 del name, long_name, dependencies # cleanup namespace
906 def _print_info(key, value, indent=19):
907 print('{0}{1}: {2}'.format(key, ' '*(indent-len(key)), value))
909 def print_system_info():
910 print("If you do not understand why the above failures occurred,")
911 print("copy and send the *entire* output (all info above and summary")
912 print("below) to the instructor for help.")
914 print('==================')
915 print('System information')
916 print('==================')
917 _print_info('os.name', _os.name)
918 _print_info('os.uname', _platform.uname())
919 _print_info('platform', _sys.platform)
920 _print_info('platform+', _platform.platform())
922 'linux_distribution',
926 value = getattr(_platform, pversion)()
928 _print_info(pversion, value)
929 _print_info('prefix', _sys.prefix)
930 _print_info('exec_prefix', _sys.exec_prefix)
931 _print_info('executable', _sys.executable)
932 _print_info('version_info', _sys.version_info)
933 _print_info('version', _sys.version)
934 _print_info('environment', '')
935 for key,value in sorted(_os.environ.items()):
936 print(' {0}={1}'.format(key, value))
937 print('==================')
939 def print_suggestions(instructor_fallback=True):
941 print('For suggestions on installing missing packages, see')
942 print('http://software-carpentry.org/setup/')
944 print('For instructings on installing a particular package,')
945 print('see the failure message for that package printed above.')
946 if instructor_fallback:
948 print('For help, email the *entire* output of this script to')
949 print('your instructor.')
952 if __name__ == '__main__':
953 import optparse as _optparse
955 parser = _optparse.OptionParser(usage='%prog [options] [check...]')
957 parser.format_epilog = lambda formatter: '\n' + epilog
959 '-v', '--verbose', action='store_true',
960 help=('print additional information to help troubleshoot '
961 'installation issues'))
962 options,args = parser.parse_args()
965 except InvalidCheck as e:
966 print("I don't know how to check for {0!r}".format(e.check))
967 print('I do know how to check for:')
968 for key,checker in sorted(CHECKER.items()):
969 if checker.long_name != checker.name:
970 print(' {0} {1}({2})'.format(
971 key, ' '*(20-len(key)), checker.long_name))
973 print(' {0}'.format(key))
979 print_suggestions(instructor_fallback=True)