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 # Some details about the implementation:
27 # The dependencies are divided into a hierarchy of classes rooted on
28 # Dependency class. You can refer to the code to see which package
29 # comes under which type of dependency.
31 # The CHECKER dictionary stores information about all the dependencies
32 # and CHECKS stores list of the dependencies which are to be checked in
33 # the current workshop.
35 # In the "__name__ == '__main__'" block, we launch all the checks with
36 # check() function, which prints information about the tests as they run
37 # and details about the failures after the tests complete. In case of
38 # failure, the functions print_system_info() and print_suggestions()
39 # are called after this, where the former prints information about the
40 # user's system for debugging purposes while the latter prints some
41 # suggestions to follow.
44 from __future__ import print_function # for Python 2.6 compatibility
46 import distutils.ccompiler as _distutils_ccompiler
47 import fnmatch as _fnmatch
48 try: # Python 2.7 and 3.x
49 import importlib as _importlib
50 except ImportError: # Python 2.6 and earlier
51 class _Importlib (object):
52 """Minimal workarounds for functions we need
55 def import_module(name):
56 module = __import__(name)
57 for n in name.split('.')[1:]:
58 module = getattr(module, n)
60 _importlib = _Importlib()
61 import logging as _logging
63 import platform as _platform
65 import shlex as _shlex
66 import subprocess as _subprocess
69 import urllib.parse as _urllib_parse
70 except ImportError: # Python 2.x
71 import urllib as _urllib_parse # for quote()
72 import xml.etree.ElementTree as _element_tree
75 if not hasattr(_shlex, 'quote'): # Python versions older than 3.3
76 # Use the undocumented pipes.quote()
77 import pipes as _pipes
78 _shlex.quote = _pipes.quote
83 # Comment out any entries you don't need
93 'hg', # Command line tool
94 #'mercurial', # Python package
96 # Build tools and packaging
98 'virtual-pypi-installer',
102 'nosetests', # Command line tool
103 'nose', # Python package
104 'py.test', # Command line tool
105 'pytest', # Python package
107 'sqlite3', # Command line tool
108 'sqlite3-python', # Python package
111 'ipython', # Command line tool
112 'IPython', # Python package
113 'argparse', # Useful for utility scripts
127 if _platform.system() == 'win32':
131 class InvalidCheck (KeyError):
132 def __init__(self, check):
133 super(InvalidCheck, self).__init__(check)
140 class DependencyError (Exception):
141 _default_url = 'http://software-carpentry.org/setup/'
142 _setup_urls = { # (system, version, package) glob pairs
143 ('*', '*', 'Cython'): 'http://docs.cython.org/src/quickstart/install.html',
144 ('Linux', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-linux',
145 ('Darwin', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-mac',
146 ('Windows', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-windows',
147 ('*', '*', 'EasyMercurial'): 'http://easyhg.org/download.html',
148 ('*', '*', 'argparse'): 'https://pypi.python.org/pypi/argparse#installation',
149 ('*', '*', 'ash'): 'http://www.in-ulm.de/~mascheck/various/ash/',
150 ('*', '*', 'bash'): 'http://www.gnu.org/software/bash/manual/html_node/Basic-Installation.html#Basic-Installation',
151 ('Linux', '*', 'chromium'): 'http://code.google.com/p/chromium/wiki/LinuxBuildInstructions',
152 ('Darwin', '*', 'chromium'): 'http://code.google.com/p/chromium/wiki/MacBuildInstructions',
153 ('Windows', '*', 'chromium'): 'http://www.chromium.org/developers/how-tos/build-instructions-windows',
154 ('*', '*', 'chromium'): 'http://www.chromium.org/developers/how-tos',
155 ('Windows', '*', 'emacs'): 'http://www.gnu.org/software/emacs/windows/Installing-Emacs.html',
156 ('*', '*', 'emacs'): 'http://www.gnu.org/software/emacs/#Obtaining',
157 ('*', '*', 'firefox'): 'http://www.mozilla.org/en-US/firefox/new/',
158 ('Linux', '*', 'gedit'): 'http://www.linuxfromscratch.org/blfs/view/svn/gnome/gedit.html',
159 ('*', '*', 'git'): 'http://git-scm.com/downloads',
160 ('*', '*', 'google-chrome'): 'https://www.google.com/intl/en/chrome/browser/',
161 ('*', '*', 'hg'): 'http://mercurial.selenic.com/',
162 ('*', '*', 'mercurial'): 'http://mercurial.selenic.com/',
163 ('*', '*', 'IPython'): 'http://ipython.org/install.html',
164 ('*', '*', 'ipython'): 'http://ipython.org/install.html',
165 ('*', '*', 'jinja'): 'http://jinja.pocoo.org/docs/intro/#installation',
166 ('*', '*', 'kate'): 'http://kate-editor.org/get-it/',
167 ('*', '*', 'make'): 'http://www.gnu.org/software/make/',
168 ('Darwin', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#building-on-osx',
169 ('Windows', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#installing-on-windows',
170 ('*', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#installing',
171 ('*', '*', 'mayavi.mlab'): 'http://docs.enthought.com/mayavi/mayavi/installation.html',
172 ('*', '*', 'nano'): 'http://www.nano-editor.org/dist/latest/faq.html#3',
173 ('*', '*', 'networkx'): 'http://networkx.github.com/documentation/latest/install.html#installing',
174 ('*', '*', 'nose'): 'https://nose.readthedocs.org/en/latest/#installation-and-quick-start',
175 ('*', '*', 'nosetests'): 'https://nose.readthedocs.org/en/latest/#installation-and-quick-start',
176 ('*', '*', 'notepad++'): 'http://notepad-plus-plus.org/download/v6.3.html',
177 ('*', '*', 'numpy'): 'http://docs.scipy.org/doc/numpy/user/install.html',
178 ('*', '*', 'pandas'): 'http://pandas.pydata.org/pandas-docs/stable/install.html',
179 ('*', '*', 'pip'): 'http://www.pip-installer.org/en/latest/installing.html',
180 ('*', '*', 'pytest'): 'http://pytest.org/latest/getting-started.html',
181 ('*', '*', 'python'): 'http://www.python.org/download/releases/2.7.3/#download',
182 ('*', '*', 'pyzmq'): 'https://github.com/zeromq/pyzmq/wiki/Building-and-Installing-PyZMQ',
183 ('*', '*', 'py.test'): 'http://pytest.org/latest/getting-started.html',
184 ('Linux', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Linux',
185 ('Darwin', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Mac_OS_X',
186 ('Windows', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Windows',
187 ('*', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy',
188 ('*', '*', 'setuptools'): 'https://pypi.python.org/pypi/setuptools#installation-instructions',
189 ('*', '*', 'sqlite3'): 'http://www.sqlite.org/download.html',
190 ('*', '*', 'sublime-text'): 'http://www.sublimetext.com/2',
191 ('*', '*', 'sympy'): 'http://docs.sympy.org/dev/install.html',
192 ('Darwin', '*', 'textmate'): 'http://macromates.com/',
193 ('Darwin', '*', 'textwrangler'): 'http://www.barebones.com/products/textwrangler/download.html',
194 ('*', '*', 'tornado'): 'http://www.tornadoweb.org/',
195 ('*', '*', 'vim'): 'http://www.vim.org/download.php',
196 ('Darwin', '*', 'xcode'): 'https://developer.apple.com/xcode/',
197 ('*', '*', 'xemacs'): 'http://www.us.xemacs.org/Install/',
198 ('*', '*', 'zsh'): 'http://www.zsh.org/',
201 def _get_message(self):
203 def _set_message(self, message):
204 self._message = message
205 message = property(_get_message, _set_message)
207 def __init__(self, checker, message, causes=None):
208 super(DependencyError, self).__init__(message)
209 self.checker = checker
210 self.message = message
216 system = _platform.system()
219 'linux_distribution',
223 value = getattr(_platform, pversion)()
227 package = self.checker.name
228 for (s,v,p),url in self._setup_urls.items():
229 if (_fnmatch.fnmatch(system, s) and
230 _fnmatch.fnmatch(version, v) and
231 _fnmatch.fnmatch(package, p)):
233 return self._default_url
238 'check for {0} failed:'.format(self.checker.full_name()),
240 ' For instructions on installing an up-to-date version, see',
244 lines.append(' causes:')
245 for cause in self.causes:
246 lines.extend(' ' + line for line in str(cause).splitlines())
247 return '\n'.join(lines)
250 def check(checks=None):
257 checker = CHECKER[check]
258 except KeyError as e:
259 raise InvalidCheck(check)# from e
260 _sys.stdout.write('check {0}...\t'.format(checker.full_name()))
262 version = checker.check()
263 except DependencyError as e:
265 _sys.stdout.write('fail\n')
267 _sys.stdout.write('pass\n')
268 successes.append((checker, version))
270 print('\nSuccesses:\n')
271 for checker,version in successes:
272 print('{0} {1}'.format(
274 version or 'unknown'))
278 for failure in failures:
279 if failure not in printed:
282 printed.append(failure)
287 class Dependency (object):
288 def __init__(self, name, long_name=None, minimum_version=None,
289 version_delimiter='.', and_dependencies=None,
290 or_dependencies=None):
292 self.long_name = long_name or name
293 self.minimum_version = minimum_version
294 self.version_delimiter = version_delimiter
295 if not and_dependencies:
296 and_dependencies = []
297 self.and_dependencies = and_dependencies
298 if not or_dependencies:
300 self.or_dependencies = or_dependencies
301 self._check_error = None
304 return '<{0} {1}>'.format(type(self).__name__, self.name)
307 if self.name == self.long_name:
310 return '{0} ({1})'.format(self.long_name, self.name)
313 if self._check_error:
314 raise self._check_error
316 self._check_dependencies()
318 except DependencyError as e:
319 self._check_error = e # cache for future calls
322 def _check_dependencies(self):
323 for dependency in self.and_dependencies:
324 if not hasattr(dependency, 'check'):
325 dependency = CHECKER[dependency]
328 except DependencyError as e:
329 raise DependencyError(
332 'some dependencies for {0} were not satisfied'
333 ).format(self.full_name()),
337 for dependency in self.or_dependencies:
338 if not hasattr(dependency, 'check'):
339 dependency = CHECKER[dependency]
341 version = dependency.check()
342 except DependencyError as e:
346 'dependency': dependency,
349 break # no need to test other dependencies
350 if self.or_dependencies and not self.or_pass:
351 raise DependencyError(
354 '{0} requires at least one of the following dependencies'
355 ).format(self.full_name()),
359 version = self._get_version()
360 parsed_version = None
361 if hasattr(self, '_get_parsed_version'):
362 parsed_version = self._get_parsed_version()
363 if self.minimum_version:
364 self._check_version(version=version, parsed_version=parsed_version)
367 def _get_version(self):
368 raise NotImplementedError(self)
370 def _minimum_version_string(self):
371 return self.version_delimiter.join(
372 str(part) for part in self.minimum_version)
374 def _check_version(self, version, parsed_version=None):
375 if not parsed_version:
376 parsed_version = self._parse_version(version=version)
377 if not parsed_version or parsed_version < self.minimum_version:
378 raise DependencyError(
380 message='outdated version of {0}: {1} (need >= {2})'.format(
381 self.full_name(), version, self._minimum_version_string()))
383 def _parse_version(self, version):
387 for part in version.split(self.version_delimiter):
389 parsed_version.append(int(part))
390 except ValueError as e:
391 raise DependencyError(
394 'unparsable {0!r} in version {1} of {2}, (need >= {3})'
396 part, version, self.full_name(),
397 self._minimum_version_string()))# from e
398 return tuple(parsed_version)
401 class VirtualDependency (Dependency):
403 return '{0} {1}'.format(
404 self.or_pass['dependency'].full_name(),
405 self.or_pass['version'])
408 class CommandDependency (Dependency):
409 exe_extension = _distutils_ccompiler.new_compiler().exe_extension
411 def __init__(self, command, paths=None, version_options=('--version',),
412 stdin=None, version_regexp=None, version_stream='stdout',
414 if 'name' not in kwargs:
415 kwargs['name'] = command
416 super(CommandDependency, self).__init__(**kwargs)
417 self.command = command
419 self.version_options = version_options
421 if not version_regexp:
422 regexp = r'([\d][\d{0}]*[\d])'.format(self.version_delimiter)
423 version_regexp = _re.compile(regexp)
424 self.version_regexp = version_regexp
425 self.version_stream = version_stream
427 def _get_command_version_stream(self, command=None, stdin=None,
430 command = self.command + (self.exe_extension or '')
434 popen_stdin = _subprocess.PIPE
438 p = _subprocess.Popen(
439 [command] + list(self.version_options), stdin=popen_stdin,
440 stdout=_subprocess.PIPE, stderr=_subprocess.PIPE,
441 universal_newlines=True)
443 raise DependencyError(
445 message="could not find '{0}' executable".format(command),
447 stdout,stderr = p.communicate(stdin)
449 if status not in expect:
451 "failed to execute: {0} {1}".format(
453 ' '.join(_shlex.quote(arg)
454 for arg in self.version_options)),
455 'status: {0}'.format(status),
457 for name,string in [('stdout', stdout), ('stderr', stderr)]:
459 lines.extend([name + ':', string])
460 raise DependencyError(checker=self, message='\n'.join(lines))
461 for name,string in [('stdout', stdout), ('stderr', stderr)]:
462 if name == self.version_stream:
464 raise DependencyError(
466 message='empty version stream on {0} for {1}'.format(
467 self.version_stream, command))
469 raise NotImplementedError(self.version_stream)
471 def _get_version_stream(self, **kwargs):
472 paths = [self.command + (self.exe_extension or '')]
473 if self.exe_extension:
474 paths.append(self.command) # also look at the extension-less path
476 paths.extend(self.paths)
480 return self._get_command_version_stream(command=path, **kwargs)
481 except DependencyError as e:
483 raise DependencyError(
485 message='errors finding {0} version'.format(
489 def _get_version(self):
490 version_stream = self._get_version_stream()
491 match = self.version_regexp.search(version_stream)
493 raise DependencyError(
495 message='no version string in output:\n{0}'.format(
497 return match.group(1)
500 class VersionPlistCommandDependency (CommandDependency):
501 """A command that doesn't support --version or equivalent options
503 On OS X, a command's executable may be hard to find, or not exist
504 in the PATH. Work around that by looking up the version
505 information in the package's version.plist file.
507 def __init__(self, key='CFBundleShortVersionString', **kwargs):
508 super(VersionPlistCommandDependency, self).__init__(**kwargs)
511 def _get_command_version_stream(self, *args, **kwargs):
512 raise NotImplementedError()
514 def _get_version_stream(self, *args, **kwargs):
515 raise NotImplementedError()
518 def _get_parent(root, element):
519 """Returns the parent of this element or None for the root element
521 for node in root.iter():
524 raise ValueError((root, element))
527 def _get_next(cls, root, element):
528 """Returns the following sibling of this element or None
530 parent = cls._get_parent(root=root, element=element)
531 siblings = iter(parent)
532 for node in siblings:
535 return next(siblings)
536 except StopIteration:
540 def _get_version_from_plist(self, path):
541 """Parse the plist and return the value string for self.key
543 tree = _element_tree.parse(source=path)
545 for key in tree.findall('.//key'):
546 value = self._get_next(root=tree, element=key)
547 if value.tag != 'string':
548 raise ValueError((tree, key, value))
549 data[key.text] = value.text
550 return data[self.key]
552 def _get_version(self):
553 for path in self.paths:
554 if _os.path.exists(path):
555 return self._get_version_from_plist(path=path)
556 raise DependencyError(
559 'nothing exists at any of the expected paths for {0}:\n {1}'
562 '\n '.join(p for p in self.paths)))
565 class UserTaskDependency (Dependency):
566 "Prompt the user to complete a task and check for success"
567 def __init__(self, prompt, **kwargs):
568 super(UserTaskDependency, self).__init__(**kwargs)
572 if _sys.version_info >= (3, ):
573 result = input(self.prompt)
575 result = raw_input(self.prompt)
576 return self._check_result(result)
578 def _check_result(self, result):
579 raise NotImplementedError()
582 class EditorTaskDependency (UserTaskDependency):
583 def __init__(self, **kwargs):
584 self.path = _os.path.expanduser(_os.path.join(
585 '~', 'swc-installation-test.txt'))
586 self.contents = 'Hello, world!'
587 super(EditorTaskDependency, self).__init__(
589 'Open your favorite text editor and create the file\n'
591 'containing the line:\n'
593 'Press enter here after you have done this.\n'
594 'You may remove the file after you have finished testing.'
595 ).format(self.path, self.contents),
598 def _check_result(self, result):
601 with open(self.path, 'r') as f:
604 raise DependencyError(
606 message='could not open {0!r}: {1}'.format(self.path, e)
608 if contents.strip() != self.contents:
609 raise DependencyError(
612 'file contents ({0!r}) did not match the expected {1!r}'
613 ).format(contents, self.contents))
616 class MakeDependency (CommandDependency):
617 makefile = '\n'.join([
619 '\t@echo "MAKE_VERSION=$(MAKE_VERSION)"',
620 '\t@echo "MAKE=$(MAKE)"',
624 def _get_version(self):
626 return super(MakeDependency, self)._get_version()
627 except DependencyError as e:
628 version_options = self.version_options
629 self.version_options = ['-f', '-']
631 stream = self._get_version_stream(stdin=self.makefile)
633 for line in stream.splitlines():
635 key,value = line.split('=', 1)
636 except ValueError as ve:
637 raise e# from NotImplementedError(stream)
639 if info.get('MAKE_VERSION', None):
640 return info['MAKE_VERSION']
641 elif info.get('MAKE', None):
645 self.version_options = version_options
648 class EasyInstallDependency (CommandDependency):
649 def _get_version(self):
651 return super(EasyInstallDependency, self)._get_version()
652 except DependencyError as e:
653 version_stream = self.version_stream
655 self.version_stream = 'stderr'
656 stream = self._get_version_stream(expect=(1,))
657 if 'option --version not recognized' in stream:
658 return 'unknown (possibly Setuptools?)'
660 self.version_stream = version_stream
663 class PythonDependency (Dependency):
664 def __init__(self, name='python', long_name='Python version',
665 minimum_version=(2, 6), **kwargs):
666 super(PythonDependency, self).__init__(
667 name=name, long_name=long_name, minimum_version=minimum_version,
670 def _get_version(self):
673 def _get_parsed_version(self):
674 return _sys.version_info
677 class PythonPackageDependency (Dependency):
678 def __init__(self, package, **kwargs):
679 if 'name' not in kwargs:
680 kwargs['name'] = package
681 if 'and_dependencies' not in kwargs:
682 kwargs['and_dependencies'] = []
683 if 'python' not in kwargs['and_dependencies']:
684 kwargs['and_dependencies'].append('python')
685 super(PythonPackageDependency, self).__init__(**kwargs)
686 self.package = package
688 def _get_version(self):
689 package = self._get_package(self.package)
690 return self._get_version_from_package(package)
692 def _get_package(self, package):
694 return _importlib.import_module(package)
695 except ImportError as e:
696 raise DependencyError(
698 message="could not import the '{0}' package for {1}".format(
699 package, self.full_name()),
702 def _get_version_from_package(self, package):
704 version = package.__version__
705 except AttributeError:
710 class MercurialPythonPackage (PythonPackageDependency):
711 def _get_version(self):
712 try: # mercurial >= 1.2
713 package = _importlib.import_module('mercurial.util')
714 except ImportError as e: # mercurial <= 1.1.2
715 package = self._get_package('mercurial.version')
716 return package.get_version()
718 return package.version()
721 class TornadoPythonPackage (PythonPackageDependency):
722 def _get_version_from_package(self, package):
723 return package.version
725 def _get_parsed_version(self):
726 package = self._get_package(self.package)
727 return package.version_info
730 class SQLitePythonPackage (PythonPackageDependency):
731 def _get_version_from_package(self, package):
734 def _get_parsed_version(self):
735 return _sys.version_info
738 def _program_files_paths(*args):
739 "Utility for generating MS Windows search paths"
740 pf = _os.environ.get('ProgramFiles', '/usr/bin')
741 pfx86 = _os.environ.get('ProgramFiles(x86)', pf)
742 paths = [_os.path.join(pf, *args)]
744 paths.append(_os.path.join(pfx86, *args))
748 CHECKER['python'] = PythonDependency()
751 for command,long_name,minimum_version,paths in [
752 ('sh', 'Bourne Shell', None, None),
753 ('ash', 'Almquist Shell', None, None),
754 ('bash', 'Bourne Again Shell', None, None),
755 ('csh', 'C Shell', None, None),
756 ('ksh', 'KornShell', None, None),
757 ('dash', 'Debian Almquist Shell', None, None),
758 ('tcsh', 'TENEX C Shell', None, None),
759 ('zsh', 'Z Shell', None, None),
760 ('git', 'Git', (1, 7, 0), None),
761 ('hg', 'Mercurial', (2, 0, 0), None),
762 ('EasyMercurial', None, (1, 3), None),
763 ('pip', None, None, None),
764 ('sqlite3', 'SQLite 3', None, None),
765 ('nosetests', 'Nose', (1, 0, 0), None),
766 ('ipython', 'IPython script', (1, 0), None),
767 ('emacs', 'Emacs', None, None),
768 ('xemacs', 'XEmacs', None, None),
769 ('vim', 'Vim', None, None),
770 ('vi', None, None, None),
771 ('nano', 'Nano', None, None),
772 ('gedit', None, None, None),
773 ('kate', 'Kate', None, None),
774 ('notepad++', 'Notepad++', None,
775 _program_files_paths('Notepad++', 'notepad++.exe')),
776 ('firefox', 'Firefox', None,
777 _program_files_paths('Mozilla Firefox', 'firefox.exe')),
778 ('google-chrome', 'Google Chrome', None,
779 _program_files_paths('Google', 'Chrome', 'Application', 'chrome.exe')
781 ('chromium', 'Chromium', None, None),
785 CHECKER[command] = CommandDependency(
786 command=command, paths=paths, long_name=long_name,
787 minimum_version=minimum_version)
788 del command, long_name, minimum_version, paths # cleanup namespace
791 CHECKER['make'] = MakeDependency(command='make', minimum_version=None)
794 CHECKER['easy_install'] = EasyInstallDependency(
795 command='easy_install', long_name='Setuptools easy_install',
796 minimum_version=None)
799 CHECKER['py.test'] = CommandDependency(
800 command='py.test', version_stream='stderr',
801 minimum_version=None)
804 for paths,name,long_name in [
805 ([_os.path.join(_ROOT_PATH, 'Applications', 'Sublime Text 2.app',
806 'Contents', 'version.plist')],
807 'sublime-text', 'Sublime Text'),
808 ([_os.path.join(_ROOT_PATH, 'Applications', 'TextMate.app',
809 'Contents', 'version.plist')],
810 'textmate', 'TextMate'),
811 ([_os.path.join(_ROOT_PATH, 'Applications', 'TextWrangler.app',
812 'Contents', 'version.plist')],
813 'textwrangler', 'TextWrangler'),
814 ([_os.path.join(_ROOT_PATH, 'Applications', 'Safari.app',
815 'Contents', 'version.plist')],
817 ([_os.path.join(_ROOT_PATH, 'Applications', 'Xcode.app',
818 'Contents', 'version.plist'), # OS X >=1.7
819 _os.path.join(_ROOT_PATH, 'Developer', 'Applications', 'Xcode.app',
820 'Contents', 'version.plist'), # OS X 1.6,
826 CHECKER[name] = VersionPlistCommandDependency(
827 command=None, paths=paths, name=name, long_name=long_name)
828 del paths, name, long_name # cleanup namespace
831 for package,name,long_name,minimum_version,and_dependencies in [
832 ('nose', None, 'Nose Python package',
833 CHECKER['nosetests'].minimum_version, None),
834 ('pytest', None, 'pytest Python package',
835 CHECKER['py.test'].minimum_version, None),
836 ('jinja2', 'jinja', 'Jinja', (2, 6), None),
837 ('zmq', 'pyzmq', 'PyZMQ', (2, 1, 4), None),
838 ('IPython', None, 'IPython Python package',
839 CHECKER['ipython'].minimum_version, [
844 name='virtual-browser-ipython',
845 long_name='IPython-compatible web browser',
848 command=CHECKER['firefox'].command,
849 paths=CHECKER['firefox'].paths,
850 name='{0}-for-ipython'.format(
851 CHECKER['firefox'].name),
852 long_name='{0} for IPython'.format(
853 CHECKER['firefox'].long_name),
854 minimum_version=(6, 0)),
856 command=CHECKER['google-chrome'].command,
857 paths=CHECKER['google-chrome'].paths,
858 name='{0}-for-ipython'.format(
859 CHECKER['google-chrome'].name),
860 long_name='{0} for IPython'.format(
861 CHECKER['google-chrome'].long_name),
862 minimum_version=(13, 0)),
864 command=CHECKER['chromium'].command,
865 paths=CHECKER['chromium'].paths,
866 name='{0}-for-ipython'.format(
867 CHECKER['chromium'].name),
868 long_name='{0} for IPython'.format(
869 CHECKER['chromium'].long_name),
870 minimum_version=(13, 0)),
871 VersionPlistCommandDependency(
872 command=CHECKER['safari'].command,
873 paths=CHECKER['safari'].paths,
874 key=CHECKER['safari'].key,
875 name='{0}-for-ipython'.format(
876 CHECKER['safari'].name),
877 long_name='{0} for IPython'.format(
878 CHECKER['safari'].long_name),
879 minimum_version=(5, 0)),
882 ('argparse', None, 'Argparse', None, None),
883 ('numpy', None, 'NumPy', None, None),
884 ('scipy', None, 'SciPy', None, None),
885 ('matplotlib', None, 'Matplotlib', None, None),
886 ('pandas', None, 'Pandas', (0, 8), None),
887 ('sympy', None, 'SymPy', None, None),
888 ('Cython', None, None, None, None),
889 ('networkx', None, 'NetworkX', None, None),
890 ('mayavi.mlab', None, 'MayaVi', None, None),
891 ('setuptools', None, 'Setuptools', None, None),
899 kwargs['and_dependencies'] = and_dependencies
900 CHECKER[name] = PythonPackageDependency(
901 package=package, name=name, long_name=long_name,
902 minimum_version=minimum_version, **kwargs)
904 del package, name, long_name, minimum_version, and_dependencies, kwargs
907 CHECKER['mercurial'] = MercurialPythonPackage(
908 package='mercurial.util', name='mercurial',
909 long_name='Mercurial Python package',
910 minimum_version=CHECKER['hg'].minimum_version)
913 CHECKER['tornado'] = TornadoPythonPackage(
914 package='tornado', name='tornado', long_name='Tornado', minimum_version=(2, 0))
917 CHECKER['sqlite3-python'] = SQLitePythonPackage(
918 package='sqlite3', name='sqlite3-python',
919 long_name='SQLite Python package',
920 minimum_version=CHECKER['sqlite3'].minimum_version)
923 CHECKER['other-editor'] = EditorTaskDependency(
924 name='other-editor', long_name='')
927 for name,long_name,dependencies in [
928 ('virtual-shell', 'command line shell', (
938 ('virtual-editor', 'text/code editor', (
950 'other-editor', # last because it requires user interaction
952 ('virtual-browser', 'web browser', (
958 ('virtual-pypi-installer', 'PyPI installer', (
963 CHECKER[name] = VirtualDependency(
964 name=name, long_name=long_name, or_dependencies=dependencies)
965 del name, long_name, dependencies # cleanup namespace
968 def _print_info(key, value, indent=19):
969 print('{0}{1}: {2}'.format(key, ' '*(indent-len(key)), value))
971 def print_system_info():
972 print("If you do not understand why the above failures occurred,")
973 print("copy and send the *entire* output (all info above and summary")
974 print("below) to the instructor for help.")
976 print('==================')
977 print('System information')
978 print('==================')
979 _print_info('os.name', _os.name)
980 _print_info('os.uname', _platform.uname())
981 _print_info('platform', _sys.platform)
982 _print_info('platform+', _platform.platform())
984 'linux_distribution',
988 value = getattr(_platform, pversion)()
990 _print_info(pversion, value)
991 _print_info('prefix', _sys.prefix)
992 _print_info('exec_prefix', _sys.exec_prefix)
993 _print_info('executable', _sys.executable)
994 _print_info('version_info', _sys.version_info)
995 _print_info('version', _sys.version)
996 _print_info('environment', '')
997 for key,value in sorted(_os.environ.items()):
998 print(' {0}={1}'.format(key, value))
999 print('==================')
1001 def print_suggestions(instructor_fallback=True):
1003 print('For suggestions on installing missing packages, see')
1004 print('http://software-carpentry.org/setup/')
1006 print('For instructings on installing a particular package,')
1007 print('see the failure message for that package printed above.')
1008 if instructor_fallback:
1010 print('For help, email the *entire* output of this script to')
1011 print('your instructor.')
1014 if __name__ == '__main__':
1015 import optparse as _optparse
1017 parser = _optparse.OptionParser(usage='%prog [options] [check...]')
1019 parser.format_epilog = lambda formatter: '\n' + epilog
1021 '-v', '--verbose', action='store_true',
1022 help=('print additional information to help troubleshoot '
1023 'installation issues'))
1024 options,args = parser.parse_args()
1026 passed = check(args)
1027 except InvalidCheck as e:
1028 print("I don't know how to check for {0!r}".format(e.check))
1029 print('I do know how to check for:')
1030 for key,checker in sorted(CHECKER.items()):
1031 if checker.long_name != checker.name:
1032 print(' {0} {1}({2})'.format(
1033 key, ' '*(20-len(key)), checker.long_name))
1035 print(' {0}'.format(key))
1041 print_suggestions(instructor_fallback=True)