diff --git a/edexOsgi/com.raytheon.uf.common.localization.python/utility/common_static/base/python/ModuleUtil.py b/edexOsgi/com.raytheon.uf.common.localization.python/utility/common_static/base/python/ModuleUtil.py index 40bc2b9d39..60c3409fb1 100644 --- a/edexOsgi/com.raytheon.uf.common.localization.python/utility/common_static/base/python/ModuleUtil.py +++ b/edexOsgi/com.raytheon.uf.common.localization.python/utility/common_static/base/python/ModuleUtil.py @@ -29,6 +29,9 @@ # Date Ticket# Engineer Description # ------------ ---------- ----------- -------------------------- # 11/11/13 bkowal Initial Creation. +# 01/20/14 2712 bkowal Always add the directory path for +# the desired module to the beginning +# of the system path. # import os, sys, imp @@ -39,19 +42,16 @@ def loadModule(filename): @summary: This function takes a filename and find the module, loads it and returns that module ''' - addedToPath = False path = os.path.splitext(filename)[0] directory = os.path.dirname(filename) # ensure the module containing directory is on the python path. - if sys.path.count(directory) == 0: - sys.path.append(directory) - addedToPath = True - filename = os.path.split(path)[1] - fp, pathname, description = imp.find_module(filename) - module = imp.load_module(filename, fp, pathname, description) - - if addedToPath: - sys.path.remove(directory) + sys.path.insert(0, directory) + try: + filename = os.path.split(path)[1] + fp, pathname, description = imp.find_module(filename) + module = imp.load_module(filename, fp, pathname, description) + finally: + sys.path.pop(0) return module \ No newline at end of file diff --git a/edexOsgi/com.raytheon.uf.common.localization.python/utility/common_static/base/python/PythonOverriderCore.py b/edexOsgi/com.raytheon.uf.common.localization.python/utility/common_static/base/python/PythonOverriderCore.py index 82c08542ee..bd082df239 100644 --- a/edexOsgi/com.raytheon.uf.common.localization.python/utility/common_static/base/python/PythonOverriderCore.py +++ b/edexOsgi/com.raytheon.uf.common.localization.python/utility/common_static/base/python/PythonOverriderCore.py @@ -30,11 +30,14 @@ # Date Ticket# Engineer Description # ------------ ---------- ----------- -------------------------- # 11/12/13 bkowal Initial Creation. +# 01/20/14 2712 bkowal Improve python class merging. Classes +# will now truly override each other +# instead of extending each other. # # # -import os, imp, types +import os, imp, types, inspect import ModuleUtil def _internalOverride(files): @@ -111,29 +114,41 @@ def _mergeClasses(source, target, className): for attr in dir(sourceClass): # include private attributes because this is a class? # methods cannot just be merged into a class, so skip them. - if isinstance(attr, types.BuiltinFunctionType) \ - or isinstance(attr, types.MethodType) \ - or attr.startswith('__') or attr.startswith('_'): - continue + if isinstance(getattr(sourceClass, attr), types.BuiltinFunctionType) \ + or isinstance(getattr(sourceClass, attr), types.MethodType) \ + or attr.startswith('__'): + continue # do we need to worry about nested classes? if isinstance(getattr(sourceClass, attr), types.ClassType) \ - or isinstance(getattr(sourceClass, attr), types.TypeType): + or isinstance(getattr(sourceClass, attr), types.TypeType): target = _mergeClasses(source, target, attr) attributeName = className + '.' + attr target = _mergeAttributes(source, target, attributeName) + + # make new class "extend" the original class + for attr in dir(targetClass): + if attr != '__init__' \ + and isinstance(getattr(targetClass, attr), types.MethodType) \ + and not attr in dir(sourceClass): + # complete the merge / override of methods for any method that + # the new class does not implement + + # retrieve the implementation of the method (this is different from + # retrieving the method, itself) + exec('method = target.' + className + '.' + attr + '.im_func') + # copy the method implementation to the other class and give it + # the same name as the original + classMethodDirective = _buildReplaceClassMethodDirective(className, attr) + exec(classMethodDirective) - # complete the merge / override of methods. - exec(_buildMergeDirective(className, legacyMode)) return _mergeAttributes(source, target, className) -def _buildMergeDirective(className, legacyMode): - if (legacyMode): - return 'source.' + className + '.__bases__ = (target.' + className + ',)' - else: - return 'source.' + className + ' = type("' + className + \ - '", (target.' + className + ',), dict(source.' + className + '.__dict__))' +def _buildReplaceClassMethodDirective(className, methodName): + replaceDirective = 'setattr(source.' + className + ', "' + methodName + '", method)' + + return replaceDirective def _compareClasses(clazz1, clazz2): clazz1Attr = dir(clazz1) diff --git a/edexOsgi/com.raytheon.uf.common.localization.python/utility/common_static/base/python/PythonOverriderInterface.py b/edexOsgi/com.raytheon.uf.common.localization.python/utility/common_static/base/python/PythonOverriderInterface.py new file mode 100644 index 0000000000..97c7e8e6c2 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.localization.python/utility/common_static/base/python/PythonOverriderInterface.py @@ -0,0 +1,89 @@ +# # +# This software was developed and / or modified by Raytheon Company, +# pursuant to Contract DG133W-05-CQ-1067 with the US Government. +# +# U.S. EXPORT CONTROLLED TECHNICAL DATA +# This software product contains export-restricted data whose +# export/transfer/disclosure is restricted by U.S. law. Dissemination +# to non-U.S. persons whether in the United States or abroad requires +# an export license or other authorization. +# +# Contractor Name: Raytheon Company +# Contractor Address: 6825 Pine Street, Suite 340 +# Mail Stop B8 +# Omaha, NE 68106 +# 402.291.0100 +# +# See the AWIPS II Master Rights File ("Master Rights File.pdf") for +# further licensing information. +# # + +# +# A proxy to Python Overrider that utilizes the capabilities of the +# Python RollbackMasterInterface. The objective of this class is +# to prevent the MasterInterface from completing imports because the +# MasterInterface will overwrite modules instead of merging them. +# +# +# SOFTWARE HISTORY +# +# Date Ticket# Engineer Description +# ------------ ---------- ----------- -------------------------- +# 01/14/2014 #2766 bkowal Initial Creation. +# +# +# + +import os, sys, string, traceback +import RollbackMasterInterface +import PythonOverrider + +class PythonOverriderInterface(RollbackMasterInterface.RollbackMasterInterface): + def __init__(self, scriptPath, localizationPath=None): + super(PythonOverriderInterface, self).__init__(scriptPath) + self._localizationPath = localizationPath + + def importModules(self): + modulesToImport = [] + + for s in self._scriptPath.split(os.path.pathsep): + if os.path.exists(s): + scriptfiles = os.listdir(s) + + for filename in scriptfiles: + split = string.split(filename, ".") + if len(split) == 2 and len(split[0]) > 0 and split[1] == "py" and not filename.endswith("Interface.py"): + if not split[0] in modulesToImport: + modulesToImport.append(split[0]) + + for moduleName in modulesToImport: + self._importModule(moduleName) + + def addModule(self, moduleName): + if not moduleName in self.scripts: + self.scripts.append(moduleName) + self.reloadModules() + + def reloadModules(self): + for script in self.scripts: + # first remove all references to the existing module + if sys.modules.has_key(script): + self.clearModuleAttributes(script) + sys.modules.pop(script) + + # now use PythonOverrider to re-import the module + self._importModule(script) + + def _importModule(self, moduleName): + scriptName = moduleName + '.py' + if self._localizationPath: + scriptName = os.path.join(self._localizationPath, scriptName) + try: + importedModule = PythonOverrider.importModule(scriptName) + except Exception, e: + msg = moduleName + "\n" + traceback.format_exc() + self.addImportError(msg) + return + + if not moduleName in self.scripts: + self.scripts.append(moduleName) \ No newline at end of file diff --git a/edexOsgi/com.raytheon.uf.common.python/utility/common_static/base/python/MasterInterface.py b/edexOsgi/com.raytheon.uf.common.python/utility/common_static/base/python/MasterInterface.py index 40d6655c57..6fbd63863d 100644 --- a/edexOsgi/com.raytheon.uf.common.python/utility/common_static/base/python/MasterInterface.py +++ b/edexOsgi/com.raytheon.uf.common.python/utility/common_static/base/python/MasterInterface.py @@ -34,7 +34,8 @@ # 10/20/08 njensen Initial Creation. # 01/17/13 1486 dgilling Make a new-style class. # 09/23/13 16614 njensen Fixed reload method -# +# 01/20/14 2712 bkowal It is now possible to add errors +# from a subclass. # # @@ -141,6 +142,9 @@ class MasterInterface(object): self.__importErrors = [] return returnList + def addImportError(self, error): + self.__importErrors.append(error) + def reloadModule(self, moduleName): if sys.modules.has_key(moduleName): # From the python documentation: