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
85 'sqlite3', # Command line tool
86 'sqlite3-python', # Python package
89 'ipython', # Command line tool
90 'IPython', # Python package
91 'argparse', # Useful for utility scripts
105 class InvalidCheck (KeyError):
106 def __init__(self, check):
107 super(InvalidCheck, self).__init__(check)
114 class DependencyError (Exception):
115 _default_url = 'http://software-carpentry.org/setup/'
116 _setup_urls = { # (system, version, package) glob pairs
117 ('*', '*', 'Cython'): 'http://docs.cython.org/src/quickstart/install.html',
118 ('Linux', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-linux',
119 ('Darwin', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-mac',
120 ('Windows', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-windows',
121 ('*', '*', 'EasyMercurial'): 'http://easyhg.org/download.html',
122 ('*', '*', 'argparse'): 'https://pypi.python.org/pypi/argparse#installation',
123 ('*', '*', 'ash'): 'http://www.in-ulm.de/~mascheck/various/ash/',
124 ('*', '*', 'bash'): 'http://www.gnu.org/software/bash/manual/html_node/Basic-Installation.html#Basic-Installation',
125 ('Linux', '*', 'chromium'): 'http://code.google.com/p/chromium/wiki/LinuxBuildInstructions',
126 ('Darwin', '*', 'chromium'): 'http://code.google.com/p/chromium/wiki/MacBuildInstructions',
127 ('Windows', '*', 'chromium'): 'http://www.chromium.org/developers/how-tos/build-instructions-windows',
128 ('*', '*', 'chromium'): 'http://www.chromium.org/developers/how-tos',
129 ('Windows', '*', 'emacs'): 'http://www.gnu.org/software/emacs/windows/Installing-Emacs.html',
130 ('*', '*', 'emacs'): 'http://www.gnu.org/software/emacs/#Obtaining',
131 ('*', '*', 'firefox'): 'http://www.mozilla.org/en-US/firefox/new/',
132 ('Linux', '*', 'gedit'): 'http://www.linuxfromscratch.org/blfs/view/svn/gnome/gedit.html',
133 ('*', '*', 'git'): 'http://git-scm.com/downloads',
134 ('*', '*', 'google-chrome'): 'https://www.google.com/intl/en/chrome/browser/',
135 ('*', '*', 'hg'): 'http://mercurial.selenic.com/',
136 ('*', '*', 'mercurial'): 'http://mercurial.selenic.com/',
137 ('*', '*', 'IPython'): 'http://ipython.org/install.html',
138 ('*', '*', 'ipython'): 'http://ipython.org/install.html',
139 ('*', '*', 'jinja'): 'http://jinja.pocoo.org/docs/intro/#installation',
140 ('*', '*', 'kate'): 'http://kate-editor.org/get-it/',
141 ('*', '*', 'make'): 'http://www.gnu.org/software/make/',
142 ('Darwin', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#building-on-osx',
143 ('Windows', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#installing-on-windows',
144 ('*', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#installing',
145 ('*', '*', 'mayavi.mlab'): 'http://docs.enthought.com/mayavi/mayavi/installation.html',
146 ('*', '*', 'nano'): 'http://www.nano-editor.org/dist/latest/faq.html#3',
147 ('*', '*', 'networkx'): 'http://networkx.github.com/documentation/latest/install.html#installing',
148 ('*', '*', 'nose'): 'https://nose.readthedocs.org/en/latest/#installation-and-quick-start',
149 ('*', '*', 'nosetests'): 'https://nose.readthedocs.org/en/latest/#installation-and-quick-start',
150 ('*', '*', 'notepad++'): 'http://notepad-plus-plus.org/download/v6.3.html',
151 ('*', '*', 'numpy'): 'http://docs.scipy.org/doc/numpy/user/install.html',
152 ('*', '*', 'pandas'): 'http://pandas.pydata.org/pandas-docs/stable/install.html',
153 ('*', '*', 'pip'): 'http://www.pip-installer.org/en/latest/installing.html',
154 ('*', '*', 'python'): 'http://www.python.org/download/releases/2.7.3/#download',
155 ('*', '*', 'pyzmq'): 'https://github.com/zeromq/pyzmq/wiki/Building-and-Installing-PyZMQ',
156 ('Linux', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Linux',
157 ('Darwin', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Mac_OS_X',
158 ('Windows', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Windows',
159 ('*', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy',
160 ('*', '*', 'setuptools'): 'https://pypi.python.org/pypi/setuptools#installation-instructions',
161 ('*', '*', 'sqlite3'): 'http://www.sqlite.org/download.html',
162 ('*', '*', 'sublime-text'): 'http://www.sublimetext.com/2',
163 ('*', '*', 'sympy'): 'http://docs.sympy.org/dev/install.html',
164 ('Darwin', '*', 'textmate'): 'http://macromates.com/',
165 ('Darwin', '*', 'textwrangler'): 'http://www.barebones.com/products/textwrangler/download.html',
166 ('*', '*', 'tornado'): 'http://www.tornadoweb.org/',
167 ('*', '*', 'vim'): 'http://www.vim.org/download.php',
168 ('Darwin', '*', 'xcode'): 'https://developer.apple.com/xcode/',
169 ('*', '*', 'xemacs'): 'http://www.us.xemacs.org/Install/',
170 ('*', '*', 'zsh'): 'http://www.zsh.org/',
173 def _get_message(self):
175 def _set_message(self, message):
176 self._message = message
177 message = property(_get_message, _set_message)
179 def __init__(self, checker, message, causes=None):
180 super(DependencyError, self).__init__(message)
181 self.checker = checker
182 self.message = message
188 system = _platform.system()
191 'linux_distribution',
195 value = getattr(_platform, pversion)()
199 package = self.checker.name
200 for (s,v,p),url in self._setup_urls.items():
201 if (_fnmatch.fnmatch(system, s) and
202 _fnmatch.fnmatch(version, v) and
203 _fnmatch.fnmatch(package, p)):
205 return self._default_url
210 'check for {0} failed:'.format(self.checker.full_name()),
212 ' For instructions on installing an up-to-date version, see',
216 lines.append(' causes:')
217 for cause in self.causes:
218 lines.extend(' ' + line for line in str(cause).splitlines())
219 return '\n'.join(lines)
222 def check(checks=None):
229 checker = CHECKER[check]
230 except KeyError as e:
231 raise InvalidCheck(check)# from e
232 _sys.stdout.write('check {0}...\t'.format(checker.full_name()))
234 version = checker.check()
235 except DependencyError as e:
237 _sys.stdout.write('fail\n')
239 _sys.stdout.write('pass\n')
240 successes.append((checker, version))
242 print('\nSuccesses:\n')
243 for checker,version in successes:
244 print('{0} {1}'.format(
246 version or 'unknown'))
250 for failure in failures:
251 if failure not in printed:
254 printed.append(failure)
259 class Dependency (object):
260 def __init__(self, name, long_name=None, minimum_version=None,
261 version_delimiter='.', and_dependencies=None,
262 or_dependencies=None):
264 self.long_name = long_name or name
265 self.minimum_version = minimum_version
266 self.version_delimiter = version_delimiter
267 if not and_dependencies:
268 and_dependencies = []
269 self.and_dependencies = and_dependencies
270 if not or_dependencies:
272 self.or_dependencies = or_dependencies
273 self._check_error = None
276 return '<{0} {1}>'.format(type(self).__name__, self.name)
279 if self.name == self.long_name:
282 return '{0} ({1})'.format(self.long_name, self.name)
285 if self._check_error:
286 raise self._check_error
288 self._check_dependencies()
290 except DependencyError as e:
291 self._check_error = e # cache for future calls
294 def _check_dependencies(self):
295 for dependency in self.and_dependencies:
296 if not hasattr(dependency, 'check'):
297 dependency = CHECKER[dependency]
300 except DependencyError as e:
301 raise DependencyError(
304 'some dependencies for {0} were not satisfied'
305 ).format(self.full_name()),
309 for dependency in self.or_dependencies:
310 if not hasattr(dependency, 'check'):
311 dependency = CHECKER[dependency]
313 version = dependency.check()
314 except DependencyError as e:
318 'dependency': dependency,
321 break # no need to test other dependencies
322 if self.or_dependencies and not self.or_pass:
323 raise DependencyError(
326 '{0} requires at least one of the following dependencies'
327 ).format(self.full_name()),
331 version = self._get_version()
332 parsed_version = None
333 if hasattr(self, '_get_parsed_version'):
334 parsed_version = self._get_parsed_version()
335 if self.minimum_version:
336 self._check_version(version=version, parsed_version=parsed_version)
339 def _get_version(self):
340 raise NotImplementedError(self)
342 def _minimum_version_string(self):
343 return self.version_delimiter.join(
344 str(part) for part in self.minimum_version)
346 def _check_version(self, version, parsed_version=None):
347 if not parsed_version:
348 parsed_version = self._parse_version(version=version)
349 if not parsed_version or parsed_version < self.minimum_version:
350 raise DependencyError(
352 message='outdated version of {0}: {1} (need >= {2})'.format(
353 self.full_name(), version, self._minimum_version_string()))
355 def _parse_version(self, version):
359 for part in version.split(self.version_delimiter):
361 parsed_version.append(int(part))
362 except ValueError as e:
363 raise DependencyError(
366 'unparsable {0!r} in version {1} of {2}, (need >= {3})'
368 part, version, self.full_name(),
369 self._minimum_version_string()))# from e
370 return tuple(parsed_version)
373 class PythonDependency (Dependency):
374 def __init__(self, name='python', long_name='Python version',
375 minimum_version=(2, 6), **kwargs):
376 super(PythonDependency, self).__init__(
377 name=name, long_name=long_name, minimum_version=minimum_version,
380 def _get_version(self):
383 def _get_parsed_version(self):
384 return _sys.version_info
387 CHECKER['python'] = PythonDependency()
390 class CommandDependency (Dependency):
391 exe_extension = _distutils_ccompiler.new_compiler().exe_extension
393 def __init__(self, command, version_options=('--version',), stdin=None,
394 version_regexp=None, version_stream='stdout', **kwargs):
395 if 'name' not in kwargs:
396 kwargs['name'] = command
397 super(CommandDependency, self).__init__(**kwargs)
398 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_version_stream(self, stdin=None, expect=(0,)):
411 popen_stdin = _subprocess.PIPE
414 command = self.command + (self.exe_extension or '')
416 p = _subprocess.Popen(
417 [command] + list(self.version_options), stdin=popen_stdin,
418 stdout=_subprocess.PIPE, stderr=_subprocess.PIPE,
419 close_fds=True, shell=False, universal_newlines=True)
421 raise DependencyError(
423 message="could not find '{0}' executable".format(command),
425 stdout,stderr = p.communicate(stdin)
427 if status not in expect:
429 "failed to execute: {0} {1}".format(
431 ' '.join(_shlex.quote(arg)
432 for arg in self.version_options)),
433 'status: {0}'.format(status),
435 for name,string in [('stdout', stdout), ('stderr', stderr)]:
437 lines.extend([name + ':', string])
438 raise DependencyError(checker=self, message='\n'.join(lines))
439 for name,string in [('stdout', stdout), ('stderr', stderr)]:
440 if name == self.version_stream:
442 raise NotImplementedError(self.version_stream)
444 def _get_version(self):
445 version_stream = self._get_version_stream()
446 match = self.version_regexp.search(version_stream)
448 raise DependencyError(
450 message='no version string in output:\n{0}'.format(
452 return match.group(1)
455 for command,long_name,minimum_version in [
456 ('sh', 'Bourne Shell', None),
457 ('ash', 'Almquist Shell', None),
458 ('bash', 'Bourne Again Shell', None),
459 ('csh', 'C Shell', None),
460 ('ksh', 'KornShell', None),
461 ('dash', 'Debian Almquist Shell', None),
462 ('tcsh', 'TENEX C Shell', None),
463 ('zsh', 'Z Shell', None),
464 ('git', 'Git', (1, 7, 0)),
465 ('hg', 'Mercurial', (2, 0, 0)),
466 ('EasyMercurial', None, (1, 3)),
468 ('sqlite3', 'SQLite 3', None),
469 ('nosetests', 'Nose', (1, 0, 0)),
470 ('ipython', 'IPython script', (0, 13)),
471 ('emacs', 'Emacs', None),
472 ('xemacs', 'XEmacs', None),
473 ('vim', 'Vim', None),
475 ('nano', 'Nano', None),
476 ('gedit', None, None),
477 ('kate', 'Kate', None),
478 ('notepad++', 'Notepad++', None),
479 ('firefox', 'Firefox', None),
480 ('google-chrome', 'Google Chrome', None),
481 ('chromium', 'Chromium', None),
485 CHECKER[command] = CommandDependency(
486 command=command, long_name=long_name, minimum_version=minimum_version)
487 del command, long_name, minimum_version # cleanup namespace
490 class MakeDependency (CommandDependency):
491 makefile = '\n'.join([
493 '\t@echo "MAKE_VERSION=$(MAKE_VERSION)"',
494 '\t@echo "MAKE=$(MAKE)"',
498 def _get_version(self):
500 return super(MakeDependency, self)._get_version()
501 except DependencyError as e:
502 version_options = self.version_options
503 self.version_options = ['-f', '-']
505 stream = self._get_version_stream(stdin=self.makefile)
507 for line in stream.splitlines():
509 key,value = line.split('=', 1)
510 except ValueError as ve:
511 raise e# from NotImplementedError(stream)
513 if info.get('MAKE_VERSION', None):
514 return info['MAKE_VERSION']
515 elif info.get('MAKE', None):
519 self.version_options = version_options
522 CHECKER['make'] = MakeDependency(command='make', minimum_version=None)
525 class EasyInstallDependency (CommandDependency):
526 def _get_version(self):
528 return super(EasyInstallDependency, self)._get_version()
529 except DependencyError as e:
530 version_stream = self.version_stream
532 self.version_stream = 'stderr'
533 stream = self._get_version_stream(expect=(1,))
534 if 'option --version not recognized' in stream:
535 return 'unknown (possibly Setuptools?)'
537 self.version_stream = version_stream
540 CHECKER['easy_install'] = EasyInstallDependency(
541 command='easy_install', long_name='Setuptools easy_install',
542 minimum_version=None)
545 class PathCommandDependency (CommandDependency):
546 """A command that doesn't support --version or equivalent options
548 On some operating systems (e.g. OS X), a command's executable may
549 be hard to find, or not exist in the PATH. Work around that by
550 just checking for the existence of a characteristic file or
551 directory. Since the characteristic path may depend on OS,
552 installed version, etc., take a list of paths, and succeed if any
555 def __init__(self, paths, **kwargs):
556 super(PathCommandDependency, self).__init__(self, **kwargs)
559 def _get_version_stream(self, *args, **kwargs):
560 raise NotImplementedError()
562 def _get_version(self):
563 for path in self.paths:
564 if _os.path.exists(path):
566 raise DependencyError(
569 'nothing exists at any of the expected paths for {0}:\n {1}'
572 '\n '.join(p for p in self.paths)))
575 for paths,name,long_name in [
576 ([_os.path.join(_os.sep, 'Applications', 'Sublime Text 2.app')],
577 'sublime-text', 'Sublime Text'),
578 ([_os.path.join(_os.sep, 'Applications', 'TextMate.app')],
579 'textmate', 'TextMate'),
580 ([_os.path.join(_os.sep, 'Applications', 'TextWrangler.app')],
581 'textwrangler', 'TextWrangler'),
582 ([_os.path.join(_os.sep, 'Applications', 'Xcode.app'), # OS X >=1.7
583 _os.path.join(_os.sep, 'Developer', 'Applications', 'Xcode.app'
590 CHECKER[name] = PathCommandDependency(
591 paths=paths, name=name, long_name=long_name)
592 del paths, name, long_name # cleanup namespace
595 class PythonPackageDependency (Dependency):
596 def __init__(self, package, **kwargs):
597 if 'name' not in kwargs:
598 kwargs['name'] = package
599 if 'and_dependencies' not in kwargs:
600 kwargs['and_dependencies'] = []
601 if 'python' not in kwargs['and_dependencies']:
602 kwargs['and_dependencies'].append('python')
603 super(PythonPackageDependency, self).__init__(**kwargs)
604 self.package = package
606 def _get_version(self):
607 package = self._get_package(self.package)
608 return self._get_version_from_package(package)
610 def _get_package(self, package):
612 return _importlib.import_module(package)
613 except ImportError as e:
614 raise DependencyError(
616 message="could not import the '{0}' package for {1}".format(
617 package, self.full_name()),
620 def _get_version_from_package(self, package):
622 version = package.__version__
623 except AttributeError:
628 for package,name,long_name,minimum_version,and_dependencies in [
629 ('nose', None, 'Nose Python package',
630 CHECKER['nosetests'].minimum_version, None),
631 ('jinja2', 'jinja', 'Jinja', (2, 6), None),
632 ('zmq', 'pyzmq', 'PyZMQ', (2, 1, 4), None),
633 ('IPython', None, 'IPython Python package',
634 CHECKER['ipython'].minimum_version, ['jinja', 'tornado', 'pyzmq']),
635 ('argparse', None, 'Argparse', None, None),
636 ('numpy', None, 'NumPy', None, None),
637 ('scipy', None, 'SciPy', None, None),
638 ('matplotlib', None, 'Matplotlib', None, None),
639 ('pandas', None, 'Pandas', (0, 8), None),
640 ('sympy', None, 'SymPy', None, None),
641 ('Cython', None, None, None, None),
642 ('networkx', None, 'NetworkX', None, None),
643 ('mayavi.mlab', None, 'MayaVi', None, None),
644 ('setuptools', None, 'Setuptools', None, None),
652 kwargs['and_dependencies'] = and_dependencies
653 CHECKER[name] = PythonPackageDependency(
654 package=package, name=name, long_name=long_name,
655 minimum_version=minimum_version, **kwargs)
657 del package, name, long_name, minimum_version, and_dependencies, kwargs
660 class MercurialPythonPackage (PythonPackageDependency):
661 def _get_version(self):
662 try: # mercurial >= 1.2
663 package = _importlib.import_module('mercurial.util')
664 except ImportError as e: # mercurial <= 1.1.2
665 package = self._get_package('mercurial.version')
666 return package.get_version()
668 return package.version()
671 CHECKER['mercurial'] = MercurialPythonPackage(
672 package='mercurial.util', name='mercurial',
673 long_name='Mercurial Python package',
674 minimum_version=CHECKER['hg'].minimum_version)
677 class TornadoPythonPackage (PythonPackageDependency):
678 def _get_version_from_package(self, package):
679 return package.version
681 def _get_parsed_version(self):
682 package = self._get_package(self.package)
683 return package.version_info
686 CHECKER['tornado'] = TornadoPythonPackage(
687 package='tornado', name='tornado', long_name='Tornado', minimum_version=(2, 0))
690 class SQLitePythonPackage (PythonPackageDependency):
691 def _get_version_from_package(self, package):
694 def _get_parsed_version(self):
695 return _sys.version_info
698 CHECKER['sqlite3-python'] = SQLitePythonPackage(
699 package='sqlite3', name='sqlite3-python',
700 long_name='SQLite Python package',
701 minimum_version=CHECKER['sqlite3'].minimum_version)
704 class UserTaskDependency (Dependency):
705 "Prompt the user to complete a task and check for success"
706 def __init__(self, prompt, **kwargs):
707 super(UserTaskDependency, self).__init__(**kwargs)
711 if _sys.version_info >= (3, ):
712 result = input(self.prompt)
714 result = raw_input(self.prompt)
715 return self._check_result(result)
717 def _check_result(self, result):
718 raise NotImplementedError()
721 class EditorTaskDependency (UserTaskDependency):
722 def __init__(self, **kwargs):
723 self.path = _os.path.expanduser(_os.path.join(
724 '~', 'swc-installation-test.txt'))
725 self.contents = 'Hello, world!'
726 super(EditorTaskDependency, self).__init__(
728 'Open your favorite text editor and create the file\n'
730 'containing the line:\n'
732 'Press enter here after you have done this.\n'
733 'You may remove the file after you have finished testing.'
734 ).format(self.path, self.contents),
737 def _check_result(self, result):
740 with open(self.path, 'r') as f:
743 raise DependencyError(
745 message='could not open {0!r}: {1}'.format(self.path, e)
747 if contents.strip() != self.contents:
748 raise DependencyError(
751 'file contents ({0!r}) did not match the expected {1!r}'
752 ).format(contents, self.contents))
755 CHECKER['other-editor'] = EditorTaskDependency(
756 name='other-editor', long_name='')
759 class VirtualDependency (Dependency):
761 return '{0} {1}'.format(
762 self.or_pass['dependency'].full_name(),
763 self.or_pass['version'])
766 for name,long_name,dependencies in [
767 ('virtual-shell', 'command line shell', (
777 ('virtual-editor', 'text/code editor', (
789 'other-editor', # last because it requires user interaction
791 ('virtual-browser', 'web browser', (
796 ('virtual-pypi-installer', 'PyPI installer', (
801 CHECKER[name] = VirtualDependency(
802 name=name, long_name=long_name, or_dependencies=dependencies)
803 del name, long_name, dependencies # cleanup namespace
806 def _print_info(key, value, indent=19):
807 print('{0}{1}: {2}'.format(key, ' '*(indent-len(key)), value))
809 def print_system_info():
810 print("If you do not understand why the above failures occurred,")
811 print("copy and send the *entire* output (all info above and summary")
812 print("below) to the instructor for help.")
814 print('==================')
815 print('System information')
816 print('==================')
817 _print_info('os.name', _os.name)
818 _print_info('os.uname', _platform.uname())
819 _print_info('platform', _sys.platform)
820 _print_info('platform+', _platform.platform())
822 'linux_distribution',
826 value = getattr(_platform, pversion)()
828 _print_info(pversion, value)
829 _print_info('prefix', _sys.prefix)
830 _print_info('exec_prefix', _sys.exec_prefix)
831 _print_info('executable', _sys.executable)
832 _print_info('version_info', _sys.version_info)
833 _print_info('version', _sys.version)
834 _print_info('environment', '')
835 for key,value in sorted(_os.environ.items()):
836 print(' {0}={1}'.format(key, value))
837 print('==================')
839 def print_suggestions(instructor_fallback=True):
841 print('For suggestions on installing missing packages, see')
842 print('http://software-carpentry.org/setup/')
844 print('For instructings on installing a particular package,')
845 print('see the failure message for that package printed above.')
846 if instructor_fallback:
848 print('For help, email the *entire* output of this script to')
849 print('your instructor.')
852 if __name__ == '__main__':
853 import optparse as _optparse
855 parser = _optparse.OptionParser(usage='%prog [options] [check...]')
857 parser.format_epilog = lambda formatter: '\n' + epilog
859 '-v', '--verbose', action='store_true',
860 help=('print additional information to help troubleshoot '
861 'installation issues'))
862 options,args = parser.parse_args()
865 except InvalidCheck as e:
866 print("I don't know how to check for {0!r}".format(e.check))
867 print('I do know how to check for:')
868 for key,checker in sorted(CHECKER.items()):
869 if checker.long_name != checker.name:
870 print(' {0} {1}({2})'.format(
871 key, ' '*(20-len(key)), checker.long_name))
873 print(' {0}'.format(key))
879 print_suggestions(instructor_fallback=True)