dependency.
"""
+# Some details about the implementation:
+
+# The dependencies are divided into a hierarchy of classes rooted on
+# Dependency class. You can refer to the code to see which package
+# comes under which type of dependency.
+
+# The CHECKER dictionary stores information about all the dependencies
+# and CHECKS stores list of the dependencies which are to be checked in
+# the current workshop.
+
+# In the "__name__ == '__main__'" block, we launch all the checks with
+# check() function, which prints information about the tests as they run
+# and details about the failures after the tests complete. In case of
+# failure, the functions print_system_info() and print_suggestions()
+# are called after this, where the former prints information about the
+# user's system for debugging purposes while the latter prints some
+# suggestions to follow.
+
+
from __future__ import print_function # for Python 2.6 compatibility
import distutils.ccompiler as _distutils_ccompiler
import urllib.parse as _urllib_parse
except ImportError: # Python 2.x
import urllib as _urllib_parse # for quote()
+import xml.etree.ElementTree as _element_tree
if not hasattr(_shlex, 'quote'): # Python versions older than 3.3
'scipy',
'matplotlib',
'pandas',
- 'sympy',
- 'Cython',
- 'networkx',
- 'mayavi.mlab',
+ #'sympy',
+ #'Cython',
+ #'networkx',
+ #'mayavi.mlab',
]
CHECKER = {}
return tuple(parsed_version)
-class PythonDependency (Dependency):
- def __init__(self, name='python', long_name='Python version',
- minimum_version=(2, 6), **kwargs):
- super(PythonDependency, self).__init__(
- name=name, long_name=long_name, minimum_version=minimum_version,
- **kwargs)
-
- def _get_version(self):
- return _sys.version
-
- def _get_parsed_version(self):
- return _sys.version_info
-
-
-CHECKER['python'] = PythonDependency()
+class VirtualDependency (Dependency):
+ def _check(self):
+ return '{0} {1}'.format(
+ self.or_pass['dependency'].full_name(),
+ self.or_pass['version'])
class CommandDependency (Dependency):
return match.group(1)
-def _program_files_paths(*args):
- "Utility for generating MS Windows search paths"
- pf = _os.environ.get('ProgramFiles', '/usr/bin')
- pfx86 = _os.environ.get('ProgramFiles(x86)', pf)
- paths = [_os.path.join(pf, *args)]
- if pfx86 != pf:
- paths.append(_os.path.join(pfx86, *args))
- return paths
+class VersionPlistCommandDependency (CommandDependency):
+ """A command that doesn't support --version or equivalent options
+ On OS X, a command's executable may be hard to find, or not exist
+ in the PATH. Work around that by looking up the version
+ information in the package's version.plist file.
+ """
+ def __init__(self, key='CFBundleShortVersionString', **kwargs):
+ super(VersionPlistCommandDependency, self).__init__(**kwargs)
+ self.key = key
-for command,long_name,minimum_version,paths in [
- ('sh', 'Bourne Shell', None, None),
- ('ash', 'Almquist Shell', None, None),
- ('bash', 'Bourne Again Shell', None, None),
- ('csh', 'C Shell', None, None),
- ('ksh', 'KornShell', None, None),
- ('dash', 'Debian Almquist Shell', None, None),
- ('tcsh', 'TENEX C Shell', None, None),
- ('zsh', 'Z Shell', None, None),
- ('git', 'Git', (1, 7, 0), None),
- ('hg', 'Mercurial', (2, 0, 0), None),
- ('EasyMercurial', None, (1, 3), None),
- ('pip', None, None, None),
- ('sqlite3', 'SQLite 3', None, None),
- ('nosetests', 'Nose', (1, 0, 0), None),
- ('ipython', 'IPython script', (0, 13), None),
- ('emacs', 'Emacs', None, None),
- ('xemacs', 'XEmacs', None, None),
- ('vim', 'Vim', None, None),
- ('vi', None, None, None),
- ('nano', 'Nano', None, None),
- ('gedit', None, None, None),
- ('kate', 'Kate', None, None),
- ('notepad++', 'Notepad++', None,
- _program_files_paths('Notepad++', 'notepad++.exe')),
- ('firefox', 'Firefox', None,
- _program_files_paths('Mozilla Firefox', 'firefox.exe')),
- ('google-chrome', 'Google Chrome', None,
- _program_files_paths('Google', 'Chrome', 'Application', 'chrome.exe')
- ),
- ('chromium', 'Chromium', None, None),
- ]:
- if not long_name:
- long_name = command
- CHECKER[command] = CommandDependency(
- command=command, paths=paths, long_name=long_name,
- minimum_version=minimum_version)
-del command, long_name, minimum_version, paths # cleanup namespace
+ def _get_command_version_stream(self, *args, **kwargs):
+ raise NotImplementedError()
+
+ def _get_version_stream(self, *args, **kwargs):
+ raise NotImplementedError()
+
+ @staticmethod
+ def _get_parent(root, element):
+ """Returns the parent of this element or None for the root element
+ """
+ for node in root.iter():
+ if element in node:
+ return node
+ raise ValueError((root, element))
+
+ @classmethod
+ def _get_next(cls, root, element):
+ """Returns the following sibling of this element or None
+ """
+ parent = cls._get_parent(root=root, element=element)
+ siblings = iter(parent)
+ for node in siblings:
+ if node == element:
+ try:
+ return next(siblings)
+ except StopIteration:
+ return None
+ return None
+
+ def _get_version_from_plist(self, path):
+ """Parse the plist and return the value string for self.key
+ """
+ tree = _element_tree.parse(source=path)
+ data = {}
+ for key in tree.findall('.//key'):
+ value = self._get_next(root=tree, element=key)
+ if value.tag != 'string':
+ raise ValueError((tree, key, value))
+ data[key.text] = value.text
+ return data[self.key]
+
+ def _get_version(self):
+ for path in self.paths:
+ if _os.path.exists(path):
+ return self._get_version_from_plist(path=path)
+ raise DependencyError(
+ checker=self,
+ message=(
+ 'nothing exists at any of the expected paths for {0}:\n {1}'
+ ).format(
+ self.full_name(),
+ '\n '.join(p for p in self.paths)))
+
+
+class UserTaskDependency (Dependency):
+ "Prompt the user to complete a task and check for success"
+ def __init__(self, prompt, **kwargs):
+ super(UserTaskDependency, self).__init__(**kwargs)
+ self.prompt = prompt
+
+ def _check(self):
+ if _sys.version_info >= (3, ):
+ result = input(self.prompt)
+ else: # Python 2.x
+ result = raw_input(self.prompt)
+ return self._check_result(result)
+
+ def _check_result(self, result):
+ raise NotImplementedError()
+
+
+class EditorTaskDependency (UserTaskDependency):
+ def __init__(self, **kwargs):
+ self.path = _os.path.expanduser(_os.path.join(
+ '~', 'swc-installation-test.txt'))
+ self.contents = 'Hello, world!'
+ super(EditorTaskDependency, self).__init__(
+ prompt=(
+ 'Open your favorite text editor and create the file\n'
+ ' {0}\n'
+ 'containing the line:\n'
+ ' {1}\n'
+ 'Press enter here after you have done this.\n'
+ 'You may remove the file after you have finished testing.'
+ ).format(self.path, self.contents),
+ **kwargs)
+
+ def _check_result(self, result):
+ message = None
+ try:
+ with open(self.path, 'r') as f:
+ contents = f.read()
+ except IOError as e:
+ raise DependencyError(
+ checker=self,
+ message='could not open {0!r}: {1}'.format(self.path, e)
+ )# from e
+ if contents.strip() != self.contents:
+ raise DependencyError(
+ checker=self,
+ message=(
+ 'file contents ({0!r}) did not match the expected {1!r}'
+ ).format(contents, self.contents))
class MakeDependency (CommandDependency):
self.version_options = version_options
-CHECKER['make'] = MakeDependency(command='make', minimum_version=None)
-
-
class EasyInstallDependency (CommandDependency):
def _get_version(self):
try:
self.version_stream = version_stream
-CHECKER['easy_install'] = EasyInstallDependency(
- command='easy_install', long_name='Setuptools easy_install',
- minimum_version=None)
-
-
-CHECKER['py.test'] = CommandDependency(
- command='py.test', version_stream='stderr',
- minimum_version=None)
-
-
-class PathCommandDependency (CommandDependency):
- """A command that doesn't support --version or equivalent options
-
- On some operating systems (e.g. OS X), a command's executable may
- be hard to find, or not exist in the PATH. Work around that by
- just checking for the existence of a characteristic file or
- directory. Since the characteristic path may depend on OS,
- installed version, etc., take a list of paths, and succeed if any
- of them exists.
- """
- def _get_command_version_stream(self, *args, **kwargs):
- raise NotImplementedError()
-
- def _get_version_stream(self, *args, **kwargs):
- raise NotImplementedError()
+class PythonDependency (Dependency):
+ def __init__(self, name='python', long_name='Python version',
+ minimum_version=(2, 6), **kwargs):
+ super(PythonDependency, self).__init__(
+ name=name, long_name=long_name, minimum_version=minimum_version,
+ **kwargs)
def _get_version(self):
- for path in self.paths:
- if _os.path.exists(path):
- return None
- raise DependencyError(
- checker=self,
- message=(
- 'nothing exists at any of the expected paths for {0}:\n {1}'
- ).format(
- self.full_name(),
- '\n '.join(p for p in self.paths)))
-
+ return _sys.version
-for paths,name,long_name in [
- ([_os.path.join(_ROOT_PATH, 'Applications', 'Sublime Text 2.app')],
- 'sublime-text', 'Sublime Text'),
- ([_os.path.join(_ROOT_PATH, 'Applications', 'TextMate.app')],
- 'textmate', 'TextMate'),
- ([_os.path.join(_ROOT_PATH, 'Applications', 'TextWrangler.app')],
- 'textwrangler', 'TextWrangler'),
- ([_os.path.join(_ROOT_PATH, 'Applications', 'Safari.app')],
- 'safari', 'Safari'),
- ([_os.path.join(_ROOT_PATH, 'Applications', 'Xcode.app'), # OS X >=1.7
- _os.path.join(_ROOT_PATH, 'Developer', 'Applications', 'Xcode.app'
- ) # OS X 1.6,
- ],
- 'xcode', 'Xcode'),
- ]:
- if not long_name:
- long_name = name
- CHECKER[name] = PathCommandDependency(
- command=None, paths=paths, name=name, long_name=long_name)
-del paths, name, long_name # cleanup namespace
+ def _get_parsed_version(self):
+ return _sys.version_info
class PythonPackageDependency (Dependency):
return version
+class MercurialPythonPackage (PythonPackageDependency):
+ def _get_version(self):
+ try: # mercurial >= 1.2
+ package = _importlib.import_module('mercurial.util')
+ except ImportError as e: # mercurial <= 1.1.2
+ package = self._get_package('mercurial.version')
+ return package.get_version()
+ else:
+ return package.version()
+
+
+class TornadoPythonPackage (PythonPackageDependency):
+ def _get_version_from_package(self, package):
+ return package.version
+
+ def _get_parsed_version(self):
+ package = self._get_package(self.package)
+ return package.version_info
+
+
+class SQLitePythonPackage (PythonPackageDependency):
+ def _get_version_from_package(self, package):
+ return _sys.version
+
+ def _get_parsed_version(self):
+ return _sys.version_info
+
+
+def _program_files_paths(*args):
+ "Utility for generating MS Windows search paths"
+ pf = _os.environ.get('ProgramFiles', '/usr/bin')
+ pfx86 = _os.environ.get('ProgramFiles(x86)', pf)
+ paths = [_os.path.join(pf, *args)]
+ if pfx86 != pf:
+ paths.append(_os.path.join(pfx86, *args))
+ return paths
+
+
+CHECKER['python'] = PythonDependency()
+
+
+for command,long_name,minimum_version,paths in [
+ ('sh', 'Bourne Shell', None, None),
+ ('ash', 'Almquist Shell', None, None),
+ ('bash', 'Bourne Again Shell', None, None),
+ ('csh', 'C Shell', None, None),
+ ('ksh', 'KornShell', None, None),
+ ('dash', 'Debian Almquist Shell', None, None),
+ ('tcsh', 'TENEX C Shell', None, None),
+ ('zsh', 'Z Shell', None, None),
+ ('git', 'Git', (1, 7, 0), None),
+ ('hg', 'Mercurial', (2, 0, 0), None),
+ ('EasyMercurial', None, (1, 3), None),
+ ('pip', None, None, None),
+ ('sqlite3', 'SQLite 3', None, None),
+ ('nosetests', 'Nose', (1, 0, 0), None),
+ ('ipython', 'IPython script', (1, 0), None),
+ ('emacs', 'Emacs', None, None),
+ ('xemacs', 'XEmacs', None, None),
+ ('vim', 'Vim', None, None),
+ ('vi', None, None, None),
+ ('nano', 'Nano', None, None),
+ ('gedit', None, None, None),
+ ('kate', 'Kate', None, None),
+ ('notepad++', 'Notepad++', None,
+ _program_files_paths('Notepad++', 'notepad++.exe')),
+ ('firefox', 'Firefox', None,
+ _program_files_paths('Mozilla Firefox', 'firefox.exe')),
+ ('google-chrome', 'Google Chrome', None,
+ _program_files_paths('Google', 'Chrome', 'Application', 'chrome.exe')
+ ),
+ ('chromium', 'Chromium', None, None),
+ ]:
+ if not long_name:
+ long_name = command
+ CHECKER[command] = CommandDependency(
+ command=command, paths=paths, long_name=long_name,
+ minimum_version=minimum_version)
+del command, long_name, minimum_version, paths # cleanup namespace
+
+
+CHECKER['make'] = MakeDependency(command='make', minimum_version=None)
+
+
+CHECKER['easy_install'] = EasyInstallDependency(
+ command='easy_install', long_name='Setuptools easy_install',
+ minimum_version=None)
+
+
+CHECKER['py.test'] = CommandDependency(
+ command='py.test', version_stream='stderr',
+ minimum_version=None)
+
+
+for paths,name,long_name in [
+ ([_os.path.join(_ROOT_PATH, 'Applications', 'Sublime Text 2.app',
+ 'Contents', 'version.plist')],
+ 'sublime-text', 'Sublime Text'),
+ ([_os.path.join(_ROOT_PATH, 'Applications', 'TextMate.app',
+ 'Contents', 'version.plist')],
+ 'textmate', 'TextMate'),
+ ([_os.path.join(_ROOT_PATH, 'Applications', 'TextWrangler.app',
+ 'Contents', 'version.plist')],
+ 'textwrangler', 'TextWrangler'),
+ ([_os.path.join(_ROOT_PATH, 'Applications', 'Safari.app',
+ 'Contents', 'version.plist')],
+ 'safari', 'Safari'),
+ ([_os.path.join(_ROOT_PATH, 'Applications', 'Xcode.app',
+ 'Contents', 'version.plist'), # OS X >=1.7
+ _os.path.join(_ROOT_PATH, 'Developer', 'Applications', 'Xcode.app',
+ 'Contents', 'version.plist'), # OS X 1.6,
+ ],
+ 'xcode', 'Xcode'),
+ ]:
+ if not long_name:
+ long_name = name
+ CHECKER[name] = VersionPlistCommandDependency(
+ command=None, paths=paths, name=name, long_name=long_name)
+del paths, name, long_name # cleanup namespace
+
+
for package,name,long_name,minimum_version,and_dependencies in [
('nose', None, 'Nose Python package',
CHECKER['nosetests'].minimum_version, None),
('jinja2', 'jinja', 'Jinja', (2, 6), None),
('zmq', 'pyzmq', 'PyZMQ', (2, 1, 4), None),
('IPython', None, 'IPython Python package',
- CHECKER['ipython'].minimum_version, ['jinja', 'tornado', 'pyzmq']),
+ CHECKER['ipython'].minimum_version, [
+ 'jinja',
+ 'tornado',
+ 'pyzmq',
+ VirtualDependency(
+ name='virtual-browser-ipython',
+ long_name='IPython-compatible web browser',
+ or_dependencies=[
+ CommandDependency(
+ command=CHECKER['firefox'].command,
+ paths=CHECKER['firefox'].paths,
+ name='{0}-for-ipython'.format(
+ CHECKER['firefox'].name),
+ long_name='{0} for IPython'.format(
+ CHECKER['firefox'].long_name),
+ minimum_version=(6, 0)),
+ CommandDependency(
+ command=CHECKER['google-chrome'].command,
+ paths=CHECKER['google-chrome'].paths,
+ name='{0}-for-ipython'.format(
+ CHECKER['google-chrome'].name),
+ long_name='{0} for IPython'.format(
+ CHECKER['google-chrome'].long_name),
+ minimum_version=(13, 0)),
+ CommandDependency(
+ command=CHECKER['chromium'].command,
+ paths=CHECKER['chromium'].paths,
+ name='{0}-for-ipython'.format(
+ CHECKER['chromium'].name),
+ long_name='{0} for IPython'.format(
+ CHECKER['chromium'].long_name),
+ minimum_version=(13, 0)),
+ VersionPlistCommandDependency(
+ command=CHECKER['safari'].command,
+ paths=CHECKER['safari'].paths,
+ key=CHECKER['safari'].key,
+ name='{0}-for-ipython'.format(
+ CHECKER['safari'].name),
+ long_name='{0} for IPython'.format(
+ CHECKER['safari'].long_name),
+ minimum_version=(5, 0)),
+ ]),
+ ]),
('argparse', None, 'Argparse', None, None),
('numpy', None, 'NumPy', None, None),
('scipy', None, 'SciPy', None, None),
del package, name, long_name, minimum_version, and_dependencies, kwargs
-class MercurialPythonPackage (PythonPackageDependency):
- def _get_version(self):
- try: # mercurial >= 1.2
- package = _importlib.import_module('mercurial.util')
- except ImportError as e: # mercurial <= 1.1.2
- package = self._get_package('mercurial.version')
- return package.get_version()
- else:
- return package.version()
-
-
CHECKER['mercurial'] = MercurialPythonPackage(
package='mercurial.util', name='mercurial',
long_name='Mercurial Python package',
minimum_version=CHECKER['hg'].minimum_version)
-class TornadoPythonPackage (PythonPackageDependency):
- def _get_version_from_package(self, package):
- return package.version
-
- def _get_parsed_version(self):
- package = self._get_package(self.package)
- return package.version_info
-
-
CHECKER['tornado'] = TornadoPythonPackage(
package='tornado', name='tornado', long_name='Tornado', minimum_version=(2, 0))
-class SQLitePythonPackage (PythonPackageDependency):
- def _get_version_from_package(self, package):
- return _sys.version
-
- def _get_parsed_version(self):
- return _sys.version_info
-
-
CHECKER['sqlite3-python'] = SQLitePythonPackage(
package='sqlite3', name='sqlite3-python',
long_name='SQLite Python package',
minimum_version=CHECKER['sqlite3'].minimum_version)
-class UserTaskDependency (Dependency):
- "Prompt the user to complete a task and check for success"
- def __init__(self, prompt, **kwargs):
- super(UserTaskDependency, self).__init__(**kwargs)
- self.prompt = prompt
-
- def _check(self):
- if _sys.version_info >= (3, ):
- result = input(self.prompt)
- else: # Python 2.x
- result = raw_input(self.prompt)
- return self._check_result(result)
-
- def _check_result(self, result):
- raise NotImplementedError()
-
-
-class EditorTaskDependency (UserTaskDependency):
- def __init__(self, **kwargs):
- self.path = _os.path.expanduser(_os.path.join(
- '~', 'swc-installation-test.txt'))
- self.contents = 'Hello, world!'
- super(EditorTaskDependency, self).__init__(
- prompt=(
- 'Open your favorite text editor and create the file\n'
- ' {0}\n'
- 'containing the line:\n'
- ' {1}\n'
- 'Press enter here after you have done this.\n'
- 'You may remove the file after you have finished testing.'
- ).format(self.path, self.contents),
- **kwargs)
-
- def _check_result(self, result):
- message = None
- try:
- with open(self.path, 'r') as f:
- contents = f.read()
- except IOError as e:
- raise DependencyError(
- checker=self,
- message='could not open {0!r}: {1}'.format(self.path, e)
- )# from e
- if contents.strip() != self.contents:
- raise DependencyError(
- checker=self,
- message=(
- 'file contents ({0!r}) did not match the expected {1!r}'
- ).format(contents, self.contents))
-
-
CHECKER['other-editor'] = EditorTaskDependency(
name='other-editor', long_name='')
-class VirtualDependency (Dependency):
- def _check(self):
- return '{0} {1}'.format(
- self.or_pass['dependency'].full_name(),
- self.or_pass['version'])
-
-
for name,long_name,dependencies in [
('virtual-shell', 'command line shell', (
'bash',