/ src / paths.py
paths.py
  1  """
  2  Path related functions
  3  """
  4  import logging
  5  import os
  6  import re
  7  import sys
  8  from datetime import datetime
  9  from shutil import move
 10  
 11  logger = logging.getLogger('default')
 12  
 13  # When using py2exe or py2app, the variable frozen is added to the sys
 14  # namespace.  This can be used to setup a different code path for
 15  # binary distributions vs source distributions.
 16  frozen = getattr(sys, 'frozen', None)
 17  
 18  
 19  def lookupExeFolder():
 20      """Returns executable folder path"""
 21      if frozen:
 22          exeFolder = (
 23              # targetdir/Bitmessage.app/Contents/MacOS/Bitmessage
 24              os.path.dirname(sys.executable).split(os.path.sep)[0]
 25              if frozen == "macosx_app" else os.path.dirname(sys.executable))
 26      elif os.getenv('APPIMAGE'):
 27          exeFolder = os.path.dirname(os.getenv('APPIMAGE'))
 28      elif __file__:
 29          exeFolder = os.path.dirname(__file__)
 30      else:
 31          return ''
 32      return exeFolder + os.path.sep
 33  
 34  
 35  def lookupAppdataFolder():
 36      """Returns path of the folder where application data is stored"""
 37      APPNAME = "PyBitmessage"
 38      dataFolder = os.environ.get('BITMESSAGE_HOME')
 39      if dataFolder:
 40          if dataFolder[-1] not in (os.path.sep, os.path.altsep):
 41              dataFolder += os.path.sep
 42      elif sys.platform == 'darwin':
 43          try:
 44              dataFolder = os.path.join(
 45                  os.environ['HOME'],
 46                  'Library/Application Support/', APPNAME
 47              ) + '/'
 48  
 49          except KeyError:
 50              sys.exit(
 51                  'Could not find home folder, please report this message'
 52                  ' and your OS X version to the BitMessage Github.')
 53      elif sys.platform.startswith('win'):
 54          dataFolder = os.path.join(os.environ['APPDATA'], APPNAME) + os.path.sep
 55      else:
 56          try:
 57              dataFolder = os.path.join(os.environ['XDG_CONFIG_HOME'], APPNAME)
 58          except KeyError:
 59              dataFolder = os.path.join(os.environ['HOME'], '.config', APPNAME)
 60  
 61          # Migrate existing data to the proper location
 62          # if this is an existing install
 63          try:
 64              move(os.path.join(os.environ['HOME'], '.%s' % APPNAME), dataFolder)
 65              logger.info('Moving data folder to %s', dataFolder)
 66          except IOError:
 67              # Old directory may not exist.
 68              pass
 69          dataFolder = dataFolder + os.path.sep
 70      return dataFolder
 71  
 72  
 73  def codePath():
 74      """Returns path to the program sources"""
 75      if not frozen:
 76          return os.path.dirname(__file__)
 77      return (
 78          os.environ.get('RESOURCEPATH')
 79          # pylint: disable=protected-access
 80          if frozen == "macosx_app" else sys._MEIPASS)
 81  
 82  
 83  def tail(f, lines=20):
 84      """Returns last lines in the f file object"""
 85      total_lines_wanted = lines
 86  
 87      BLOCK_SIZE = 1024
 88      f.seek(0, 2)
 89      block_end_byte = f.tell()
 90      lines_to_go = total_lines_wanted
 91      block_number = -1
 92      # blocks of size BLOCK_SIZE, in reverse order starting
 93      # from the end of the file
 94      blocks = []
 95      while lines_to_go > 0 and block_end_byte > 0:
 96          if block_end_byte - BLOCK_SIZE > 0:
 97              # read the last block we haven't yet read
 98              f.seek(block_number * BLOCK_SIZE, 2)
 99              blocks.append(f.read(BLOCK_SIZE))
100          else:
101              # file too small, start from begining
102              f.seek(0, 0)
103              # only read what was not read
104              blocks.append(f.read(block_end_byte))
105          lines_found = blocks[-1].count('\n')
106          lines_to_go -= lines_found
107          block_end_byte -= BLOCK_SIZE
108          block_number -= 1
109      all_read_text = ''.join(reversed(blocks))
110      return '\n'.join(all_read_text.splitlines()[-total_lines_wanted:])
111  
112  
113  def lastCommit():
114      """
115      Returns last commit information as dict with 'commit' and 'time' keys
116      """
117      githeadfile = os.path.join(codePath(), '..', '.git', 'logs', 'HEAD')
118      result = {}
119      if os.path.isfile(githeadfile):
120          try:
121              with open(githeadfile, 'rt') as githead:
122                  line = tail(githead, 1)
123              result['commit'] = line.split()[1]
124              result['time'] = datetime.fromtimestamp(
125                  float(re.search(r'>\s*(.*?)\s', line).group(1))
126              )
127          except (IOError, AttributeError, TypeError):
128              pass
129      return result