codereloading.py (9363B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\codereloading.py 2 import __builtin__ 3 import inspect 4 import logging 5 import os 6 import Queue 7 import sys 8 import time 9 import traceback 10 import weakref 11 import blue 12 import signals 13 import uthread 14 from codereload import xreload 15 from eveprefs import prefs 16 from watchdog.events import FileSystemEventHandler 17 from watchdog.observers import Observer 18 logger = logging.getLogger(__name__) 19 logger.addHandler(logging.NullHandler()) 20 21 class CodeReloadHandler(FileSystemEventHandler): 22 EXCLUDED_FOLDERS = [ os.path.normpath(folder) for folder in ['packages/audio2', 23 'packages/d3dinfo', 24 'packages/destiny', 25 'packages/eveLocalization', 26 'packages/geo2', 27 'packages/geo2helpers', 28 'packages/imageutils', 29 'packages/inputmethod', 30 'packages/pyFSD', 31 'packages/spacemouse', 32 'packages/videoplayer'] ] 33 34 def __init__(self): 35 super(CodeReloadHandler, self).__init__() 36 self._reload_queue = Queue.Queue() 37 self.on_file_reloaded = signals.Signal() 38 self.on_file_reload_failed = signals.Signal() 39 uthread.new(self._monitor_reload_queue, weakref.ref(self)) 40 41 @staticmethod 42 def _monitor_reload_queue(self_ref): 43 while True: 44 self = self_ref() 45 if self is None: 46 return 47 while not self._reload_queue.empty(): 48 try: 49 filename = self._reload_queue.get_nowait() 50 except Queue.Empty: 51 break 52 53 self.reload_file(filename) 54 55 del self 56 blue.pyos.synchro.Yield() 57 58 def on_modified(self, event): 59 super(CodeReloadHandler, self).on_modified(event) 60 if event.is_directory: 61 return 62 logger.debug('File modification detected: %s', event) 63 self._reload_queue.put(event.src_path, block=False) 64 65 def on_moved(self, event): 66 super(CodeReloadHandler, self).on_moved(event) 67 if event.is_directory: 68 return 69 logger.debug('File move modification detected: %s', event) 70 self._reload_queue.put(event.dest_path, block=False) 71 72 def reload_file(self, filename): 73 orig_filename = filename 74 logger.debug('enter reload_file(%s)', orig_filename) 75 filename = os.path.normpath(os.path.abspath(filename)) 76 for excluded_folder in self.EXCLUDED_FOLDERS: 77 if excluded_folder in filename: 78 logger.debug('reload_file[%s] excluded because of excluded_folder[%s]' % (filename, excluded_folder)) 79 return 80 81 if filename[-3:] != '.py': 82 return 83 84 def find_module(filename): 85 filename = os.path.normpath(filename) 86 normalized_case_filename = os.path.normcase(filename) 87 for prefix in sys.path[1:]: 88 prefix = os.path.normcase(os.path.normpath(prefix)) 89 if not normalized_case_filename.startswith(prefix): 90 continue 91 p, f = os.path.split(filename) 92 if not f: 93 logger.error('Unexpected missing module name for filename %s', filename) 94 continue 95 p = p[len(prefix) + len(os.path.sep):] 96 p = p.replace(os.path.sep, '.') 97 f = f[:-3] 98 if f != '__init__': 99 if p: 100 p = p + '.' + f 101 else: 102 p = f 103 m = sys.modules.get(p, None) 104 if m is not None: 105 return m 106 107 module = find_module(filename) 108 if module is None: 109 logger.warning('Could not find module to reload for filename %s', orig_filename) 110 else: 111 try: 112 t = time.time() 113 with open(filename) as stream: 114 source = stream.read() + '\n' 115 seconds_read = time.time() - t 116 logger.info('Compiling %s using source %s', module.__name__, filename) 117 code = compile(source, filename, 'exec') 118 logger.info('Reloading %s', module) 119 seconds_compile = time.time() - t - seconds_read 120 xreload(module, code) 121 uthread.new(self.on_file_reloaded, filename, module) 122 seconds_total = time.time() - t 123 seconds_xreload = seconds_total - seconds_compile 124 logger.info('Reloaded %s in %.3f seconds (read: %.3f seconds, compile: %.3f seconds, xreload: %.3f seconds)', module, seconds_total, seconds_read, seconds_compile, seconds_xreload) 125 except Exception: 126 logger.exception('reload_file failed for %s', filename) 127 self.on_file_reload_failed(filename, sys.exc_info()) 128 129 130 class FsdReloadHandler(FileSystemEventHandler): 131 132 def __init__(self): 133 super(FsdReloadHandler, self).__init__() 134 135 def on_modified(self, event): 136 super(FsdReloadHandler, self).on_modified(event) 137 logger.debug('FSD modification detected: %s', event.src_path) 138 try: 139 from fsdBuiltData.common.base import BuiltDataLoader 140 except ImportError: 141 logger.exception('Failed importing fsdBuiltData') 142 return 143 144 for subClass in BuiltDataLoader.__subclasses__(): 145 try: 146 if subClass.CheckReloadDataFromDisk(): 147 logger.info('Reloaded FSD data for %s: %s', subClass.__name__, subClass.GetAutobuildFilePath()) 148 except Exception: 149 logger.exception('Failed to reload FSD data') 150 151 152 def _OnFileReloaded(filename, module): 153 try: 154 sm = getattr(__builtin__, 'sm', None) 155 if sm is None: 156 logger.warn(' sm not defined, cannot reload services.') 157 return 158 guids = [ cls.__guid__[4:] for name, cls in inspect.getmembers(module, inspect.isclass) if getattr(cls, '__guid__', '').startswith('svc.') ] 159 sm.Reload(set(guids)) 160 reloadHook = getattr(module, '__SakeReloadHook', None) 161 if reloadHook is not None: 162 reloadHook() 163 logger.info('Reloaded %s', filename) 164 print 'Reloaded', filename 165 except Exception: 166 logger.exception('Unhandled exception in _OnFileReloaded') 167 168 169 def _OnFileReloadFailed(filename, exc_info): 170 sys.stderr.write('Failed to reload: %s\n' % filename) 171 traceback.print_exception(*exc_info) 172 173 174 __SAKE_INSTALLATION_ATTEMPTED = False 175 __OBSERVER = None 176 __WATCHES = [] 177 178 def FileSystemObserver(): 179 global __OBSERVER 180 if __OBSERVER is None: 181 __OBSERVER = Observer() 182 return __OBSERVER 183 184 185 def InstallSakeAutocompiler(): 186 global __SAKE_INSTALLATION_ATTEMPTED 187 if not __SAKE_INSTALLATION_ATTEMPTED: 188 if prefs.clusterMode == 'LOCAL' and not blue.pyos.packaged: 189 try: 190 InitializeCodeReloadSpy() 191 InitializeFSDReloadSpy() 192 except Exception: 193 logger.exception('sake AutoCompiler failed to install.') 194 195 else: 196 logger.info('Skipping sake AutoCompiler installation.') 197 if __OBSERVER is not None: 198 __OBSERVER.start() 199 logger.info('AutoCompiler installation complete.') 200 __SAKE_INSTALLATION_ATTEMPTED = True 201 else: 202 logger.info('Tried to install sake AutoCompiler but already installed.') 203 204 205 def InitializeCodeReloadSpy(onFileReloaded = _OnFileReloaded, onFileReloadFailed = _OnFileReloadFailed): 206 global __WATCHES 207 blueSpy = CodeReloadHandler() 208 blueSpy.on_file_reloaded.connect(onFileReloaded) 209 blueSpy.on_file_reload_failed.connect(onFileReloadFailed) 210 211 def filterOutSubPaths(paths): 212 newPaths = [] 213 for absPath, relPath in sorted([ (os.path.normcase(os.path.abspath(path) + os.sep), path) for path in paths ]): 214 if not len([ path for path, _ in newPaths if absPath.startswith(path) ]): 215 newPaths.append((absPath, relPath)) 216 217 return [ path for _, path in newPaths ] 218 219 paths = filterOutSubPaths(blue.paths.GetExpandedSearchPaths()['lib']) 220 for path in paths: 221 logger.info('Observing: %s', path) 222 __WATCHES.append(FileSystemObserver().schedule(blueSpy, path, recursive=True)) 223 224 logger.info('Installed sake AutoCompiler.' + repr(paths)) 225 226 227 def InitializeFSDReloadSpy(boot_role = None): 228 from fsd import GetBranchRoot, GetFullName, AbsJoin 229 from eveprefs import boot 230 branch_root = GetBranchRoot() 231 if boot_role is None: 232 boot_role = boot.role 233 if boot_role == 'server': 234 paths = [GetFullName(AbsJoin(branch_root, 'eve/autobuild/staticData/server'))] 235 elif boot_role == 'client': 236 paths = [GetFullName(AbsJoin(branch_root, 'eve/autobuild/staticData/client'))] 237 else: 238 return 239 handler = FsdReloadHandler() 240 for path in paths: 241 __WATCHES.append(FileSystemObserver().schedule(handler, path, recursive=True)) 242 243 244 def OnFSDReload(path): 245 from fsdBuiltData.common.base import BuiltDataLoader 246 for subClass in BuiltDataLoader.__subclasses__(): 247 try: 248 if subClass.CheckReloadDataFromDisk(): 249 print 'Reloaded FSD data for %s: %s' % (subClass.__name__, subClass.GetAutobuildFilePath()) 250 except: 251 logger.exception('Failed to reload FSD data')