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