Omaha #4120 Improve PythonOverriderInterface and PythonOverriderCore to handle incremental overrides for HS.

Change-Id: I04ae6553624237e909dbeabbccff40113303aeda

Former-commit-id: e79792fddfcb80e72a406a5e817bb7878cb692c0
This commit is contained in:
Robert Blum 2015-02-12 13:13:41 -06:00
parent 923fb69061
commit d9f635e8c7
2 changed files with 59 additions and 22 deletions

View file

@ -33,6 +33,7 @@
# 01/20/14 2712 bkowal Improve python class merging. Classes # 01/20/14 2712 bkowal Improve python class merging. Classes
# will now truly override each other # will now truly override each other
# instead of extending each other. # instead of extending each other.
# 02/09/15 4120 reblum Fixed bugs in the class merging.
# #
# #
# #
@ -42,14 +43,16 @@ import ModuleUtil
def _internalOverride(files): def _internalOverride(files):
""" """
Takes the files and overrides them Combines the different localization levels of the same class/module into a single
class/module with the lower localization level methods taking precedence over the
higher localization level methods.
Args: Args:
files : the files that are to be overridden files : the files that are to be overridden
Returns: Returns:
a new module that contains all the necessary elements a new module that contains all the necessary elements
""" """
themodule = imp.new_module('tmpmodule') themodule = imp.new_module('tmpmodule')
# modules = list of all the modules # modules = list of all the modules
for module in files : for module in files :
@ -126,12 +129,17 @@ def _mergeClasses(source, target, className):
attributeName = className + '.' + attr attributeName = className + '.' + attr
target = _mergeAttributes(source, target, attributeName) target = _mergeAttributes(source, target, attributeName)
# Get the set of unimplemented abstractmethods from the sourceClass
abstractMethods = set()
if sourceClass.__dict__.has_key('__abstractmethods__'):
for method in sourceClass.__abstractmethods__:
abstractMethods.add(method)
# make new class "extend" the original class # make new class "extend" the original class
for attr in dir(targetClass): for attr in dir(targetClass):
if attr != '__init__' \ if isinstance(getattr(targetClass, attr), types.MethodType) \
and isinstance(getattr(targetClass, attr), types.MethodType) \ and not attr in sourceClass.__dict__:
and not attr in dir(sourceClass):
# complete the merge / override of methods for any method that # complete the merge / override of methods for any method that
# the new class does not implement # the new class does not implement
@ -141,7 +149,17 @@ def _mergeClasses(source, target, className):
# copy the method implementation to the other class and give it # copy the method implementation to the other class and give it
# the same name as the original # the same name as the original
classMethodDirective = _buildReplaceClassMethodDirective(className, attr) classMethodDirective = _buildReplaceClassMethodDirective(className, attr)
exec(classMethodDirective) exec(classMethodDirective)
# If the method we just merged was an abstractmethod remove it from
# abstractMethods since it now has been implemented.
if attr in abstractMethods:
abstractMethods.remove(attr)
# Update __abstractmethods__ so that it correctly reflects if there are any
# unimplemented abstactmethods for the merged class.
if sourceClass.__dict__.has_key('__abstractmethods__'):
directive = 'sourceClass.__abstractmethods__ = frozenset(abstractMethods)'
exec(directive)
return _mergeAttributes(source, target, className) return _mergeAttributes(source, target, className)
@ -164,8 +182,13 @@ def _compareClasses(clazz1, clazz2):
return False return False
# compare the attributes directly # compare the attributes directly
attr1 = getattr(clazz1, clazz1Attr[i]) attr1 = None
attr2 = getattr(clazz2, clazz2Attr[i]) attr2 = None
# see http://bugs.python.org/issue10006
if clazz1Attr[i] != '__abstractmethods__':
attr1 = getattr(clazz1, clazz1Attr[i])
if clazz2Attr[i] != '__abstractmethods__':
attr2 = getattr(clazz2, clazz2Attr[i])
if (attr1 != attr2): if (attr1 != attr2):
return False return False

View file

@ -23,25 +23,31 @@
# Python RollbackMasterInterface. The objective of this class is # Python RollbackMasterInterface. The objective of this class is
# to prevent the MasterInterface from completing imports because the # to prevent the MasterInterface from completing imports because the
# MasterInterface will overwrite modules instead of merging them. # MasterInterface will overwrite modules instead of merging them.
# #
# TODO: njensen thinks it may be safer and more stable to dynamically
# create a new type with the appropriate inheritance tree and then
# utilize normal object-oriented inheritance to provide the incremental/selective
# overrides capability. Investigate as necessary.
# #
# SOFTWARE HISTORY # SOFTWARE HISTORY
# #
# Date Ticket# Engineer Description # Date Ticket# Engineer Description
# ------------ ---------- ----------- -------------------------- # ------------ ---------- ----------- --------------------------
# 01/14/2014 #2766 bkowal Initial Creation. # 01/14/2014 #2766 bkowal Initial Creation.
# 02/09/2015 #4120 reblum Inherit straight from MasterInterface.
# #
# #
# #
import os, sys, string, traceback import os, sys, string, traceback
import RollbackMasterInterface import MasterInterface
import PythonOverrider import PythonOverrider
class PythonOverriderInterface(RollbackMasterInterface.RollbackMasterInterface): class PythonOverriderInterface(MasterInterface.MasterInterface):
def __init__(self, scriptPath, localizationPath=None): def __init__(self, scriptPath, localizationPath=None):
super(PythonOverriderInterface, self).__init__(scriptPath) super(PythonOverriderInterface, self).__init__()
self._localizationPath = localizationPath self._localizationPath = localizationPath
self._scriptPath = scriptPath
def importModules(self): def importModules(self):
modulesToImport = [] modulesToImport = []
@ -58,22 +64,23 @@ class PythonOverriderInterface(RollbackMasterInterface.RollbackMasterInterface):
for moduleName in modulesToImport: for moduleName in modulesToImport:
self._importModule(moduleName) self._importModule(moduleName)
def addModule(self, moduleName): def addModule(self, moduleName):
if not moduleName in self.scripts: if not moduleName in self.scripts:
self.scripts.append(moduleName) self.scripts.append(moduleName)
self.reloadModules() self.reloadModules()
def reloadModule(self, moduleName):
if sys.modules.has_key(moduleName):
self.clearModuleAttributes(moduleName)
self._importModule(moduleName)
def reloadModules(self): def reloadModules(self):
for script in self.scripts: for script in self.scripts:
# first remove all references to the existing module self.clearModuleAttributes(script)
if sys.modules.has_key(script):
self.clearModuleAttributes(script)
sys.modules.pop(script)
# now use PythonOverrider to re-import the module # now use PythonOverrider to re-import the module
self._importModule(script) self._importModule(script)
def _importModule(self, moduleName): def _importModule(self, moduleName):
scriptName = moduleName + '.py' scriptName = moduleName + '.py'
if self._localizationPath: if self._localizationPath:
@ -86,4 +93,11 @@ class PythonOverriderInterface(RollbackMasterInterface.RollbackMasterInterface):
return return
if not moduleName in self.scripts: if not moduleName in self.scripts:
self.scripts.append(moduleName) self.scripts.append(moduleName)
def getStartupErrors(self):
from java.util import ArrayList
errorList = ArrayList()
for err in self.getImportErrors():
errorList.add(str(err))
return errorList