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 How to get a command line:
11 - On OSX run this with the Terminal application.
13 - On Windows, go to the Start menu, select 'Run' and type 'cmd'
14 (without the quotes) to run the 'cmd.exe' Windows Command Prompt.
16 - On Linux, either use your login shell directly, or run one of a
17 number of graphical terminals (e.g. 'xterm', 'gnome-terminal', ...).
19 Run the script and follow the instructions it prints at the end.
21 This script requires at least Python 2.6. You can check the version
22 of Python that you have installed with 'swc-installation-test-1.py'.
25 from __future__ import print_function # for Python 2.6 compatibility
27 import distutils.ccompiler as _distutils_ccompiler
28 try: # Python 2.7 and 3.x
29 import importlib as _importlib
30 except ImportError: # Python 2.6 and earlier
31 class _Importlib (object):
32 """Minimal workarounds for functions we need
35 def import_module(name):
36 module = __import__(name)
37 for n in name.split('.')[1:]:
38 module = getattr(module, n)
40 _importlib = _Importlib()
41 import logging as _logging
43 import platform as _platform
45 import subprocess as _subprocess
51 # Comment out any entries you don't need
61 'hg', # Command line tool
62 'mercurial', # Python package
63 # Build tools and packaging
68 'nosetests', # Command line tool
69 'nose', # Python package
71 'sqlite3', # Command line tool
72 'sqlite3-python', # Python package
88 class DependencyError (Exception):
89 def __init__(self, checker, message):
90 self.checker = checker
91 self.message = message
94 return 'check for {0} failed:\n{1}'.format(
95 self.checker.full_name(), self.message)
98 def check(checks=None):
104 checker = CHECKER[check]
105 _sys.stdout.write('check {0}...\t'.format(checker.full_name()))
107 version = checker.check()
108 except DependencyError as e:
110 _sys.stdout.write('fail\n')
112 _sys.stdout.write('pass\n')
113 successes.append((checker, version))
115 print('\nSuccesses:\n')
116 for checker,version in successes:
117 print('{0} {1}'.format(
119 version or 'unknown'))
123 for failure in failures:
124 if failure not in printed:
127 printed.append(failure)
132 class Dependency (object):
133 def __init__(self, name, long_name=None, minimum_version=None,
134 version_delimiter='.', and_dependencies=None,
135 or_dependencies=None):
137 self.long_name = long_name or name
138 self.minimum_version = minimum_version
139 self.version_delimiter = version_delimiter
140 if not and_dependencies:
141 and_dependencies = []
142 self.and_dependencies = and_dependencies
143 if not or_dependencies:
145 self.or_dependencies = or_dependencies
146 self._check_error = None
149 return '<{0} {1}>'.format(type(self).__name__, self.name)
152 if self.name == self.long_name:
155 return '{0} ({1})'.format(self.long_name, self.name)
158 if self._check_error:
159 raise self._check_error
161 self._check_dependencies()
163 except DependencyError as e:
164 self._check_error = e # cache for future calls
167 def _check_dependencies(self):
168 for dependency in self.and_dependencies:
169 if not hasattr(dependency, 'check'):
170 dependency = CHECKER[dependency]
172 self.or_pass = or_error = None
173 for dependency in self.or_dependencies:
174 if not hasattr(dependency, 'check'):
175 dependency = CHECKER[dependency]
177 version = dependency.check()
178 except DependencyError as e:
182 'dependency': dependency,
185 if self.or_dependencies and not self.or_pass:
189 version = self._get_version()
190 parsed_version = None
191 if hasattr(self, '_get_parsed_version'):
192 parsed_version = self._get_parsed_version()
193 if self.minimum_version:
194 self._check_version(version=version, parsed_version=parsed_version)
197 def _get_version(self):
198 raise NotImplementedError(self)
200 def _check_version(self, version, parsed_version=None):
201 if not parsed_version:
202 parsed_version = self._parse_version(version=version)
203 if not parsed_version or parsed_version < self.minimum_version:
204 raise DependencyError(
206 message='outdated version of {0}: {1} (need >= {2})'.format(
207 self.full_name(), version,
208 self.version_delimiter.join(
209 str(part) for part in self.minimum_version)))
211 def _parse_version(self, version):
215 for part in version.split(self.version_delimiter):
217 parsed_version.append(int(part))
218 except ValueError as e:
219 raise NotImplementedError((version, part))# from e
220 return tuple(parsed_version)
223 class PythonDependency (Dependency):
224 def __init__(self, name='python', long_name='Python version',
225 minimum_version=(2, 6), **kwargs):
226 super(PythonDependency, self).__init__(
227 name=name, long_name=long_name, minimum_version=minimum_version,
230 def _get_version(self):
233 def _get_parsed_version(self):
234 return _sys.version_info
237 CHECKER['python'] = PythonDependency()
240 class CommandDependency (Dependency):
241 exe_extension = _distutils_ccompiler.new_compiler().exe_extension
243 def __init__(self, command, version_option='--version',
244 version_regexp=None, version_stream='stdout', **kwargs):
245 if 'name' not in kwargs:
246 kwargs['name'] = command
247 super(CommandDependency, self).__init__(**kwargs)
248 self.command = command
249 self.version_option = version_option
250 if not version_regexp:
251 regexp = r'([\d][\d{0}]*[\d])'.format(self.version_delimiter)
252 version_regexp = _re.compile(regexp)
253 self.version_regexp = version_regexp
254 self.version_stream = version_stream
256 def _get_version_stream(self, expect=(0,)):
257 command = self.command + (self.exe_extension or '')
259 p = _subprocess.Popen(
260 [command, self.version_option],
261 stdout=_subprocess.PIPE, stderr=_subprocess.PIPE,
262 close_fds=True, shell=False, universal_newlines=True)
264 raise DependencyError(
266 message="could not find '{0}' executable".format(command),
268 stdout,stderr = p.communicate()
270 if status not in expect:
272 "failed to execute '{0} {1}':".format(
273 command, self.version_option),
274 'status: {0}'.format(status),
276 for name,string in [('stdout', stdout), ('stderr', stderr)]:
278 lines.extend([name + ':', string])
279 raise DependencyError(checker=self, message='\n'.join(lines))
280 for name,string in [('stdout', stdout), ('stderr', stderr)]:
281 if name == self.version_stream:
283 raise NotImplementedError(self.version_stream)
285 def _get_version(self):
286 version_stream = self._get_version_stream()
287 match = self.version_regexp.search(version_stream)
289 raise DependencyError(
291 message='no version string in output:\n{0}'.format(
293 return match.group(1)
296 for command,long_name,minimum_version in [
297 ('sh', 'Bourne Shell', None),
298 ('ash', 'Almquist Shell', None),
299 ('bash', 'Bourne Again Shell', None),
300 ('csh', 'C Shell', None),
301 ('ksh', 'KornShell', None),
302 ('dash', 'Debian Almquist Shell', None),
303 ('tcsh', 'TENEX C Shell', None),
304 ('zsh', 'Z Shell', None),
305 ('git', 'Git', (1, 8, 0)),
306 ('hg', 'Mercurial', (2, 0, 0)),
307 ('make', None, None),
308 ('sqlite3', 'SQLite 3', None),
309 ('nosetests', 'Nose', (1, 0, 0)),
310 ('emacs', 'Emacs', None),
311 ('xemacs', 'XEmacs', None),
312 ('vim', 'Vim', None),
314 ('nano', 'Nano', None),
315 ('kate', 'Kate', None),
316 ('notepad++', 'Notepad++', None),
317 ('firefox', 'Firefox', None),
318 ('google-chrome', 'Google Chrome', None),
319 ('chromium', 'Chromium', None),
323 CHECKER[command] = CommandDependency(
324 command=command, long_name=long_name, minimum_version=minimum_version)
325 del command, long_name, minimum_version # cleanup namespace
328 class EasyInstallDependency (CommandDependency):
329 def _get_version(self):
331 return super(EasyInstallDependency, self)._get_version()
332 except DependencyError as e:
333 version_stream = self.version_stream
335 self.version_stream = 'stderr'
336 stream = self._get_version_stream(expect=(1,))
337 if 'option --version not recognized' in stream:
338 return 'unknown (possibly Setuptools?)'
340 self.version_stream = version_stream
343 CHECKER['easy_install'] = EasyInstallDependency(
344 command='easy_install', long_name='Setuptools easy_install',
345 minimum_version=None)
348 class PythonPackageDependency (Dependency):
349 def __init__(self, package, **kwargs):
350 if 'name' not in kwargs:
351 kwargs['name'] = package
352 if 'and_dependencies' not in kwargs:
353 kwargs['and_dependencies'] = []
354 if 'python' not in kwargs['and_dependencies']:
355 kwargs['and_dependencies'].append('python')
356 super(PythonPackageDependency, self).__init__(**kwargs)
357 self.package = package
359 def _get_version(self):
360 package = self._get_package(self.package)
361 return self._get_version_from_package(package)
363 def _get_package(self, package):
365 return _importlib.import_module(package)
366 except ImportError as e:
367 raise DependencyError(
369 message="could not import the '{0}' package for {1}".format(
370 package, self.full_name()),
373 def _get_version_from_package(self, package):
375 version = package.__version__
376 except AttributeError:
381 for package,name,long_name,minimum_version in [
382 ('nose', None, 'Nose Python package',
383 CHECKER['nosetests'].minimum_version),
384 ('IPython', None, None, None),
385 ('numpy', None, 'NumPy', None),
386 ('scipy', None, 'SciPy', None),
387 ('matplotlib', None, 'Matplotlib', None),
388 ('sympy', None, 'SymPy', None),
389 ('Cython', None, None, None),
390 ('networkx', None, 'NetworkX', None),
391 ('mayavi.mlab', None, 'MayaVi', None),
392 ('setuptools', None, 'Setuptools', None),
398 CHECKER[name] = PythonPackageDependency(
399 package=package, name=name, long_name=long_name,
400 minimum_version=minimum_version)
401 del package, name, long_name, minimum_version # cleanup namespace
404 class MercurialPythonPackage (PythonPackageDependency):
405 def _get_version(self):
406 try: # mercurial >= 1.2
407 package = _importlib.import_module('mercurial.util')
408 except ImportError as e: # mercurial <= 1.1.2
409 package = self._get_package('mercurial.version')
410 return package.get_version()
412 return package.version()
415 CHECKER['mercurial'] = MercurialPythonPackage(
416 package='mercurial.util', name='mercurial',
417 long_name='Mercurial Python package',
418 minimum_version=CHECKER['hg'].minimum_version)
421 class SQLitePythonPackage (PythonPackageDependency):
422 def _get_version_from_package(self, package):
425 def _get_parsed_version(self):
426 return _sys.version_info
429 CHECKER['sqlite3-python'] = SQLitePythonPackage(
430 package='sqlite3', name='sqlite3-python',
431 long_name='SQLite Python package',
432 minimum_version=CHECKER['sqlite3'].minimum_version)
435 class VirtualDependency (Dependency):
437 return '{0} {1}'.format(
438 self.or_pass['dependency'].full_name(),
439 self.or_pass['version'])
442 for name,dependencies in [
462 ('virtual-browser', (
468 CHECKER[name] = VirtualDependency(
469 name=name, long_name=name, or_dependencies=dependencies)
470 del name, dependencies # cleanup namespace
473 def print_system_info():
474 print("If you do not understand why the above failures occurred,")
475 print("copy and send the *entire* output (all info above and summary")
476 print("below) to the instructor for help.")
478 print('==================')
479 print('System information')
480 print('==================')
481 print('os.name : {0}'.format(_os.name))
483 print('os.uname : {0}'.format(_os.uname()))
486 print('platform : {0}'.format(_sys.platform))
487 print('platform+ : {0}'.format(_platform.platform()))
488 print('prefix : {0}'.format(_sys.prefix))
489 print('exec_prefix : {0}'.format(_sys.exec_prefix))
490 print('executable : {0}'.format(_sys.executable))
491 print('version_info : {0}'.format(_sys.version_info))
492 print('version : {0}'.format(_sys.version))
493 print('environment :')
494 for key,value in sorted(_os.environ.items()):
495 print(' {0}={1}'.format(key, value))
496 print('==================')
499 if __name__ == '__main__':
500 if not check(_sys.argv[1:]):