#******************************************************************** # # Copyright (C) 2005-2006 Hari Krishna Dara # # This file is part of p4admin. # # p4admin is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # p4admin is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # #******************************************************************* import time import sys import sys import os import os.path import threading import logging import utils log = logging.getLogger(__name__) # From pyunit#unittestgui.py class RollbackImporter: """This class is used to make sure that all the modules can be reloaded when they are refreshed from perforce.""" def __init__(self): self.previousModules = sys.modules.copy() def rollbackImports(self): # Assume that the scripts that are loaded from the scriptsRoot are not # system modules. scriptsRoot = utils.getScriptsRoot() for modname in sys.modules.keys(): modfile = '' if dir(sys.modules[modname]).count('__file__') != 0: modfile = sys.modules[modname].__file__ log.debug('module name: '+modname+ ' checking if file: '+modfile+ ' is under: '+scriptsRoot) # We unload only those modules that are loaded from current root. if modfile[0:len(scriptsRoot)] == scriptsRoot and \ not self.previousModules.has_key(modname): log.debug('unloading module: %s', modname) # Force reload when modname next imported del(sys.modules[modname]) def isModuleLoaded(module): return sys.modules.has_key(module) def notifyKeyboardInterrupt(): """Notify only if config is loaded. Helps testing.""" if isModuleLoaded('config'): import config if config.notifyKill: import notify notify.sendError("KeyboardInterrupt received: killed") def notifySystemExit(): """Notify only if config is loaded. Helps testing.""" if isModuleLoaded('config'): import config if config.notifyExit: import notify notify.sendError("SystemExit received: exited") def notifyGlobalException(): """Notify only if config is loaded. Helps testing.""" if isModuleLoaded('config'): import notify notify.sendError("Exception caught: " + str(sys.exc_info()[1])) def getErrRestartDelay(): if isModuleLoaded('config'): import config return config.errRestartDelay else: return 1 class RunLoop((threading.Thread)): def __init__(self, code='import runSched; runSched.runSched()'): super(RunLoop, self).__init__() self.setDaemon(True) self.code = code self.__rollbackImporter = None def run(self): while True: if self.__rollbackImporter: self.__rollbackImporter.rollbackImports() self.__rollbackImporter = RollbackImporter() try: exec self.code except utils.RestartProgram, ex: log.info('Requst to reload modules received') except KeyboardInterrupt: log.info("Exiting via KeyboardInterrupt") notifyKeyboardInterrupt() sys.exit() except SystemExit, ex: log.info("Exiting via SystemExit") notifySystemExit() raise ex except: log.exception("Global exception handler") time.sleep(getErrRestartDelay()) notifyGlobalException() log.info("Reloading modules and restarting the loop...") def test(): # Basic console logger. rootLogger = logging.getLogger() rootLogger.setLevel(logging.INFO) handler = logging.StreamHandler(sys.stdout) handler.setFormatter(logging.Formatter("%(message)s")) rootLogger.addHandler(handler) RunLoop('import testRunLoop; print "testValue="+str(testRunLoop.testValue); testRunLoop.restart()').start() #RunLoop('import testRunLoop; print "testValue="+str(testRunLoop.testValue); sys.exit(0)').start() #RunLoop('import testRunLoop; print "testValue="+str(testRunLoop.testValue); testRunLoop.raiseExcept()').start() if __name__ == '__main__': r = RunLoop() r.start() # Allows for KeyboardInterrupt to be generated in the main thread. while r.isAlive(): r.join(1)