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 PythonDependency (Dependency):
382 def __init__(self, name='python', long_name='Python version',
383 minimum_version=(2, 6), **kwargs):
384 super(PythonDependency, self).__init__(
385 name=name, long_name=long_name, minimum_version=minimum_version,
388 def _get_version(self):
391 def _get_parsed_version(self):
392 return _sys.version_info
395 CHECKER['python'] = PythonDependency()
398 class CommandDependency (Dependency):
399 exe_extension = _distutils_ccompiler.new_compiler().exe_extension
401 def __init__(self, command, paths=None, version_options=('--version',),
402 stdin=None, version_regexp=None, version_stream='stdout',
404 if 'name' not in kwargs:
405 kwargs['name'] = command
406 super(CommandDependency, self).__init__(**kwargs)
407 self.command = command
409 self.version_options = version_options
411 if not version_regexp:
412 regexp = r'([\d][\d{0}]*[\d])'.format(self.version_delimiter)
413 version_regexp = _re.compile(regexp)
414 self.version_regexp = version_regexp
415 self.version_stream = version_stream
417 def _get_command_version_stream(self, command=None, stdin=None,
420 command = self.command + (self.exe_extension or '')
424 popen_stdin = _subprocess.PIPE
428 p = _subprocess.Popen(
429 [command] + list(self.version_options), stdin=popen_stdin,
430 stdout=_subprocess.PIPE, stderr=_subprocess.PIPE,
431 universal_newlines=True)
433 raise DependencyError(
435 message="could not find '{0}' executable".format(command),
437 stdout,stderr = p.communicate(stdin)
439 if status not in expect:
441 "failed to execute: {0} {1}".format(
443 ' '.join(_shlex.quote(arg)
444 for arg in self.version_options)),
445 'status: {0}'.format(status),
447 for name,string in [('stdout', stdout), ('stderr', stderr)]:
449 lines.extend([name + ':', string])
450 raise DependencyError(checker=self, message='\n'.join(lines))
451 for name,string in [('stdout', stdout), ('stderr', stderr)]:
452 if name == self.version_stream:
454 raise DependencyError(
456 message='empty version stream on {0} for {1}'.format(
457 self.version_stream, command))
459 raise NotImplementedError(self.version_stream)
461 def _get_version_stream(self, **kwargs):
462 paths = [self.command + (self.exe_extension or '')]
463 if self.exe_extension:
464 paths.append(self.command) # also look at the extension-less path
466 paths.extend(self.paths)
470 return self._get_command_version_stream(command=path, **kwargs)
471 except DependencyError as e:
473 raise DependencyError(
475 message='errors finding {0} version'.format(
479 def _get_version(self):
480 version_stream = self._get_version_stream()
481 match = self.version_regexp.search(version_stream)
483 raise DependencyError(
485 message='no version string in output:\n{0}'.format(
487 return match.group(1)
490 def _program_files_paths(*args):
491 "Utility for generating MS Windows search paths"
492 pf = _os.environ.get('ProgramFiles', '/usr/bin')
493 pfx86 = _os.environ.get('ProgramFiles(x86)', pf)
494 paths = [_os.path.join(pf, *args)]
496 paths.append(_os.path.join(pfx86, *args))
500 for command,long_name,minimum_version,paths in [
501 ('sh', 'Bourne Shell', None, None),
502 ('ash', 'Almquist Shell', None, None),
503 ('bash', 'Bourne Again Shell', None, None),
504 ('csh', 'C Shell', None, None),
505 ('ksh', 'KornShell', None, None),
506 ('dash', 'Debian Almquist Shell', None, None),
507 ('tcsh', 'TENEX C Shell', None, None),
508 ('zsh', 'Z Shell', None, None),
509 ('git', 'Git', (1, 7, 0), None),
510 ('hg', 'Mercurial', (2, 0, 0), None),
511 ('EasyMercurial', None, (1, 3), None),
512 ('pip', None, None, None),
513 ('sqlite3', 'SQLite 3', None, None),
514 ('nosetests', 'Nose', (1, 0, 0), None),
515 ('ipython', 'IPython script', (0, 13), None),
516 ('emacs', 'Emacs', None, None),
517 ('xemacs', 'XEmacs', None, None),
518 ('vim', 'Vim', None, None),
519 ('vi', None, None, None),
520 ('nano', 'Nano', None, None),
521 ('gedit', None, None, None),
522 ('kate', 'Kate', None, None),
523 ('notepad++', 'Notepad++', None,
524 _program_files_paths('Notepad++', 'notepad++.exe')),
525 ('firefox', 'Firefox', None,
526 _program_files_paths('Mozilla Firefox', 'firefox.exe')),
527 ('google-chrome', 'Google Chrome', None,
528 _program_files_paths('Google', 'Chrome', 'Application', 'chrome.exe')
530 ('chromium', 'Chromium', None, None),
534 CHECKER[command] = CommandDependency(
535 command=command, paths=paths, long_name=long_name,
536 minimum_version=minimum_version)
537 del command, long_name, minimum_version, paths # cleanup namespace
540 class MakeDependency (CommandDependency):
541 makefile = '\n'.join([
543 '\t@echo "MAKE_VERSION=$(MAKE_VERSION)"',
544 '\t@echo "MAKE=$(MAKE)"',
548 def _get_version(self):
550 return super(MakeDependency, self)._get_version()
551 except DependencyError as e:
552 version_options = self.version_options
553 self.version_options = ['-f', '-']
555 stream = self._get_version_stream(stdin=self.makefile)
557 for line in stream.splitlines():
559 key,value = line.split('=', 1)
560 except ValueError as ve:
561 raise e# from NotImplementedError(stream)
563 if info.get('MAKE_VERSION', None):
564 return info['MAKE_VERSION']
565 elif info.get('MAKE', None):
569 self.version_options = version_options
572 CHECKER['make'] = MakeDependency(command='make', minimum_version=None)
575 class EasyInstallDependency (CommandDependency):
576 def _get_version(self):
578 return super(EasyInstallDependency, self)._get_version()
579 except DependencyError as e:
580 version_stream = self.version_stream
582 self.version_stream = 'stderr'
583 stream = self._get_version_stream(expect=(1,))
584 if 'option --version not recognized' in stream:
585 return 'unknown (possibly Setuptools?)'
587 self.version_stream = version_stream
590 CHECKER['easy_install'] = EasyInstallDependency(
591 command='easy_install', long_name='Setuptools easy_install',
592 minimum_version=None)
595 CHECKER['py.test'] = CommandDependency(
596 command='py.test', version_stream='stderr',
597 minimum_version=None)
600 class PathCommandDependency (CommandDependency):
601 """A command that doesn't support --version or equivalent options
603 On some operating systems (e.g. OS X), a command's executable may
604 be hard to find, or not exist in the PATH. Work around that by
605 just checking for the existence of a characteristic file or
606 directory. Since the characteristic path may depend on OS,
607 installed version, etc., take a list of paths, and succeed if any
610 def _get_command_version_stream(self, *args, **kwargs):
611 raise NotImplementedError()
613 def _get_version_stream(self, *args, **kwargs):
614 raise NotImplementedError()
616 def _get_version(self):
617 for path in self.paths:
618 if _os.path.exists(path):
620 raise DependencyError(
623 'nothing exists at any of the expected paths for {0}:\n {1}'
626 '\n '.join(p for p in self.paths)))
629 for paths,name,long_name in [
630 ([_os.path.join(_ROOT_PATH, 'Applications', 'Sublime Text 2.app')],
631 'sublime-text', 'Sublime Text'),
632 ([_os.path.join(_ROOT_PATH, 'Applications', 'TextMate.app')],
633 'textmate', 'TextMate'),
634 ([_os.path.join(_ROOT_PATH, 'Applications', 'TextWrangler.app')],
635 'textwrangler', 'TextWrangler'),
636 ([_os.path.join(_ROOT_PATH, 'Applications', 'Safari.app')],
638 ([_os.path.join(_ROOT_PATH, 'Applications', 'Xcode.app'), # OS X >=1.7
639 _os.path.join(_ROOT_PATH, 'Developer', 'Applications', 'Xcode.app'
646 CHECKER[name] = PathCommandDependency(
647 command=None, paths=paths, name=name, long_name=long_name)
648 del paths, name, long_name # cleanup namespace
651 class PythonPackageDependency (Dependency):
652 def __init__(self, package, **kwargs):
653 if 'name' not in kwargs:
654 kwargs['name'] = package
655 if 'and_dependencies' not in kwargs:
656 kwargs['and_dependencies'] = []
657 if 'python' not in kwargs['and_dependencies']:
658 kwargs['and_dependencies'].append('python')
659 super(PythonPackageDependency, self).__init__(**kwargs)
660 self.package = package
662 def _get_version(self):
663 package = self._get_package(self.package)
664 return self._get_version_from_package(package)
666 def _get_package(self, package):
668 return _importlib.import_module(package)
669 except ImportError as e:
670 raise DependencyError(
672 message="could not import the '{0}' package for {1}".format(
673 package, self.full_name()),
676 def _get_version_from_package(self, package):
678 version = package.__version__
679 except AttributeError:
684 for package,name,long_name,minimum_version,and_dependencies in [
685 ('nose', None, 'Nose Python package',
686 CHECKER['nosetests'].minimum_version, None),
687 ('pytest', None, 'pytest Python package',
688 CHECKER['py.test'].minimum_version, None),
689 ('jinja2', 'jinja', 'Jinja', (2, 6), None),
690 ('zmq', 'pyzmq', 'PyZMQ', (2, 1, 4), None),
691 ('IPython', None, 'IPython Python package',
692 CHECKER['ipython'].minimum_version, ['jinja', 'tornado', 'pyzmq']),
693 ('argparse', None, 'Argparse', None, None),
694 ('numpy', None, 'NumPy', None, None),
695 ('scipy', None, 'SciPy', None, None),
696 ('matplotlib', None, 'Matplotlib', None, None),
697 ('pandas', None, 'Pandas', (0, 8), None),
698 ('sympy', None, 'SymPy', None, None),
699 ('Cython', None, None, None, None),
700 ('networkx', None, 'NetworkX', None, None),
701 ('mayavi.mlab', None, 'MayaVi', None, None),
702 ('setuptools', None, 'Setuptools', None, None),
710 kwargs['and_dependencies'] = and_dependencies
711 CHECKER[name] = PythonPackageDependency(
712 package=package, name=name, long_name=long_name,
713 minimum_version=minimum_version, **kwargs)
715 del package, name, long_name, minimum_version, and_dependencies, kwargs
718 class MercurialPythonPackage (PythonPackageDependency):
719 def _get_version(self):
720 try: # mercurial >= 1.2
721 package = _importlib.import_module('mercurial.util')
722 except ImportError as e: # mercurial <= 1.1.2
723 package = self._get_package('mercurial.version')
724 return package.get_version()
726 return package.version()
729 CHECKER['mercurial'] = MercurialPythonPackage(
730 package='mercurial.util', name='mercurial',
731 long_name='Mercurial Python package',
732 minimum_version=CHECKER['hg'].minimum_version)
735 class TornadoPythonPackage (PythonPackageDependency):
736 def _get_version_from_package(self, package):
737 return package.version
739 def _get_parsed_version(self):
740 package = self._get_package(self.package)
741 return package.version_info
744 CHECKER['tornado'] = TornadoPythonPackage(
745 package='tornado', name='tornado', long_name='Tornado', minimum_version=(2, 0))
748 class SQLitePythonPackage (PythonPackageDependency):
749 def _get_version_from_package(self, package):
752 def _get_parsed_version(self):
753 return _sys.version_info
756 CHECKER['sqlite3-python'] = SQLitePythonPackage(
757 package='sqlite3', name='sqlite3-python',
758 long_name='SQLite Python package',
759 minimum_version=CHECKER['sqlite3'].minimum_version)
762 class UserTaskDependency (Dependency):
763 "Prompt the user to complete a task and check for success"
764 def __init__(self, prompt, **kwargs):
765 super(UserTaskDependency, self).__init__(**kwargs)
769 if _sys.version_info >= (3, ):
770 result = input(self.prompt)
772 result = raw_input(self.prompt)
773 return self._check_result(result)
775 def _check_result(self, result):
776 raise NotImplementedError()
779 class EditorTaskDependency (UserTaskDependency):
780 def __init__(self, **kwargs):
781 self.path = _os.path.expanduser(_os.path.join(
782 '~', 'swc-installation-test.txt'))
783 self.contents = 'Hello, world!'
784 super(EditorTaskDependency, self).__init__(
786 'Open your favorite text editor and create the file\n'
788 'containing the line:\n'
790 'Press enter here after you have done this.\n'
791 'You may remove the file after you have finished testing.'
792 ).format(self.path, self.contents),
795 def _check_result(self, result):
798 with open(self.path, 'r') as f:
801 raise DependencyError(
803 message='could not open {0!r}: {1}'.format(self.path, e)
805 if contents.strip() != self.contents:
806 raise DependencyError(
809 'file contents ({0!r}) did not match the expected {1!r}'
810 ).format(contents, self.contents))
813 CHECKER['other-editor'] = EditorTaskDependency(
814 name='other-editor', long_name='')
817 class VirtualDependency (Dependency):
819 return '{0} {1}'.format(
820 self.or_pass['dependency'].full_name(),
821 self.or_pass['version'])
824 for name,long_name,dependencies in [
825 ('virtual-shell', 'command line shell', (
835 ('virtual-editor', 'text/code editor', (
847 'other-editor', # last because it requires user interaction
849 ('virtual-browser', 'web browser', (
855 ('virtual-pypi-installer', 'PyPI installer', (
860 CHECKER[name] = VirtualDependency(
861 name=name, long_name=long_name, or_dependencies=dependencies)
862 del name, long_name, dependencies # cleanup namespace
865 def _print_info(key, value, indent=19):
866 print('{0}{1}: {2}'.format(key, ' '*(indent-len(key)), value))
868 def print_system_info():
869 print("If you do not understand why the above failures occurred,")
870 print("copy and send the *entire* output (all info above and summary")
871 print("below) to the instructor for help.")
873 print('==================')
874 print('System information')
875 print('==================')
876 _print_info('os.name', _os.name)
877 _print_info('os.uname', _platform.uname())
878 _print_info('platform', _sys.platform)
879 _print_info('platform+', _platform.platform())
881 'linux_distribution',
885 value = getattr(_platform, pversion)()
887 _print_info(pversion, value)
888 _print_info('prefix', _sys.prefix)
889 _print_info('exec_prefix', _sys.exec_prefix)
890 _print_info('executable', _sys.executable)
891 _print_info('version_info', _sys.version_info)
892 _print_info('version', _sys.version)
893 _print_info('environment', '')
894 for key,value in sorted(_os.environ.items()):
895 print(' {0}={1}'.format(key, value))
896 print('==================')
898 def print_suggestions(instructor_fallback=True):
900 print('For suggestions on installing missing packages, see')
901 print('http://software-carpentry.org/setup/')
903 print('For instructings on installing a particular package,')
904 print('see the failure message for that package printed above.')
905 if instructor_fallback:
907 print('For help, email the *entire* output of this script to')
908 print('your instructor.')
911 if __name__ == '__main__':
912 import optparse as _optparse
914 parser = _optparse.OptionParser(usage='%prog [options] [check...]')
916 parser.format_epilog = lambda formatter: '\n' + epilog
918 '-v', '--verbose', action='store_true',
919 help=('print additional information to help troubleshoot '
920 'installation issues'))
921 options,args = parser.parse_args()
924 except InvalidCheck as e:
925 print("I don't know how to check for {0!r}".format(e.check))
926 print('I do know how to check for:')
927 for key,checker in sorted(CHECKER.items()):
928 if checker.long_name != checker.name:
929 print(' {0} {1}({2})'.format(
930 key, ' '*(20-len(key)), checker.long_name))
932 print(' {0}'.format(key))
938 print_suggestions(instructor_fallback=True)