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 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 DependencyError(
444 message='empty version stream on {0} for {1}'.format(
445 self.version_stream, command))
447 raise NotImplementedError(self.version_stream)
449 def _get_version(self):
450 version_stream = self._get_version_stream()
451 match = self.version_regexp.search(version_stream)
453 raise DependencyError(
455 message='no version string in output:\n{0}'.format(
457 return match.group(1)
460 for command,long_name,minimum_version in [
461 ('sh', 'Bourne Shell', None),
462 ('ash', 'Almquist Shell', None),
463 ('bash', 'Bourne Again Shell', None),
464 ('csh', 'C Shell', None),
465 ('ksh', 'KornShell', None),
466 ('dash', 'Debian Almquist Shell', None),
467 ('tcsh', 'TENEX C Shell', None),
468 ('zsh', 'Z Shell', None),
469 ('git', 'Git', (1, 7, 0)),
470 ('hg', 'Mercurial', (2, 0, 0)),
471 ('EasyMercurial', None, (1, 3)),
473 ('sqlite3', 'SQLite 3', None),
474 ('nosetests', 'Nose', (1, 0, 0)),
475 ('ipython', 'IPython script', (0, 13)),
476 ('emacs', 'Emacs', None),
477 ('xemacs', 'XEmacs', None),
478 ('vim', 'Vim', None),
480 ('nano', 'Nano', None),
481 ('gedit', None, None),
482 ('kate', 'Kate', None),
483 ('notepad++', 'Notepad++', None),
484 ('firefox', 'Firefox', None),
485 ('google-chrome', 'Google Chrome', None),
486 ('chromium', 'Chromium', None),
490 CHECKER[command] = CommandDependency(
491 command=command, long_name=long_name, minimum_version=minimum_version)
492 del command, long_name, minimum_version # cleanup namespace
495 class MakeDependency (CommandDependency):
496 makefile = '\n'.join([
498 '\t@echo "MAKE_VERSION=$(MAKE_VERSION)"',
499 '\t@echo "MAKE=$(MAKE)"',
503 def _get_version(self):
505 return super(MakeDependency, self)._get_version()
506 except DependencyError as e:
507 version_options = self.version_options
508 self.version_options = ['-f', '-']
510 stream = self._get_version_stream(stdin=self.makefile)
512 for line in stream.splitlines():
514 key,value = line.split('=', 1)
515 except ValueError as ve:
516 raise e# from NotImplementedError(stream)
518 if info.get('MAKE_VERSION', None):
519 return info['MAKE_VERSION']
520 elif info.get('MAKE', None):
524 self.version_options = version_options
527 CHECKER['make'] = MakeDependency(command='make', minimum_version=None)
530 class EasyInstallDependency (CommandDependency):
531 def _get_version(self):
533 return super(EasyInstallDependency, self)._get_version()
534 except DependencyError as e:
535 version_stream = self.version_stream
537 self.version_stream = 'stderr'
538 stream = self._get_version_stream(expect=(1,))
539 if 'option --version not recognized' in stream:
540 return 'unknown (possibly Setuptools?)'
542 self.version_stream = version_stream
545 CHECKER['easy_install'] = EasyInstallDependency(
546 command='easy_install', long_name='Setuptools easy_install',
547 minimum_version=None)
550 class PathCommandDependency (CommandDependency):
551 """A command that doesn't support --version or equivalent options
553 On some operating systems (e.g. OS X), a command's executable may
554 be hard to find, or not exist in the PATH. Work around that by
555 just checking for the existence of a characteristic file or
556 directory. Since the characteristic path may depend on OS,
557 installed version, etc., take a list of paths, and succeed if any
560 def __init__(self, paths, **kwargs):
561 super(PathCommandDependency, self).__init__(self, **kwargs)
564 def _get_version_stream(self, *args, **kwargs):
565 raise NotImplementedError()
567 def _get_version(self):
568 for path in self.paths:
569 if _os.path.exists(path):
571 raise DependencyError(
574 'nothing exists at any of the expected paths for {0}:\n {1}'
577 '\n '.join(p for p in self.paths)))
580 for paths,name,long_name in [
581 ([_os.path.join(_os.sep, 'Applications', 'Sublime Text 2.app')],
582 'sublime-text', 'Sublime Text'),
583 ([_os.path.join(_os.sep, 'Applications', 'TextMate.app')],
584 'textmate', 'TextMate'),
585 ([_os.path.join(_os.sep, 'Applications', 'TextWrangler.app')],
586 'textwrangler', 'TextWrangler'),
587 ([_os.path.join(_os.sep, 'Applications', 'Safari.app')],
589 ([_os.path.join(_os.sep, 'Applications', 'Xcode.app'), # OS X >=1.7
590 _os.path.join(_os.sep, 'Developer', 'Applications', 'Xcode.app'
597 CHECKER[name] = PathCommandDependency(
598 paths=paths, name=name, long_name=long_name)
599 del paths, name, long_name # cleanup namespace
602 class PythonPackageDependency (Dependency):
603 def __init__(self, package, **kwargs):
604 if 'name' not in kwargs:
605 kwargs['name'] = package
606 if 'and_dependencies' not in kwargs:
607 kwargs['and_dependencies'] = []
608 if 'python' not in kwargs['and_dependencies']:
609 kwargs['and_dependencies'].append('python')
610 super(PythonPackageDependency, self).__init__(**kwargs)
611 self.package = package
613 def _get_version(self):
614 package = self._get_package(self.package)
615 return self._get_version_from_package(package)
617 def _get_package(self, package):
619 return _importlib.import_module(package)
620 except ImportError as e:
621 raise DependencyError(
623 message="could not import the '{0}' package for {1}".format(
624 package, self.full_name()),
627 def _get_version_from_package(self, package):
629 version = package.__version__
630 except AttributeError:
635 for package,name,long_name,minimum_version,and_dependencies in [
636 ('nose', None, 'Nose Python package',
637 CHECKER['nosetests'].minimum_version, None),
638 ('jinja2', 'jinja', 'Jinja', (2, 6), None),
639 ('zmq', 'pyzmq', 'PyZMQ', (2, 1, 4), None),
640 ('IPython', None, 'IPython Python package',
641 CHECKER['ipython'].minimum_version, ['jinja', 'tornado', 'pyzmq']),
642 ('argparse', None, 'Argparse', None, None),
643 ('numpy', None, 'NumPy', None, None),
644 ('scipy', None, 'SciPy', None, None),
645 ('matplotlib', None, 'Matplotlib', None, None),
646 ('pandas', None, 'Pandas', (0, 8), None),
647 ('sympy', None, 'SymPy', None, None),
648 ('Cython', None, None, None, None),
649 ('networkx', None, 'NetworkX', None, None),
650 ('mayavi.mlab', None, 'MayaVi', None, None),
651 ('setuptools', None, 'Setuptools', None, None),
659 kwargs['and_dependencies'] = and_dependencies
660 CHECKER[name] = PythonPackageDependency(
661 package=package, name=name, long_name=long_name,
662 minimum_version=minimum_version, **kwargs)
664 del package, name, long_name, minimum_version, and_dependencies, kwargs
667 class MercurialPythonPackage (PythonPackageDependency):
668 def _get_version(self):
669 try: # mercurial >= 1.2
670 package = _importlib.import_module('mercurial.util')
671 except ImportError as e: # mercurial <= 1.1.2
672 package = self._get_package('mercurial.version')
673 return package.get_version()
675 return package.version()
678 CHECKER['mercurial'] = MercurialPythonPackage(
679 package='mercurial.util', name='mercurial',
680 long_name='Mercurial Python package',
681 minimum_version=CHECKER['hg'].minimum_version)
684 class TornadoPythonPackage (PythonPackageDependency):
685 def _get_version_from_package(self, package):
686 return package.version
688 def _get_parsed_version(self):
689 package = self._get_package(self.package)
690 return package.version_info
693 CHECKER['tornado'] = TornadoPythonPackage(
694 package='tornado', name='tornado', long_name='Tornado', minimum_version=(2, 0))
697 class SQLitePythonPackage (PythonPackageDependency):
698 def _get_version_from_package(self, package):
701 def _get_parsed_version(self):
702 return _sys.version_info
705 CHECKER['sqlite3-python'] = SQLitePythonPackage(
706 package='sqlite3', name='sqlite3-python',
707 long_name='SQLite Python package',
708 minimum_version=CHECKER['sqlite3'].minimum_version)
711 class UserTaskDependency (Dependency):
712 "Prompt the user to complete a task and check for success"
713 def __init__(self, prompt, **kwargs):
714 super(UserTaskDependency, self).__init__(**kwargs)
718 if _sys.version_info >= (3, ):
719 result = input(self.prompt)
721 result = raw_input(self.prompt)
722 return self._check_result(result)
724 def _check_result(self, result):
725 raise NotImplementedError()
728 class EditorTaskDependency (UserTaskDependency):
729 def __init__(self, **kwargs):
730 self.path = _os.path.expanduser(_os.path.join(
731 '~', 'swc-installation-test.txt'))
732 self.contents = 'Hello, world!'
733 super(EditorTaskDependency, self).__init__(
735 'Open your favorite text editor and create the file\n'
737 'containing the line:\n'
739 'Press enter here after you have done this.\n'
740 'You may remove the file after you have finished testing.'
741 ).format(self.path, self.contents),
744 def _check_result(self, result):
747 with open(self.path, 'r') as f:
750 raise DependencyError(
752 message='could not open {0!r}: {1}'.format(self.path, e)
754 if contents.strip() != self.contents:
755 raise DependencyError(
758 'file contents ({0!r}) did not match the expected {1!r}'
759 ).format(contents, self.contents))
762 CHECKER['other-editor'] = EditorTaskDependency(
763 name='other-editor', long_name='')
766 class VirtualDependency (Dependency):
768 return '{0} {1}'.format(
769 self.or_pass['dependency'].full_name(),
770 self.or_pass['version'])
773 for name,long_name,dependencies in [
774 ('virtual-shell', 'command line shell', (
784 ('virtual-editor', 'text/code editor', (
796 'other-editor', # last because it requires user interaction
798 ('virtual-browser', 'web browser', (
804 ('virtual-pypi-installer', 'PyPI installer', (
809 CHECKER[name] = VirtualDependency(
810 name=name, long_name=long_name, or_dependencies=dependencies)
811 del name, long_name, dependencies # cleanup namespace
814 def _print_info(key, value, indent=19):
815 print('{0}{1}: {2}'.format(key, ' '*(indent-len(key)), value))
817 def print_system_info():
818 print("If you do not understand why the above failures occurred,")
819 print("copy and send the *entire* output (all info above and summary")
820 print("below) to the instructor for help.")
822 print('==================')
823 print('System information')
824 print('==================')
825 _print_info('os.name', _os.name)
826 _print_info('os.uname', _platform.uname())
827 _print_info('platform', _sys.platform)
828 _print_info('platform+', _platform.platform())
830 'linux_distribution',
834 value = getattr(_platform, pversion)()
836 _print_info(pversion, value)
837 _print_info('prefix', _sys.prefix)
838 _print_info('exec_prefix', _sys.exec_prefix)
839 _print_info('executable', _sys.executable)
840 _print_info('version_info', _sys.version_info)
841 _print_info('version', _sys.version)
842 _print_info('environment', '')
843 for key,value in sorted(_os.environ.items()):
844 print(' {0}={1}'.format(key, value))
845 print('==================')
847 def print_suggestions(instructor_fallback=True):
849 print('For suggestions on installing missing packages, see')
850 print('http://software-carpentry.org/setup/')
852 print('For instructings on installing a particular package,')
853 print('see the failure message for that package printed above.')
854 if instructor_fallback:
856 print('For help, email the *entire* output of this script to')
857 print('your instructor.')
860 if __name__ == '__main__':
861 import optparse as _optparse
863 parser = _optparse.OptionParser(usage='%prog [options] [check...]')
865 parser.format_epilog = lambda formatter: '\n' + epilog
867 '-v', '--verbose', action='store_true',
868 help=('print additional information to help troubleshoot '
869 'installation issues'))
870 options,args = parser.parse_args()
873 except InvalidCheck as e:
874 print("I don't know how to check for {0!r}".format(e.check))
875 print('I do know how to check for:')
876 for key,checker in sorted(CHECKER.items()):
877 if checker.long_name != checker.name:
878 print(' {0} {1}({2})'.format(
879 key, ' '*(20-len(key)), checker.long_name))
881 print(' {0}'.format(key))
887 print_suggestions(instructor_fallback=True)