text: # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
  # vi: set ft=python sts=4 ts=4 sw=4 et:
  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
  #
  #   See COPYING file distributed along with the PyMVPA package for the
  #   copyright and license terms.
  #
  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
  """Provide system and PyMVPA information useful while reporting bugs
  """
  
  __docformat__ = 'restructuredtext'
  
  import time, sys, subprocess
  import os
  from os.path import join as pathjoin
  import platform as pl
  from tempfile import mkstemp
  from StringIO import StringIO
  
  import mvpa2
  from mvpa2.base import externals, cfg
  from mvpa2.base.dochelpers import borrowkwargs
  
  def _t2s(t):
      res = []
      for e in t:
          if isinstance(e, tuple):
              es = _t2s(e)
              if es != '':
                  res += ['(%s)' % es]
          elif e != '':
              res += [e]
      return '/'.join(res)
  
  __all__ = ['wtf', 'get_pymvpa_gitversion']
  
  
  def get_pymvpa_gitversion():
      """PyMVPA version as reported by git.
  
      Returns
      -------
      None or str
        Version of PyMVPA according to git.
      """
      gitpath = pathjoin(os.path.dirname(mvpa2.__file__), os.path.pardir)
      gitpathgit = pathjoin(gitpath, '.git')
      if not os.path.exists(gitpathgit):
          return None
      ver = None
      try:
          (tmpd, tmpn) = mkstemp('mvpa', 'git')
          retcode = subprocess.call(['git',
                                     '--git-dir=%s' % gitpathgit,
                                     '--work-tree=%s' % gitpath,
                                     'describe', '--abbrev=4', 'HEAD'
                                     ],
                                    stdout=tmpd,
                                    stderr=subprocess.STDOUT)
          outline = open(tmpn, 'r').readlines()[0].strip()
          if outline.startswith('upstream/'):
              ver = outline.replace('upstream/', '')
      finally:
          os.remove(tmpn)
      return ver
  
  
  class WTF(object):
      """Convenience class to contain information about PyMVPA and OS
  
      TODO: refactor to actually not contain just string representation
      but rather a dictionary (of dictionaries)
      """
  
      __knownitems__ = set(('process', 'runtime',
                            'externals', 'system', 'sources'))
  
      def __init__(self, include=None, exclude=None):
          """
          Parameters
          ----------
          include : list of str
            By default, all known (listed in `__knownitems__`) are reported.
            If only a limited set is needed to be reported -- specify it here.
          exclude : list of str
            If you want to exclude any item from being reported.
          """
          self._info = ''
          if include is None:
              report_items = self.__knownitems__.copy()
          else:
              # check first
              if not self.__knownitems__.issuperset(include):
                  raise ValueError, \
                        "Items %s provided in exclude are not known to WTF." \
                        " Known are %s" % \
                        (str(set(include).difference(self.__knownitems__)),
                         self.__knownitems__)
              report_items = set(include)
  
          if exclude is not None:
              # check if all are known
              if not self.__knownitems__.issuperset(exclude):
                  raise ValueError, \
                        "Items %s provided in exclude are not known to WTF." \
                        " Known are %s" % \
                        (str(set(exclude).difference(self.__knownitems__)),
                         self.__knownitems__)
              report_items = report_items.difference(exclude)
          self._report_items = report_items
          self._acquire()
  
  
      def _acquire_sources(self, out):
          out.write("PyMVPA:\n")
          out.write(" Version:       %s\n" % mvpa2.__version__)
          out.write(" Hash:          %s\n" % mvpa2.__hash__)
          out.write(" Path:          %s\n" % mvpa2.__file__)
  
          # Try to obtain git information if available
          out.write(" Version control (GIT):\n")
          try:
              gitpath = pathjoin(os.path.dirname(mvpa2.__file__), os.path.pardir)
              gitpathgit = pathjoin(gitpath, '.git')
              if os.path.exists(gitpathgit):
                  for scmd, cmd in [
                      ('Status', ['status']),
                      ('Reference', 'show-ref -h HEAD'.split(' ')),
                      ('Difference from last release %s' % mvpa2.__version__,
                       ['diff', '--shortstat', 'upstream/%s...' % mvpa2.__version__])]:
                      try:
                          (tmpd, tmpn) = mkstemp('mvpa', 'git')
                          retcode = subprocess.call(['git',
                                                     '--git-dir=%s' % gitpathgit,
                                                     '--work-tree=%s' % gitpath] + cmd,
                                                    stdout=tmpd,
                                                    stderr=subprocess.STDOUT)
                      finally:
                          outlines = open(tmpn, 'r').readlines()
                          if len(outlines):
                              out.write('  %s:\n   %s' % (scmd, '   '.join(outlines)))
                          os.remove(tmpn)
                      #except Exception, e:
                      #    pass
              else:
                  raise RuntimeError, "%s is not under GIT" % gitpath
          except Exception, e:
              out.write(' GIT information could not be obtained due "%s"\n' % e)
  
  
      def _acquire_system(self, out):
          out.write('SYSTEM:\n')
          out.write(' OS:            %s\n' %
                    ' '.join([os.name,
                              pl.system(),
                              pl.release(),
                              pl.version()]).rstrip())
          out.write(' Distribution:  %s\n' %
                    ' '.join([_t2s(pl.dist()),
                              _t2s(pl.mac_ver()),
                              _t2s(pl.win32_ver())]).rstrip())
  
      def _acquire_externals(self, out):
          # Test and list all dependencies:
          sdeps = {True: [], False: [], 'Error': []}
          for dep in sorted(externals._KNOWN):
              try:
                  sdeps[externals.exists(dep, force=False)] += [dep]
              except:
                  sdeps['Error'] += [dep]
          out.write('EXTERNALS:\n')
          out.write(' Present:       %s\n' % ', '.join(sdeps[True]))
          out.write(' Absent:        %s\n' % ', '.join(sdeps[False]))
          if len(sdeps['Error']):
              out.write(' Errors in determining: %s\n' % ', '.join(sdeps['Error']))
  
          SV = ('.__version__', )              # standard versioning
          out.write(' Versions of critical externals:\n')
          # First the ones known to externals,
          for k, v in sorted(externals.versions.iteritems()):
              out.write('  %-12s: %s\n' % (k, str(v)))
          try:
              if externals.exists('matplotlib'):
                  import matplotlib
                  out.write(' Matplotlib backend: %s\n'
                            % matplotlib.get_backend())
          except Exception, exc:
              out.write(' Failed to determine backend of matplotlib due to "%s"'
                        % str(exc))
  
      def _acquire_runtime(self, out):
          out.write("RUNTIME:\n")
          out.write(" PyMVPA Environment Variables:\n")
          pymvpa_envvars = '  '.join([
              '%-20s: "%s"\n' % (str(k), str(v))
              for k, v in os.environ.iteritems()
              if (k.startswith('MVPA') or k.startswith('PYTHON'))
          ])
          if pymvpa_envvars:
              out.write('  ' + pymvpa_envvars)
  
          out.write(" PyMVPA Runtime Configuration:\n")
          out.write('  ' + str(cfg).replace('\n', '\n  ').rstrip() + '\n')
  
      def _acquire_process(self, out):
          try:
              procstat = open('/proc/%d/status' % os.getpid()).readlines()
              procstat += ['Python recursion limit: %d' % sys.getrecursionlimit()]
              out.write(' Process Information:\n')
              out.write('  ' + '  '.join(procstat))
          except:
              pass
  
  
      def _acquire(self):
          """
          TODO: refactor and redo ;)
          """
          out = StringIO()
  
          out.write("Current date:   %s\n" % time.strftime("%Y-%m-%d %H:%M"))
  
          # Little silly communicator/
          if 'sources' in self._report_items:
              self._acquire_sources(out)
          if 'system' in self._report_items:
              self._acquire_system(out)
          if 'externals' in self._report_items:
              self._acquire_externals(out)
          if 'runtime' in self._report_items:
              self._acquire_runtime(out)
          if 'process' in self._report_items:
              self._acquire_process(out)
  
          self._info = out.getvalue()
  
  
      def __repr__(self):
          if self._info is None:
              self._acquire()
          return self._info
  
      __str__ = __repr__
  
  
  @borrowkwargs(WTF, '__init__')
  def wtf(filename=None, **kwargs):
      """Report summary about PyMVPA and the system
  
      Parameters
      ----------
      filename : None or str
        If provided, information will be stored in a file, not printed
        to the screen
      **kwargs
        Passed to initialize `WTF` instance
      """
  
      info = WTF(**kwargs)
      if filename is not None:
          _ = open(filename, 'w').write(str(info))
      else:
          return info
  
  
  if __name__ == '__main__':
      print wtf()
  