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()
53 import xml.etree.ElementTree as _element_tree
56 if not hasattr(_shlex, 'quote'): # Python versions older than 3.3
57 # Use the undocumented pipes.quote()
58 import pipes as _pipes
59 _shlex.quote = _pipes.quote
64 # Comment out any entries you don't need
74 'hg', # Command line tool
75 #'mercurial', # Python package
77 # Build tools and packaging
79 'virtual-pypi-installer',
83 'nosetests', # Command line tool
84 'nose', # Python package
85 'py.test', # Command line tool
86 'pytest', # Python package
88 'sqlite3', # Command line tool
89 'sqlite3-python', # Python package
92 'ipython', # Command line tool
93 'IPython', # Python package
94 'argparse', # Useful for utility scripts
105 # setup page. Leave this alone to use the stock workshop template's
106 # setup instructions, point at your workshop page to use your
107 # workshop's setup instructions, or set to None to only link to the
108 # upstream project's setup instructions.
109 SETUP_URL = 'https://swcarpentry.github.io/workshop-template/'
111 # setup ID scheme (e.g. map nano issue to "editor")
112 SETUP_IDS = { # list unpredictable IDs in this dict literal
113 ('Windows', '*', 'notepad++'): 'editor-windows',
114 ('Darwin', '*', 'textwrangler'): 'editor-macosx',
115 ('Darwin', '*', 'sublime-text'): 'editor-macosx',
116 ('Linux', '*', 'kate'): 'editor-linux',
119 # add predictable {system}-{check} IDs to SETUP_IDS
120 for check, check_id in [
124 ('python', 'python'),
127 for system, system_id in [
129 ('Darwin', 'macosx'),
130 ('Windows', 'windows'),
132 SETUP_IDS[(system, '*', check)] = '{0}-{1}'.format(check_id, system_id)
137 if _platform.system() == 'win32':
141 class InvalidCheck (KeyError):
142 def __init__(self, check):
143 super(InvalidCheck, self).__init__(check)
150 class DependencyError (Exception):
151 _default_url = SETUP_URL
152 _setup_urls = { # (system, version, package) glob pairs
153 ('*', '*', 'Cython'): 'http://docs.cython.org/src/quickstart/install.html',
154 ('Linux', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-linux',
155 ('Darwin', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-mac',
156 ('Windows', '*', 'EasyMercurial'): 'http://easyhg.org/download.html#download-windows',
157 ('*', '*', 'EasyMercurial'): 'http://easyhg.org/download.html',
158 ('*', '*', 'argparse'): 'https://pypi.python.org/pypi/argparse#installation',
159 ('*', '*', 'ash'): 'http://www.in-ulm.de/~mascheck/various/ash/',
160 ('*', '*', 'bash'): 'http://www.gnu.org/software/bash/manual/html_node/Basic-Installation.html#Basic-Installation',
161 ('Linux', '*', 'chromium'): 'http://code.google.com/p/chromium/wiki/LinuxBuildInstructions',
162 ('Darwin', '*', 'chromium'): 'http://code.google.com/p/chromium/wiki/MacBuildInstructions',
163 ('Windows', '*', 'chromium'): 'http://www.chromium.org/developers/how-tos/build-instructions-windows',
164 ('*', '*', 'chromium'): 'http://www.chromium.org/developers/how-tos',
165 ('Windows', '*', 'emacs'): 'http://www.gnu.org/software/emacs/windows/Installing-Emacs.html',
166 ('*', '*', 'emacs'): 'http://www.gnu.org/software/emacs/#Obtaining',
167 ('*', '*', 'firefox'): 'http://www.mozilla.org/en-US/firefox/new/',
168 ('Linux', '*', 'gedit'): 'http://www.linuxfromscratch.org/blfs/view/svn/gnome/gedit.html',
169 ('*', '*', 'git'): 'http://git-scm.com/downloads',
170 ('*', '*', 'google-chrome'): 'https://www.google.com/intl/en/chrome/browser/',
171 ('*', '*', 'hg'): 'http://mercurial.selenic.com/',
172 ('*', '*', 'mercurial'): 'http://mercurial.selenic.com/',
173 ('*', '*', 'IPython'): 'http://ipython.org/install.html',
174 ('*', '*', 'ipython'): 'http://ipython.org/install.html',
175 ('*', '*', 'jinja'): 'http://jinja.pocoo.org/docs/intro/#installation',
176 ('*', '*', 'kate'): 'http://kate-editor.org/get-it/',
177 ('*', '*', 'make'): 'http://www.gnu.org/software/make/',
178 ('Darwin', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#building-on-osx',
179 ('Windows', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#installing-on-windows',
180 ('*', '*', 'matplotlib'): 'http://matplotlib.org/users/installing.html#installing',
181 ('*', '*', 'mayavi.mlab'): 'http://docs.enthought.com/mayavi/mayavi/installation.html',
182 ('*', '*', 'nano'): 'http://www.nano-editor.org/dist/latest/faq.html#3',
183 ('*', '*', 'networkx'): 'http://networkx.github.com/documentation/latest/install.html#installing',
184 ('*', '*', 'nose'): 'https://nose.readthedocs.org/en/latest/#installation-and-quick-start',
185 ('*', '*', 'nosetests'): 'https://nose.readthedocs.org/en/latest/#installation-and-quick-start',
186 ('*', '*', 'notepad++'): 'http://notepad-plus-plus.org/download/v6.3.html',
187 ('*', '*', 'numpy'): 'http://docs.scipy.org/doc/numpy/user/install.html',
188 ('*', '*', 'pandas'): 'http://pandas.pydata.org/pandas-docs/stable/install.html',
189 ('*', '*', 'pip'): 'http://www.pip-installer.org/en/latest/installing.html',
190 ('*', '*', 'pytest'): 'http://pytest.org/latest/getting-started.html',
191 ('*', '*', 'python'): 'http://www.python.org/download/releases/2.7.3/#download',
192 ('*', '*', 'pyzmq'): 'https://github.com/zeromq/pyzmq/wiki/Building-and-Installing-PyZMQ',
193 ('*', '*', 'py.test'): 'http://pytest.org/latest/getting-started.html',
194 ('Linux', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Linux',
195 ('Darwin', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Mac_OS_X',
196 ('Windows', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy/Windows',
197 ('*', '*', 'scipy'): 'http://www.scipy.org/Installing_SciPy',
198 ('*', '*', 'setuptools'): 'https://pypi.python.org/pypi/setuptools#installation-instructions',
199 ('*', '*', 'sqlite3'): 'http://www.sqlite.org/download.html',
200 ('*', '*', 'sublime-text'): 'http://www.sublimetext.com/2',
201 ('*', '*', 'sympy'): 'http://docs.sympy.org/dev/install.html',
202 ('Darwin', '*', 'textmate'): 'http://macromates.com/',
203 ('Darwin', '*', 'textwrangler'): 'http://www.barebones.com/products/textwrangler/download.html',
204 ('*', '*', 'tornado'): 'http://www.tornadoweb.org/',
205 ('*', '*', 'vim'): 'http://www.vim.org/download.php',
206 ('Darwin', '*', 'xcode'): 'https://developer.apple.com/xcode/',
207 ('*', '*', 'xemacs'): 'http://www.us.xemacs.org/Install/',
208 ('*', '*', 'zsh'): 'http://www.zsh.org/',
211 def _get_message(self):
213 def _set_message(self, message):
214 self._message = message
215 message = property(_get_message, _set_message)
217 def __init__(self, checker, message, causes=None):
218 super(DependencyError, self).__init__(message)
219 self.checker = checker
220 self.message = message
226 system = _platform.system()
229 'linux_distribution',
233 value = getattr(_platform, pversion)()
237 package = self.checker.name
239 for (s,v,p),id in SETUP_IDS.items():
240 if (_fnmatch.fnmatch(system, s) and
241 _fnmatch.fnmatch(version, v) and
242 _fnmatch.fnmatch(package, p)):
243 urls.append('{0}#{1}'.format(SETUP_URL, id))
244 for (s,v,p),url in self._setup_urls.items():
245 if (_fnmatch.fnmatch(system, s) and
246 _fnmatch.fnmatch(version, v) and
247 _fnmatch.fnmatch(package, p)):
251 return [self._default_url]
254 urls = self.get_urls()
256 'check for {0} failed:'.format(self.checker.full_name()),
258 ' For instructions on installing an up-to-date version, see',
260 lines.extend([' {0}'.format(url) for url in urls])
262 lines.append(' causes:')
263 for cause in self.causes:
264 lines.extend(' ' + line for line in str(cause).splitlines())
265 return '\n'.join(lines)
268 def check(checks=None):
275 checker = CHECKER[check]
276 except KeyError as e:
277 raise InvalidCheck(check)# from e
278 _sys.stdout.write('check {0}...\t'.format(checker.full_name()))
280 version = checker.check()
281 except DependencyError as e:
283 _sys.stdout.write('fail\n')
285 _sys.stdout.write('pass\n')
286 successes.append((checker, version))
288 print('\nSuccesses:\n')
289 for checker,version in successes:
290 print('{0} {1}'.format(
292 version or 'unknown'))
296 for failure in failures:
297 if failure not in printed:
300 printed.append(failure)
305 class Dependency (object):
306 def __init__(self, name, long_name=None, minimum_version=None,
307 version_delimiter='.', and_dependencies=None,
308 or_dependencies=None):
310 self.long_name = long_name or name
311 self.minimum_version = minimum_version
312 self.version_delimiter = version_delimiter
313 if not and_dependencies:
314 and_dependencies = []
315 self.and_dependencies = and_dependencies
316 if not or_dependencies:
318 self.or_dependencies = or_dependencies
319 self._check_error = None
322 return '<{0} {1}>'.format(type(self).__name__, self.name)
325 if self.name == self.long_name:
328 return '{0} ({1})'.format(self.long_name, self.name)
331 if self._check_error:
332 raise self._check_error
334 self._check_dependencies()
336 except DependencyError as e:
337 self._check_error = e # cache for future calls
340 def _check_dependencies(self):
341 for dependency in self.and_dependencies:
342 if not hasattr(dependency, 'check'):
343 dependency = CHECKER[dependency]
346 except DependencyError as e:
347 raise DependencyError(
350 'some dependencies for {0} were not satisfied'
351 ).format(self.full_name()),
355 for dependency in self.or_dependencies:
356 if not hasattr(dependency, 'check'):
357 dependency = CHECKER[dependency]
359 version = dependency.check()
360 except DependencyError as e:
364 'dependency': dependency,
367 break # no need to test other dependencies
368 if self.or_dependencies and not self.or_pass:
369 raise DependencyError(
372 '{0} requires at least one of the following dependencies'
373 ).format(self.full_name()),
377 version = self._get_version()
378 parsed_version = None
379 if hasattr(self, '_get_parsed_version'):
380 parsed_version = self._get_parsed_version()
381 if self.minimum_version:
382 self._check_version(version=version, parsed_version=parsed_version)
385 def _get_version(self):
386 raise NotImplementedError(self)
388 def _minimum_version_string(self):
389 return self.version_delimiter.join(
390 str(part) for part in self.minimum_version)
392 def _check_version(self, version, parsed_version=None):
393 if not parsed_version:
394 parsed_version = self._parse_version(version=version)
395 if not parsed_version or parsed_version < self.minimum_version:
396 raise DependencyError(
398 message='outdated version of {0}: {1} (need >= {2})'.format(
399 self.full_name(), version, self._minimum_version_string()))
401 def _parse_version(self, version):
405 for part in version.split(self.version_delimiter):
407 parsed_version.append(int(part))
408 except ValueError as e:
409 raise DependencyError(
412 'unparsable {0!r} in version {1} of {2}, (need >= {3})'
414 part, version, self.full_name(),
415 self._minimum_version_string()))# from e
416 return tuple(parsed_version)
419 class VirtualDependency (Dependency):
421 return '{0} {1}'.format(
422 self.or_pass['dependency'].full_name(),
423 self.or_pass['version'])
426 class CommandDependency (Dependency):
427 exe_extension = _distutils_ccompiler.new_compiler().exe_extension
429 def __init__(self, command, paths=None, version_options=('--version',),
430 stdin=None, version_regexp=None, version_stream='stdout',
432 if 'name' not in kwargs:
433 kwargs['name'] = command
434 super(CommandDependency, self).__init__(**kwargs)
435 self.command = command
437 self.version_options = version_options
439 if not version_regexp:
440 regexp = r'([\d][\d{0}]*[\d])'.format(self.version_delimiter)
441 version_regexp = _re.compile(regexp)
442 self.version_regexp = version_regexp
443 self.version_stream = version_stream
445 def _get_command_version_stream(self, command=None, stdin=None,
448 command = self.command + (self.exe_extension or '')
452 popen_stdin = _subprocess.PIPE
456 p = _subprocess.Popen(
457 [command] + list(self.version_options), stdin=popen_stdin,
458 stdout=_subprocess.PIPE, stderr=_subprocess.PIPE,
459 universal_newlines=True)
461 raise DependencyError(
463 message="could not find '{0}' executable".format(command),
465 stdout,stderr = p.communicate(stdin)
467 if status not in expect:
469 "failed to execute: {0} {1}".format(
471 ' '.join(_shlex.quote(arg)
472 for arg in self.version_options)),
473 'status: {0}'.format(status),
475 for name,string in [('stdout', stdout), ('stderr', stderr)]:
477 lines.extend([name + ':', string])
478 raise DependencyError(checker=self, message='\n'.join(lines))
479 for name,string in [('stdout', stdout), ('stderr', stderr)]:
480 if name == self.version_stream:
482 raise DependencyError(
484 message='empty version stream on {0} for {1}'.format(
485 self.version_stream, command))
487 raise NotImplementedError(self.version_stream)
489 def _get_version_stream(self, **kwargs):
490 paths = [self.command + (self.exe_extension or '')]
491 if self.exe_extension:
492 paths.append(self.command) # also look at the extension-less path
494 paths.extend(self.paths)
498 return self._get_command_version_stream(command=path, **kwargs)
499 except DependencyError as e:
501 raise DependencyError(
503 message='errors finding {0} version'.format(
507 def _get_version(self):
508 version_stream = self._get_version_stream()
509 match = self.version_regexp.search(version_stream)
511 raise DependencyError(
513 message='no version string in output:\n{0}'.format(
515 return match.group(1)
518 class VersionPlistCommandDependency (CommandDependency):
519 """A command that doesn't support --version or equivalent options
521 On OS X, a command's executable may be hard to find, or not exist
522 in the PATH. Work around that by looking up the version
523 information in the package's version.plist file.
525 def __init__(self, key='CFBundleShortVersionString', **kwargs):
526 super(VersionPlistCommandDependency, self).__init__(**kwargs)
529 def _get_command_version_stream(self, *args, **kwargs):
530 raise NotImplementedError()
532 def _get_version_stream(self, *args, **kwargs):
533 raise NotImplementedError()
536 def _get_parent(root, element):
537 """Returns the parent of this element or None for the root element
539 for node in root.iter():
542 raise ValueError((root, element))
545 def _get_next(cls, root, element):
546 """Returns the following sibling of this element or None
548 parent = cls._get_parent(root=root, element=element)
549 siblings = iter(parent)
550 for node in siblings:
553 return next(siblings)
554 except StopIteration:
558 def _get_version_from_plist(self, path):
559 """Parse the plist and return the value string for self.key
561 tree = _element_tree.parse(source=path)
563 for key in tree.findall('.//key'):
564 value = self._get_next(root=tree, element=key)
565 if value.tag != 'string':
566 raise ValueError((tree, key, value))
567 data[key.text] = value.text
568 return data[self.key]
570 def _get_version(self):
571 for path in self.paths:
572 if _os.path.exists(path):
573 return self._get_version_from_plist(path=path)
574 raise DependencyError(
577 'nothing exists at any of the expected paths for {0}:\n {1}'
580 '\n '.join(p for p in self.paths)))
583 class UserTaskDependency (Dependency):
584 "Prompt the user to complete a task and check for success"
585 def __init__(self, prompt, **kwargs):
586 super(UserTaskDependency, self).__init__(**kwargs)
590 if _sys.version_info >= (3, ):
591 result = input(self.prompt)
593 result = raw_input(self.prompt)
594 return self._check_result(result)
596 def _check_result(self, result):
597 raise NotImplementedError()
600 class EditorTaskDependency (UserTaskDependency):
601 def __init__(self, **kwargs):
602 self.path = _os.path.expanduser(_os.path.join(
603 '~', 'swc-installation-test.txt'))
604 self.contents = 'Hello, world!'
605 super(EditorTaskDependency, self).__init__(
607 'Open your favorite text editor and create the file\n'
609 'containing the line:\n'
611 'Press enter here after you have done this.\n'
612 'You may remove the file after you have finished testing.'
613 ).format(self.path, self.contents),
616 def _check_result(self, result):
619 with open(self.path, 'r') as f:
622 raise DependencyError(
624 message='could not open {0!r}: {1}'.format(self.path, e)
626 if contents.strip() != self.contents:
627 raise DependencyError(
630 'file contents ({0!r}) did not match the expected {1!r}'
631 ).format(contents, self.contents))
634 class MakeDependency (CommandDependency):
635 makefile = '\n'.join([
637 '\t@echo "MAKE_VERSION=$(MAKE_VERSION)"',
638 '\t@echo "MAKE=$(MAKE)"',
642 def _get_version(self):
644 return super(MakeDependency, self)._get_version()
645 except DependencyError as e:
646 version_options = self.version_options
647 self.version_options = ['-f', '-']
649 stream = self._get_version_stream(stdin=self.makefile)
651 for line in stream.splitlines():
653 key,value = line.split('=', 1)
654 except ValueError as ve:
655 raise e# from NotImplementedError(stream)
657 if info.get('MAKE_VERSION', None):
658 return info['MAKE_VERSION']
659 elif info.get('MAKE', None):
663 self.version_options = version_options
666 class EasyInstallDependency (CommandDependency):
667 def _get_version(self):
669 return super(EasyInstallDependency, self)._get_version()
670 except DependencyError as e:
671 version_stream = self.version_stream
673 self.version_stream = 'stderr'
674 stream = self._get_version_stream(expect=(1,))
675 if 'option --version not recognized' in stream:
676 return 'unknown (possibly Setuptools?)'
678 self.version_stream = version_stream
681 class PythonDependency (Dependency):
682 def __init__(self, name='python', long_name='Python version',
683 minimum_version=(2, 6), **kwargs):
684 super(PythonDependency, self).__init__(
685 name=name, long_name=long_name, minimum_version=minimum_version,
688 def _get_version(self):
691 def _get_parsed_version(self):
692 return _sys.version_info
695 class PythonPackageDependency (Dependency):
696 def __init__(self, package, **kwargs):
697 if 'name' not in kwargs:
698 kwargs['name'] = package
699 if 'and_dependencies' not in kwargs:
700 kwargs['and_dependencies'] = []
701 if 'python' not in kwargs['and_dependencies']:
702 kwargs['and_dependencies'].append('python')
703 super(PythonPackageDependency, self).__init__(**kwargs)
704 self.package = package
706 def _get_version(self):
707 package = self._get_package(self.package)
708 return self._get_version_from_package(package)
710 def _get_package(self, package):
712 return _importlib.import_module(package)
713 except ImportError as e:
714 raise DependencyError(
716 message="could not import the '{0}' package for {1}".format(
717 package, self.full_name()),
720 def _get_version_from_package(self, package):
722 version = package.__version__
723 except AttributeError:
728 class MercurialPythonPackage (PythonPackageDependency):
729 def _get_version(self):
730 try: # mercurial >= 1.2
731 package = _importlib.import_module('mercurial.util')
732 except ImportError as e: # mercurial <= 1.1.2
733 package = self._get_package('mercurial.version')
734 return package.get_version()
736 return package.version()
739 class TornadoPythonPackage (PythonPackageDependency):
740 def _get_version_from_package(self, package):
741 return package.version
743 def _get_parsed_version(self):
744 package = self._get_package(self.package)
745 return package.version_info
748 class SQLitePythonPackage (PythonPackageDependency):
749 def _get_version_from_package(self, package):
752 def _get_parsed_version(self):
753 return _sys.version_info
756 def _program_files_paths(*args):
757 "Utility for generating MS Windows search paths"
758 pf = _os.environ.get('ProgramFiles', '/usr/bin')
759 pfx86 = _os.environ.get('ProgramFiles(x86)', pf)
760 paths = [_os.path.join(pf, *args)]
762 paths.append(_os.path.join(pfx86, *args))
766 CHECKER['python'] = PythonDependency()
769 for command,long_name,minimum_version,paths in [
770 ('sh', 'Bourne Shell', None, None),
771 ('ash', 'Almquist Shell', None, None),
772 ('bash', 'Bourne Again Shell', None, None),
773 ('csh', 'C Shell', None, None),
774 ('ksh', 'KornShell', None, None),
775 ('dash', 'Debian Almquist Shell', None, None),
776 ('tcsh', 'TENEX C Shell', None, None),
777 ('zsh', 'Z Shell', None, None),
778 ('git', 'Git', (1, 7, 0), None),
779 ('hg', 'Mercurial', (2, 0, 0), None),
780 ('EasyMercurial', None, (1, 3), None),
781 ('pip', None, None, None),
782 ('sqlite3', 'SQLite 3', None, None),
783 ('nosetests', 'Nose', (1, 0, 0), None),
784 ('ipython', 'IPython script', (1, 0), None),
785 ('emacs', 'Emacs', None, None),
786 ('xemacs', 'XEmacs', None, None),
787 ('vim', 'Vim', None, None),
788 ('vi', None, None, None),
789 ('nano', 'Nano', None, None),
790 ('gedit', None, None, None),
791 ('kate', 'Kate', None, None),
792 ('notepad++', 'Notepad++', None,
793 _program_files_paths('Notepad++', 'notepad++.exe')),
794 ('firefox', 'Firefox', None,
795 _program_files_paths('Mozilla Firefox', 'firefox.exe')),
796 ('google-chrome', 'Google Chrome', None,
797 _program_files_paths('Google', 'Chrome', 'Application', 'chrome.exe')
799 ('chromium', 'Chromium', None, None),
803 CHECKER[command] = CommandDependency(
804 command=command, paths=paths, long_name=long_name,
805 minimum_version=minimum_version)
806 del command, long_name, minimum_version, paths # cleanup namespace
809 CHECKER['make'] = MakeDependency(command='make', minimum_version=None)
812 CHECKER['easy_install'] = EasyInstallDependency(
813 command='easy_install', long_name='Setuptools easy_install',
814 minimum_version=None)
817 CHECKER['py.test'] = CommandDependency(
818 command='py.test', version_stream='stderr',
819 minimum_version=None)
822 for paths,name,long_name in [
823 ([_os.path.join(_ROOT_PATH, 'Applications', 'Sublime Text 2.app',
824 'Contents', 'version.plist')],
825 'sublime-text', 'Sublime Text'),
826 ([_os.path.join(_ROOT_PATH, 'Applications', 'TextMate.app',
827 'Contents', 'version.plist')],
828 'textmate', 'TextMate'),
829 ([_os.path.join(_ROOT_PATH, 'Applications', 'TextWrangler.app',
830 'Contents', 'version.plist')],
831 'textwrangler', 'TextWrangler'),
832 ([_os.path.join(_ROOT_PATH, 'Applications', 'Safari.app',
833 'Contents', 'version.plist')],
835 ([_os.path.join(_ROOT_PATH, 'Applications', 'Xcode.app',
836 'Contents', 'version.plist'), # OS X >=1.7
837 _os.path.join(_ROOT_PATH, 'Developer', 'Applications', 'Xcode.app',
838 'Contents', 'version.plist'), # OS X 1.6,
844 CHECKER[name] = VersionPlistCommandDependency(
845 command=None, paths=paths, name=name, long_name=long_name)
846 del paths, name, long_name # cleanup namespace
849 for package,name,long_name,minimum_version,and_dependencies in [
850 ('nose', None, 'Nose Python package',
851 CHECKER['nosetests'].minimum_version, None),
852 ('pytest', None, 'pytest Python package',
853 CHECKER['py.test'].minimum_version, None),
854 ('jinja2', 'jinja', 'Jinja', (2, 6), None),
855 ('zmq', 'pyzmq', 'PyZMQ', (2, 1, 4), None),
856 ('IPython', None, 'IPython Python package',
857 CHECKER['ipython'].minimum_version, [
862 name='virtual-browser-ipython',
863 long_name='IPython-compatible web browser',
866 command=CHECKER['firefox'].command,
867 paths=CHECKER['firefox'].paths,
868 name='{0}-for-ipython'.format(
869 CHECKER['firefox'].name),
870 long_name='{0} for IPython'.format(
871 CHECKER['firefox'].long_name),
872 minimum_version=(6, 0)),
874 command=CHECKER['google-chrome'].command,
875 paths=CHECKER['google-chrome'].paths,
876 name='{0}-for-ipython'.format(
877 CHECKER['google-chrome'].name),
878 long_name='{0} for IPython'.format(
879 CHECKER['google-chrome'].long_name),
880 minimum_version=(13, 0)),
882 command=CHECKER['chromium'].command,
883 paths=CHECKER['chromium'].paths,
884 name='{0}-for-ipython'.format(
885 CHECKER['chromium'].name),
886 long_name='{0} for IPython'.format(
887 CHECKER['chromium'].long_name),
888 minimum_version=(13, 0)),
889 VersionPlistCommandDependency(
890 command=CHECKER['safari'].command,
891 paths=CHECKER['safari'].paths,
892 key=CHECKER['safari'].key,
893 name='{0}-for-ipython'.format(
894 CHECKER['safari'].name),
895 long_name='{0} for IPython'.format(
896 CHECKER['safari'].long_name),
897 minimum_version=(5, 0)),
900 ('argparse', None, 'Argparse', None, None),
901 ('numpy', None, 'NumPy', None, None),
902 ('scipy', None, 'SciPy', None, None),
903 ('matplotlib', None, 'Matplotlib', None, None),
904 ('pandas', None, 'Pandas', (0, 8), None),
905 ('sympy', None, 'SymPy', None, None),
906 ('Cython', None, None, None, None),
907 ('networkx', None, 'NetworkX', None, None),
908 ('mayavi.mlab', None, 'MayaVi', None, None),
909 ('setuptools', None, 'Setuptools', None, None),
917 kwargs['and_dependencies'] = and_dependencies
918 CHECKER[name] = PythonPackageDependency(
919 package=package, name=name, long_name=long_name,
920 minimum_version=minimum_version, **kwargs)
922 del package, name, long_name, minimum_version, and_dependencies, kwargs
925 CHECKER['mercurial'] = MercurialPythonPackage(
926 package='mercurial.util', name='mercurial',
927 long_name='Mercurial Python package',
928 minimum_version=CHECKER['hg'].minimum_version)
931 CHECKER['tornado'] = TornadoPythonPackage(
932 package='tornado', name='tornado', long_name='Tornado', minimum_version=(2, 0))
935 CHECKER['sqlite3-python'] = SQLitePythonPackage(
936 package='sqlite3', name='sqlite3-python',
937 long_name='SQLite Python package',
938 minimum_version=CHECKER['sqlite3'].minimum_version)
941 CHECKER['other-editor'] = EditorTaskDependency(
942 name='other-editor', long_name='')
945 for name,long_name,dependencies in [
946 ('virtual-shell', 'command line shell', (
956 ('virtual-editor', 'text/code editor', (
968 'other-editor', # last because it requires user interaction
970 ('virtual-browser', 'web browser', (
976 ('virtual-pypi-installer', 'PyPI installer', (
981 CHECKER[name] = VirtualDependency(
982 name=name, long_name=long_name, or_dependencies=dependencies)
983 del name, long_name, dependencies # cleanup namespace
986 def _print_info(key, value, indent=19):
987 print('{0}{1}: {2}'.format(key, ' '*(indent-len(key)), value))
989 def print_system_info():
990 print("If you do not understand why the above failures occurred,")
991 print("copy and send the *entire* output (all info above and summary")
992 print("below) to the instructor for help.")
994 print('==================')
995 print('System information')
996 print('==================')
997 _print_info('os.name', _os.name)
998 _print_info('os.uname', _platform.uname())
999 _print_info('platform', _sys.platform)
1000 _print_info('platform+', _platform.platform())
1002 'linux_distribution',
1006 value = getattr(_platform, pversion)()
1008 _print_info(pversion, value)
1009 _print_info('prefix', _sys.prefix)
1010 _print_info('exec_prefix', _sys.exec_prefix)
1011 _print_info('executable', _sys.executable)
1012 _print_info('version_info', _sys.version_info)
1013 _print_info('version', _sys.version)
1014 _print_info('environment', '')
1015 for key,value in sorted(_os.environ.items()):
1016 print(' {0}={1}'.format(key, value))
1017 print('==================')
1019 def print_suggestions(instructor_fallback=True):
1021 print('For suggestions on installing missing packages, see')
1022 print('http://software-carpentry.org/setup/')
1024 print('For instructings on installing a particular package,')
1025 print('see the failure message for that package printed above.')
1026 if instructor_fallback:
1028 print('For help, email the *entire* output of this script to')
1029 print('your instructor.')
1032 if __name__ == '__main__':
1033 import optparse as _optparse
1035 parser = _optparse.OptionParser(usage='%prog [options] [check...]')
1037 parser.format_epilog = lambda formatter: '\n' + epilog
1039 '-v', '--verbose', action='store_true',
1040 help=('print additional information to help troubleshoot '
1041 'installation issues'))
1042 options,args = parser.parse_args()
1044 passed = check(args)
1045 except InvalidCheck as e:
1046 print("I don't know how to check for {0!r}".format(e.check))
1047 print('I do know how to check for:')
1048 for key,checker in sorted(CHECKER.items()):
1049 if checker.long_name != checker.name:
1050 print(' {0} {1}({2})'.format(
1051 key, ' '*(20-len(key)), checker.long_name))
1053 print(' {0}'.format(key))
1059 print_suggestions(instructor_fallback=True)