/
githubmirror
/
cmssw
Обзор
Документация
Войти
/
githubmirror
/
cmssw
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
FWCore/PythonUtilities/python/XML2Python.py
276 строк
9 KB
Shahzad Malik Muzaffar
[CORE] py2/3 compatibility:drop use of __future__
22 ноя 2024, 20:16
22 ноя 2024, 20:16
4759e53
Код
Авторство
О чём код?
## Original version of code heavily based on recipe written by Wai Yip ## Tung, released under PSF license. ## http://code.activestate.com/recipes/534109/ import re import os import xml.sax.handler import pprint class DataNode (object): spaces = 4 def __init__ (self, **kwargs): self._attrs = {} # XML attributes and child elements self._data = None # child text data self._ncDict = kwargs.get ('nameChangeDict', {}) def __len__ (self): # treat single element as a list of 1 return 1 def __getitem__ (self, key): if isinstance (key, str): return self._attrs.get(key,None) else: return [self][key] def __contains__ (self, name): return name in self._attrs def __nonzero__ (self): return bool (self._attrs or self._data) def __getattr__ (self, name): if name.startswith('__'): # need to do this for Python special methods??? raise AttributeError (name) return self._attrs.get (name, None) def _add_xml_attr (self, name, value): change = self._ncDict.get (name) if change: name = change if name in self._attrs: # multiple attribute of the same name are represented by a list children = self._attrs[name] if not isinstance(children, list): children = [children] self._attrs[name] = children children.append(value) else: self._attrs[name] = value def __str__ (self): return self.stringify() def __repr__ (self): items = sorted (self._attrs.items()) if self._data: items.append(('data', self._data)) return u'{%s}' % ', '.join([u'%s:%s' % (k,repr(v)) for k,v in items]) def attributes (self): return self._attrs @staticmethod def isiterable (obj): return getattr (obj, '__iter__', False) @staticmethod def _outputValues (obj, name, offset): retval = ' ' * offset if name: retval += '%s: ' % name offset += len (name) + DataNode.spaces # if this is a list if isinstance (obj, list): first = True for value in obj: print("value", value, value.__class__.__name__) if first: tempoffset = offset first = False retval += '[\n ' + ' ' * offset else: retval += ',\n ' + ' ' * offset tempoffset = offset if isinstance (value, DataNode): retval += value.stringify (offset=tempoffset) print(" calling stringify for %s" % value) elif DataNode.isiterable (value): retval += DataNode._outputValues (value, '', offset) else: retval += "%s" % value retval += '\n' + ' ' * (offset - 2) +']\n' return retval retval += pprint.pformat(obj, indent= offset, width=1) return retval def stringify (self, name = '', offset = 0): # is this just data and nothing below if self._data and not len (self._attrs): return _outputValues (self._data, name, offset) retval = ' ' * offset if name: retval += '%s : %s\n' % \ (name, pprint.pformat (self._data, indent= offset+DataNode.spaces, width=1) ) else: retval += pprint.pformat (self._data, indent=offset+DataNode.spaces, width=1) return retval # this has attributes retval = '' if name: retval += '\n' + ' ' * offset retval += '%s: ' % name first = True for key, value in sorted (self._attrs.items()): if first: retval += '{ \n' tempspace = offset + 3 first = False else: retval += ',\n' tempspace = offset + 3 if isinstance (value, DataNode): retval += value.stringify (key, tempspace) else: retval += DataNode._outputValues (value, key, tempspace) # this has data too if self._data: retval += ',\n' tempspace = offset + 3 retval += DataNode._ouptputValues (self._data, name, tempspace) retval += '\n ' + ' ' * offset + '}' return retval class TreeBuilder (xml.sax.handler.ContentHandler): non_id_char = re.compile('[^_0-9a-zA-Z]') def __init__ (self, **kwargs): self._stack = [] self._text_parts = [] self._ncDict = kwargs.get ('nameChangeDict', {}) self._root = DataNode (nameChangeDict = self._ncDict) self.current = self._root def startElement (self, name, attrs): self._stack.append( (self.current, self._text_parts)) self.current = DataNode (nameChangeDict = self._ncDict) self._text_parts = [] # xml attributes --> python attributes for k, v in attrs.items(): self.current._add_xml_attr (TreeBuilder._name_mangle(k), v) def endElement (self, name): text = ''.join (self._text_parts).strip() if text: self.current._data = text if self.current.attributes(): obj = self.current else: # a text only node is simply represented by the string obj = text or '' self.current, self._text_parts = self._stack.pop() self.current._add_xml_attr (TreeBuilder._name_mangle(name), obj) def characters (self, content): self._text_parts.append(content) def root (self): return self._root def topLevel (self): '''Returns top level object''' return list(self._root.attributes().values())[0] @staticmethod def _name_mangle (name): return TreeBuilder.non_id_char.sub('_', name) regexList = [ (re.compile (r'&'), '&' ), (re.compile (r'<'), '<' ), (re.compile (r'>'), '>' ), (re.compile (r'"'), '"e;' ), (re.compile (r"'"), ''' ) ] quoteRE = re.compile (r'(\w\s*=\s*")([^"]+)"') def fixQuoteValue (match): '''Changes all characters inside of the match''' quote = match.group(2) for regexTup in regexList: quote = regexTup[0].sub( regexTup[1], quote ) return match.group(1) + quote + '"' def xml2obj (**kwargs): ''' Converts XML data into native Python object. Takes either file handle or string as input. Does NOT fix illegal characters. input source: Exactly one of the three following is needed filehandle - input from file handle contents - input from string filename - input from filename options: filtering - boolean value telling code whether or not to fileter input selection to remove illegal XML characters nameChangeDict - dictionaries of names to change in python object''' # make sure we have exactly 1 input source filehandle = kwargs.get ('filehandle') contents = kwargs.get ('contents') filename = kwargs.get ('filename') if not filehandle and not contents and not filename: raise RuntimeError("You must provide 'filehandle', 'contents', or 'filename'") if filehandle and contents or \ filehandle and filename or \ contents and filename: raise RuntimeError("You must provide only ONE of 'filehandle', 'contents', or 'filename'") # are we filtering? filtering = kwargs.get ('filtering') if filtering: # if we are filtering, we need to read in the contents to modify them if not contents: if not filehandle: try: filehandle = open (filename, 'r') except: raise RuntimeError("Failed to open '%s'" % filename) contents = '' for line in filehandle: contents += line filehandle.close() filehandle = filename = '' contents = quoteRE.sub (fixQuoteValue, contents) ncDict = kwargs.get ('nameChangeDict', {}) builder = TreeBuilder (nameChangeDict = ncDict) if contents: xml.sax.parseString(contents, builder) else: if not filehandle: try: filehandle = open (filename, 'r') except: raise RuntimeError("Failed to open '%s'" % filename) xml.sax.parse(filehandle, builder) return builder.topLevel()