Gaudi Framework, version v23r4

Home   Generated: Mon Sep 17 2012

xmlModule.py

Go to the documentation of this file.
00001 '''
00002 Created on Jul 2, 2011
00003 
00004 @author: mplajner
00005 '''
00006 
00007 from xml.dom import minidom
00008 import Variable
00009 import logging.config
00010 import os
00011 from cPickle import load, dump
00012 from hashlib import md5
00013 
00014 class XMLFile():
00015     '''Takes care of XML file operations such as reading and writing.'''
00016 
00017     def __init__(self):
00018         self.xmlResult = '<?xml version="1.0" encoding="UTF-8"?><env:config xmlns:env="EnvSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="EnvSchema ./EnvSchema.xsd ">\n'
00019         self.declaredVars = []
00020         logConf = os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + '/log.conf')
00021         if not logging.getLogger('envLogger').handlers and os.path.exists(logConf):
00022             logging.config.fileConfig(logConf)
00023         self.logger = logging.getLogger('envLogger')
00024 
00025     def variable(self, path, namespace='EnvSchema', name=None):
00026         '''Returns list containing name of variable, action and value
00027 
00028         If no name given, returns list of lists of all variables and locals(instead of action 'local' is filled).
00029         '''
00030         if not os.path.isfile(path):
00031             raise IOError('No such file.')
00032         sum = md5()
00033         sum.update(open(path, 'rb').read())
00034         sum = sum.digest()
00035 
00036         cpath = path + "c" # preparsed file
00037         try:
00038             f = open(cpath, 'rb')
00039             oldsum, data = load(f)
00040             if oldsum == sum:
00041                 return data
00042         except:
00043             pass
00044 
00045         # Get file
00046         self.doc = minidom.parse(path)
00047         if namespace == '':
00048             namespace = None
00049 
00050         # Get all variables
00051         nodes = self.doc.getElementsByTagNameNS(namespace, "config")[0].childNodes
00052         variables = []
00053         for node in nodes:
00054             # if it is an element node
00055             if node.nodeType == 1:
00056                 varname = str(node.getAttribute('variable'))
00057                 if name and varname != name:
00058                         continue
00059 
00060                 action = str(node.localName)
00061                 if action == 'declare':
00062                     variables.append((action, (varname, str(node.getAttribute('type')), str(node.getAttribute('local')))))
00063                 else:
00064                     if node.childNodes:
00065                         value = str(node.childNodes[0].data)
00066                     else:
00067                         value = ''
00068                     variables.append((action, (varname, value, None)))
00069 
00070         try:
00071             f = open(cpath, 'wb')
00072             dump((sum, variables), f, protocol=2)
00073             f.close()
00074         except:
00075             pass
00076         return variables
00077 
00078 
00079     def resetWriter(self):
00080         '''resets the buffer of writer'''
00081         self.xmlResult = '<?xml version="1.0" encoding="UTF-8"?><env:config xmlns:env="EnvSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="EnvSchema ./EnvSchema.xsd ">\n'
00082         self.declaredVars = []
00083 
00084     def writeToFile(self, outputFile = ''):
00085         '''Finishes the XML input and writes XML to file.'''
00086         if(outputFile == ''):
00087             raise IOError("No output file given")
00088         self.xmlResult += '</env:config>'
00089 
00090         doc = minidom.parseString(self.xmlResult)
00091         with open(outputFile, "w") as f:
00092             f.write( doc.toxml() )
00093 
00094         f.close()
00095         return outputFile
00096 
00097     def writeVar(self, varName, action, value, type='list', local='false'):
00098         '''Writes a action to a file. Declare undeclared elements (not local List is default type).'''
00099         if action == 'declare':
00100             self.xmlResult += '<env:declare variable="'+varName+'" type="'+ type.lower() +'" local="'+(str(local)).lower()+'" />\n'
00101             self.declaredVars.append(varName)
00102             return
00103 
00104         if varName not in self.declaredVars:
00105             self.xmlResult += '<env:declare variable="'+varName+'" type="'+ type +'" local="'+(str(local)).lower()+'" />\n'
00106             self.declaredVars.append(varName)
00107         self.xmlResult += '<env:'+action+' variable="'+ varName +'">'+value+'</env:'+action+'>\n'
00108 
00109 
00110 class Report():
00111     '''This class is used to catch errors and warnings from XML file processing to allow better managing and testing.'''
00112 
00113     # Sequence of levels: warn - warning - info - error
00114     def __init__(self, level = 1, reportOutput = False):
00115         self.errors = []
00116         self.warns = []
00117         self.info = []
00118         self.warnings = []
00119         self.level = level
00120 
00121         if not reportOutput:
00122             self.reportOutput = False
00123         else:
00124             self.reportOutput = open(reportOutput, 'w')
00125 
00126         logConf = os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + '/log.conf')
00127         if not logging.getLogger('envLogger').handlers and os.path.exists(logConf):
00128             logging.config.fileConfig(logConf)
00129         self.logger = logging.getLogger('envLogger')
00130 
00131     def addError(self, message, varName = '', action = '', varValue = '', procedure = ''):
00132         error = [message, varName, action, varValue, procedure]
00133         if self.level < 4:
00134             if not self.reportOutput:
00135                 print 'Error: ' + error[0]
00136             else:
00137                 self.reportOutput.write('Error: ' + error[0] + '\n')
00138         self.errors.append(error)
00139         self.logger.error(message)
00140 
00141     def addWarn(self, message, varName = '', action = '', varValue = '', procedure = ''):
00142         error = [message, varName, action, varValue, procedure]
00143         if self.level < 1:
00144             if not self.reportOutput:
00145                 print 'Warn: ' + error[0]
00146             else:
00147                 self.reportOutput.write('Warn: ' + error[0] + '\n')
00148         self.warns.append(error)
00149         self.logger.warn(message)
00150 
00151     def addWarning(self, message, varName = '', action = '', varValue = '', procedure = ''):
00152         error = [message, varName, action, varValue, procedure]
00153         if self.level < 2:
00154             if not self.reportOutput:
00155                 print 'Warning: ' + error[0]
00156             else:
00157                 self.reportOutput.write('Warning: ' + error[0] + '\n')
00158         self.warnings.append(error)
00159         self.logger.warning(message)
00160 
00161     def addInfo(self, message, varName = '', action = '', varValue = '', procedure = ''):
00162         error = [message, varName, action, varValue, procedure]
00163         if self.level < 3:
00164             if not self.reportOutput:
00165                 print 'Info: ' + error[0]
00166             else:
00167                 self.reportOutput.write('Info: ' + error[0] + '\n')
00168         self.warnings.append(error)
00169         self.logger.info(message)
00170 
00171     def clear(self):
00172         self.errors = []
00173         self.warns = []
00174         self.info = []
00175         self.warnings = []
00176 
00177     def closeFile(self):
00178         if self.reportOutput:
00179             self.reportOutput.close()
00180 
00181     def numErrors(self):
00182         return len(self.errors)
00183 
00184     def numWarnings(self):
00185         return len(self.warns) + len(self.warnings)
00186 
00187     def error(self, key):
00188         return self.errors[key]
00189 
00190     def warn(self, key):
00191         return self.warns[key]
00192 
00193 
00194 class XMLOperations():
00195     '''This class is for checking and merging XML files.
00196 
00197     Variables are stored in a double dictionary with keys of names and then actions.
00198     '''
00199     def __init__(self, separator=':', reportLevel = 0, reportOutput = False):
00200         self.posActions = ['append','prepend','set','unset', 'remove', 'remove-regexp', 'declare']
00201         self.separator = separator
00202         self.report = Report(reportLevel, reportOutput = reportOutput)
00203 
00204     def errors(self):
00205         return self.report.numErrors()
00206 
00207     def warnings(self):
00208         return self.report.numWarnings()
00209 
00210     def check(self, xmlFile):
00211         '''Runs a check through file
00212 
00213         First check is made on wrong action parameter.
00214         All valid actions are checked after and duplicated variables as well.
00215         '''
00216         #self.local = Variable.Local()
00217         self.varNames = []
00218         self.realVariables = {}
00219         self.variables = {}
00220 
00221         # load variables and resolve references to locals and then variables
00222         self._loadVariables(xmlFile)
00223 
00224         # report
00225         if (self.warnings() > 0 or self.errors() > 0):
00226             self.report.addInfo('Encountered '+ (str)(self.warnings()) +' warnings and ' + (str)(self.errors()) + ' errors.')
00227             return [self.warnings(), self.errors()]
00228         else:
00229             return True
00230 
00231         self.report.closeFile()
00232 
00233 
00234     def merge(self, xmlDoc1, xmlDoc2, outputFile = '', reportCheck = False):
00235         '''Merges two files together. Files are checked first during variables loading process.
00236 
00237         Second file is processed first, then the first file and after that they are merged together.
00238         '''
00239         self.output = outputFile
00240         self.file = XMLFile()
00241         self.variables = {}
00242 
00243         variables = self.file.variable(xmlDoc1)
00244         self._processVars(variables)
00245         variables = self.file.variable(xmlDoc2)
00246         self._processVars(variables)
00247 
00248         if not reportCheck:
00249             self.report.level = 5
00250 
00251         self.file.writeToFile(outputFile)
00252 
00253         self.report.addInfo('Files merged. Running check on the result.')
00254         self.check(self.output)
00255         self.report.closeFile()
00256 
00257     def _processVars(self, variables):
00258         for action, (arg1, arg2, arg3) in variables:
00259             if action == 'declare':
00260                 if arg1 in self.variables.keys():
00261                     if arg2.lower() != self.variables[arg1][0]:
00262                         raise Variable.EnvironmentError(arg1, 'redeclaration')
00263                     else:
00264                         if arg3.lower() != self.variables[arg1][1]:
00265                             raise Variable.EnvironmentError(arg1, 'redeclaration')
00266                 else:
00267                     self.file.writeVar(arg1, 'declare', '', arg2, arg3)
00268                     self.variables[arg1] = [arg2.lower(), arg3.lower()]
00269             else:
00270                 self.file.writeVar(arg1, action, arg2)
00271 
00272 
00273     def _checkVariable(self, varName, action, local, value, nodeNum):
00274         '''Tries to add to variables dict, checks for errors during process'''
00275 
00276         if varName not in self.variables:
00277             self.variables[varName] = []
00278             self.variables[varName].append(action)
00279 
00280         # If variable is in dict, check if this is not an unset command
00281         elif action == 'unset':
00282             if 'unset' in self.variables[varName]:
00283                 self.report.addWarn('Multiple "unset" actions found for variable: "'+varName+'".', varName, 'multiple unset','', 'checkVariable')
00284             if not('unset' in self.variables[varName] and len(self.variables[varName]) == 1):
00285                 self.report.addError('Node '+str(nodeNum)+': "unset" action found for variable "'+varName+'" after previous command(s). Any previous commands are overridden.', varName, 'unset overwrite')
00286 
00287         # or set command
00288         elif action == 'set':
00289             if len(self.variables[varName]) == 1 and 'unset' in self.variables[varName]:
00290                 self.report.addWarn('Node '+str(nodeNum)+': "set" action found for variable "'+varName+'" after unset. Can be merged to one set only.')
00291             else:
00292                 self.report.addError('Node '+str(nodeNum)+': "set" action found for variable "'+varName+'" after previous command(s). Any previous commands are overridden.', varName, 'set overwrite')
00293                 if 'set' in self.variables[varName]:
00294                     self.report.addWarn('Multiple "set" actions found for variable: "'+varName+'".', varName, 'multiple set','', 'checkVariable')
00295 
00296         if action not in self.variables[varName]:
00297             self.variables[varName].append(action)
00298 
00299         try:
00300             if action == 'remove-regexp':
00301                 action = 'remove_regexp'
00302             eval('(self.realVariables[varName]).'+action+'(value)')
00303         except Variable.EnvironmentError as e:
00304             if e.code == 'undefined':
00305                 self.report.addWarn('Referenced variable "' +e.val+ '" is not defined.')
00306             elif e.code == 'ref2var':
00307                 self.report.addError('Reference to list from the middle of string.')
00308             elif e.code == 'redeclaration':
00309                 self.report.addError('Redeclaration of variable "'+e.val+'".')
00310             else:
00311                 self.report.addError('Unknown environment error occured.')
00312 
00313 
00314     def _loadVariables(self, fileName):
00315         '''loads XML file for input variables'''
00316         XMLfile = XMLFile()
00317         variables = XMLfile.variable(fileName)
00318         for i, (action, (arg1, arg2, arg3))  in enumerate(variables):
00319             undeclared = False
00320             if arg1 == '':
00321                 raise RuntimeError('Empty variable or local name is not allowed.')
00322 
00323             if arg1 not in self.realVariables.keys():
00324                 if action != 'declare':
00325                     self.report.addInfo('Node '+str(i)+': Variable '+arg1+' is used before declaration. Treated as an unlocal list furthermore.')
00326                     undeclared = True
00327                     self.realVariables[arg1] = Variable.List(arg1, False, report=self.report)
00328                 else:
00329                     self.varNames.append(arg1)
00330                     if arg2 == 'list':
00331                         self.realVariables[arg1] = Variable.List(arg1, arg3, report=self.report)
00332                     else:
00333                         self.realVariables[arg1] = Variable.Scalar(arg1, arg3, report=self.report)
00334                     if not undeclared:
00335                         continue
00336 
00337             if action not in self.posActions:
00338                 self.report.addError('Node '+str(i)+': Action "'+action+'" which is not implemented found. Variable "'+arg1+'".', arg1, action, arg2)
00339                 continue
00340 
00341             else:
00342                 if action == 'declare':
00343                     self.report.addError('Node '+str(i)+': Variable '+arg1+' is redeclared.')
00344                 else:
00345                     self._checkVariable(arg1, action, self.realVariables[arg1].local, str(arg2), i)
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines

Generated at Mon Sep 17 2012 13:49:25 for Gaudi Framework, version v23r4 by Doxygen version 1.7.2 written by Dimitri van Heesch, © 1997-2004