Gaudi Framework, version v23r6

Home   Generated: Wed Jan 30 2013
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
xmlModule.py
Go to the documentation of this file.
1 '''
2 Created on Jul 2, 2011
3 
4 @author: mplajner
5 '''
6 
7 from xml.dom import minidom
8 import Variable
9 import logging.config
10 import os
11 from cPickle import load, dump
12 from hashlib import md5 # pylint: disable=E0611
13 
14 class XMLFile():
15  '''Takes care of XML file operations such as reading and writing.'''
16 
17  def __init__(self):
18  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'
19  self.declaredVars = []
20  logConf = os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + '/log.conf')
21  if not logging.getLogger('envLogger').handlers and os.path.exists(logConf):
22  logging.config.fileConfig(logConf)
23  self.logger = logging.getLogger('envLogger')
24 
25  def variable(self, path, namespace='EnvSchema', name=None):
26  '''Returns list containing name of variable, action and value.
27 
28  @param path: a file name or a file-like object
29 
30  If no name given, returns list of lists of all variables and locals(instead of action 'local' is filled).
31  '''
32  isFilename = type(path) is str
33  if isFilename:
34  checksum = md5()
35  checksum.update(open(path, 'rb').read())
36  checksum = checksum.digest()
37 
38  cpath = path + "c" # preparsed file
39  try:
40  f = open(cpath, 'rb')
41  oldsum, data = load(f)
42  if oldsum == checksum:
43  return data
44  except IOError:
45  pass
46  except EOFError:
47  pass
48 
49  caller = path
50  else:
51  caller = None
52 
53  # Get file
54  doc = minidom.parse(path)
55  if namespace == '':
56  namespace = None
57 
58  ELEMENT_NODE = minidom.Node.ELEMENT_NODE
59  # Get all variables
60  nodes = doc.getElementsByTagNameNS(namespace, "config")[0].childNodes
61  variables = []
62  for node in nodes:
63  # if it is an element node
64  if node.nodeType == ELEMENT_NODE:
65  action = str(node.localName)
66 
67  if action == 'include':
68  if node.childNodes:
69  value = str(node.childNodes[0].data)
70  else:
71  value = ''
72  variables.append((action, (value, caller, str(node.getAttribute('hints')))))
73 
74  else:
75  varname = str(node.getAttribute('variable'))
76  if name and varname != name:
77  continue
78 
79  if action == 'declare':
80  variables.append((action, (varname, str(node.getAttribute('type')), str(node.getAttribute('local')))))
81  else:
82  if node.childNodes:
83  value = str(node.childNodes[0].data)
84  else:
85  value = ''
86  variables.append((action, (varname, value, None)))
87 
88  if isFilename:
89  try:
90  f = open(cpath, 'wb')
91  dump((checksum, variables), f, protocol=2)
92  f.close()
93  except IOError:
94  pass
95  return variables
96 
97 
98  def resetWriter(self):
99  '''resets the buffer of writer'''
100  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'
101  self.declaredVars = []
102 
103  def writeToFile(self, outputFile=None):
104  '''Finishes the XML input and writes XML to file.'''
105  if outputFile is None:
106  raise IOError("No output file given")
107  self.xmlResult += '</env:config>'
108 
109  doc = minidom.parseString(self.xmlResult)
110  with open(outputFile, "w") as f:
111  f.write( doc.toxml() )
112 
113  f.close()
114  return outputFile
115 
116  def writeVar(self, varName, action, value, vartype='list', local=False):
117  '''Writes a action to a file. Declare undeclared elements (non-local list is default type).'''
118  if action == 'declare':
119  self.xmlResult += '<env:declare variable="'+varName+'" type="'+ vartype.lower() +'" local="'+(str(local)).lower()+'" />\n'
120  self.declaredVars.append(varName)
121  return
122 
123  if varName not in self.declaredVars:
124  self.xmlResult += '<env:declare variable="'+varName+'" type="'+ vartype +'" local="'+(str(local)).lower()+'" />\n'
125  self.declaredVars.append(varName)
126  self.xmlResult += '<env:'+action+' variable="'+ varName +'">'+value+'</env:'+action+'>\n'
127 
128 
129 class Report():
130  '''This class is used to catch errors and warnings from XML file processing to allow better managing and testing.'''
131 
132  # Sequence of levels: warn - warning - info - error
133  def __init__(self, level = 1, reportOutput = False):
134  self.errors = []
135  self.warns = []
136  self.info = []
137  self.warnings = []
138  self.level = level
139 
140  if not reportOutput:
141  self.reportOutput = None
142  else:
143  self.reportOutput = open(reportOutput, 'w')
144 
145  logConf = os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + '/log.conf')
146  if not logging.getLogger('envLogger').handlers and os.path.exists(logConf):
147  logging.config.fileConfig(logConf)
148  self.logger = logging.getLogger('envLogger')
149 
150  def addError(self, message, varName = '', action = '', varValue = '', procedure = ''):
151  error = [message, varName, action, varValue, procedure]
152  if self.level < 4:
153  if not self.reportOutput:
154  print 'Error: ' + error[0]
155  else:
156  self.reportOutput.write('Error: ' + error[0] + '\n')
157  self.errors.append(error)
158  self.logger.error(message)
159 
160  def addWarn(self, message, varName = '', action = '', varValue = '', procedure = ''):
161  error = [message, varName, action, varValue, procedure]
162  if self.level < 1:
163  if not self.reportOutput:
164  print 'Warn: ' + error[0]
165  else:
166  self.reportOutput.write('Warn: ' + error[0] + '\n')
167  self.warns.append(error)
168  self.logger.warn(message)
169 
170  def addWarning(self, message, varName = '', action = '', varValue = '', procedure = ''):
171  error = [message, varName, action, varValue, procedure]
172  if self.level < 2:
173  if not self.reportOutput:
174  print 'Warning: ' + error[0]
175  else:
176  self.reportOutput.write('Warning: ' + error[0] + '\n')
177  self.warnings.append(error)
178  self.logger.warning(message)
179 
180  def addInfo(self, message, varName = '', action = '', varValue = '', procedure = ''):
181  error = [message, varName, action, varValue, procedure]
182  if self.level < 3:
183  if not self.reportOutput:
184  print 'Info: ' + error[0]
185  else:
186  self.reportOutput.write('Info: ' + error[0] + '\n')
187  self.warnings.append(error)
188  self.logger.info(message)
189 
190  def clear(self):
191  self.errors = []
192  self.warns = []
193  self.info = []
194  self.warnings = []
195 
196  def closeFile(self):
197  if self.reportOutput:
198  self.reportOutput.close()
199 
200  def numErrors(self):
201  return len(self.errors)
202 
203  def numWarnings(self):
204  return len(self.warns) + len(self.warnings)
205 
206  def error(self, key):
207  return self.errors[key]
208 
209  def warn(self, key):
210  return self.warns[key]
211 
212 
214  '''This class is for checking and merging XML files.
215 
216  Variables are stored in a double dictionary with keys of names and then actions.
217  '''
218  def __init__(self, separator=':', reportLevel=0, reportOutput=None):
219  self.posActions = ['append','prepend','set','unset', 'remove', 'remove-regexp', 'declare']
220  self.separator = separator
221  self.report = Report(reportLevel, reportOutput=reportOutput)
222  self.varNames = []
223  self.realVariables = {}
224  self.variables = {}
225  self.file = None
226  self.output = None
227 
228  def errors(self):
229  return self.report.numErrors()
230 
231  def warnings(self):
232  return self.report.numWarnings()
233 
234  def check(self, xmlFile):
235  '''Runs a check through file
236 
237  First check is made on wrong action parameter.
238  All valid actions are checked after and duplicated variables as well.
239  '''
240  #self.local = Variable.Local()
241  # reset state
242  self.varNames = []
243  self.realVariables = {}
244  self.variables = {}
245 
246  # load variables and resolve references to locals and then variables
247  self._loadVariables(xmlFile)
248 
249  # report
250  if (self.warnings() > 0 or self.errors() > 0):
251  self.report.addInfo('Encountered '+ (str)(self.warnings()) +' warnings and ' + (str)(self.errors()) + ' errors.')
252  return [self.warnings(), self.errors()]
253  else:
254  return True
255 
256  self.report.closeFile()
257 
258 
259  def merge(self, xmlDoc1, xmlDoc2, outputFile = '', reportCheck = False):
260  '''Merges two files together. Files are checked first during variables loading process.
261 
262  Second file is processed first, then the first file and after that they are merged together.
263  '''
264  self.output = outputFile
265  self.file = XMLFile()
266  self.variables = {}
267 
268  variables = self.file.variable(xmlDoc1)
269  self._processVars(variables)
270  variables = self.file.variable(xmlDoc2)
271  self._processVars(variables)
272 
273  if not reportCheck:
274  self.report.level = 5
275 
276  self.file.writeToFile(outputFile)
277 
278  self.report.addInfo('Files merged. Running check on the result.')
279  self.check(self.output)
280  self.report.closeFile()
281 
282  def _processVars(self, variables):
283  for action, (arg1, arg2, arg3) in variables:
284  if action == 'declare':
285  if arg1 in self.variables.keys():
286  if arg2.lower() != self.variables[arg1][0]:
287  raise Variable.EnvError(arg1, 'redeclaration')
288  else:
289  if arg3.lower() != self.variables[arg1][1]:
290  raise Variable.EnvError(arg1, 'redeclaration')
291  else:
292  self.file.writeVar(arg1, 'declare', '', arg2, arg3)
293  self.variables[arg1] = [arg2.lower(), arg3.lower()]
294  else:
295  self.file.writeVar(arg1, action, arg2)
296 
297 
298  def _checkVariable(self, varName, action, local, value, nodeNum):# pylint: disable=W0613
299  '''Tries to add to variables dict, checks for errors during process'''
300 
301  if varName not in self.variables:
302  self.variables[varName] = []
303  self.variables[varName].append(action)
304 
305  # If variable is in dict, check if this is not an unset command
306  elif action == 'unset':
307  if 'unset' in self.variables[varName]:
308  self.report.addWarn('Multiple "unset" actions found for variable: "'+varName+'".', varName, 'multiple unset','', 'checkVariable')
309  if not('unset' in self.variables[varName] and len(self.variables[varName]) == 1):
310  self.report.addError('Node '+str(nodeNum)+': "unset" action found for variable "'+varName+'" after previous command(s). Any previous commands are overridden.', varName, 'unset overwrite')
311 
312  # or set command
313  elif action == 'set':
314  if len(self.variables[varName]) == 1 and 'unset' in self.variables[varName]:
315  self.report.addWarn('Node '+str(nodeNum)+': "set" action found for variable "'+varName+'" after unset. Can be merged to one set only.')
316  else:
317  self.report.addError('Node '+str(nodeNum)+': "set" action found for variable "'+varName+'" after previous command(s). Any previous commands are overridden.', varName, 'set overwrite')
318  if 'set' in self.variables[varName]:
319  self.report.addWarn('Multiple "set" actions found for variable: "'+varName+'".', varName, 'multiple set','', 'checkVariable')
320 
321  if action not in self.variables[varName]:
322  self.variables[varName].append(action)
323 
324  try:
325  if action == 'remove-regexp':
326  action = 'remove_regexp'
327  eval('(self.realVariables[varName]).'+action+'(value)')
328  except Variable.EnvError as e:
329  if e.code == 'undefined':
330  self.report.addWarn('Referenced variable "' +e.val+ '" is not defined.')
331  elif e.code == 'ref2var':
332  self.report.addError('Reference to list from the middle of string.')
333  elif e.code == 'redeclaration':
334  self.report.addError('Redeclaration of variable "'+e.val+'".')
335  else:
336  self.report.addError('Unknown environment error occured.')
337 
338 
339  def _loadVariables(self, fileName):
340  '''loads XML file for input variables'''
341  XMLfile = XMLFile()
342  variables = XMLfile.variable(fileName)
343  for i, (action, (arg1, arg2, arg3)) in enumerate(variables):
344  undeclared = False
345  if arg1 == '':
346  raise RuntimeError('Empty variable or local name is not allowed.')
347 
348  if arg1 not in self.realVariables.keys():
349  if action != 'declare':
350  self.report.addInfo('Node '+str(i)+': Variable '+arg1+' is used before declaration. Treated as an unlocal list furthermore.')
351  undeclared = True
352  self.realVariables[arg1] = Variable.List(arg1, False, report=self.report)
353  else:
354  self.varNames.append(arg1)
355  if arg2 == 'list':
356  self.realVariables[arg1] = Variable.List(arg1, arg3, report=self.report)
357  else:
358  self.realVariables[arg1] = Variable.Scalar(arg1, arg3, report=self.report)
359  if not undeclared:
360  continue
361 
362  if action not in self.posActions:
363  self.report.addError('Node '+str(i)+': Action "'+action+'" which is not implemented found. Variable "'+arg1+'".', arg1, action, arg2)
364  continue
365 
366  else:
367  if action == 'declare':
368  self.report.addError('Node '+str(i)+': Variable '+arg1+' is redeclared.')
369  else:
370  self._checkVariable(arg1, action, self.realVariables[arg1].local, str(arg2), i)

Generated at Wed Jan 30 2013 17:13:37 for Gaudi Framework, version v23r6 by Doxygen version 1.8.2 written by Dimitri van Heesch, © 1997-2004