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
# will now truly override 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):
"""
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:
files : the files that are to be overridden
Returns:
a new module that contains all the necessary elements
"""
"""
themodule = imp.new_module('tmpmodule')
# modules = list of all the modules
for module in files :
@ -126,12 +129,17 @@ def _mergeClasses(source, target, className):
attributeName = className + '.' + attr
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
for attr in dir(targetClass):
if attr != '__init__' \
and isinstance(getattr(targetClass, attr), types.MethodType) \
and not attr in dir(sourceClass):
if isinstance(getattr(targetClass, attr), types.MethodType) \
and not attr in sourceClass.__dict__:
# complete the merge / override of methods for any method that
# 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
# the same name as the original
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)
@ -164,8 +182,13 @@ def _compareClasses(clazz1, clazz2):
return False
# compare the attributes directly
attr1 = getattr(clazz1, clazz1Attr[i])
attr2 = getattr(clazz2, clazz2Attr[i])
attr1 = None
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):
return False

View file

@ -23,25 +23,31 @@
# 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.
#
#
# 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
#
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# 01/14/2014 #2766 bkowal Initial Creation.
# 02/09/2015 #4120 reblum Inherit straight from MasterInterface.
#
#
#
import os, sys, string, traceback
import RollbackMasterInterface
import MasterInterface
import PythonOverrider
class PythonOverriderInterface(RollbackMasterInterface.RollbackMasterInterface):
class PythonOverriderInterface(MasterInterface.MasterInterface):
def __init__(self, scriptPath, localizationPath=None):
super(PythonOverriderInterface, self).__init__(scriptPath)
super(PythonOverriderInterface, self).__init__()
self._localizationPath = localizationPath
self._scriptPath = scriptPath
def importModules(self):
modulesToImport = []
@ -58,22 +64,23 @@ class PythonOverriderInterface(RollbackMasterInterface.RollbackMasterInterface):
for moduleName in modulesToImport:
self._importModule(moduleName)
def addModule(self, moduleName):
if not moduleName in self.scripts:
self.scripts.append(moduleName)
self.reloadModules()
def reloadModule(self, moduleName):
if sys.modules.has_key(moduleName):
self.clearModuleAttributes(moduleName)
self._importModule(moduleName)
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)
self.clearModuleAttributes(script)
# now use PythonOverrider to re-import the module
self._importModule(script)
def _importModule(self, moduleName):
scriptName = moduleName + '.py'
if self._localizationPath:
@ -86,4 +93,11 @@ class PythonOverriderInterface(RollbackMasterInterface.RollbackMasterInterface):
return
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