Source code for fudge.core.utilities.xmlNode

# <<BEGIN-copyright>>
# <<END-copyright>>

"""
Wrapper for xml parsers (Etree, DOM, etc), in case we need to support multiple parsers. For now only supporting python's xml.etree.
This is only for reading (not writing) xml; creating new elements should happen within fudge
"""

__metaclass__ = type

[docs]class xmlNode: etree = 'etree' dom = 'dom' allowedParsers = (etree, dom) def __init__(self, parsedXMLNode, parser): """Keep track of which parser is used ('etree','dom', etc). """ if parser not in xmlNode.allowedParsers: raise Exception("%s xml parser not supported" % parser) self.data = parsedXMLNode self.parser = parser if self.parser==xmlNode.etree: self.text = self.data.text self.tail = self.data.tail self.tag = self.data.tag def __len__(self): """Returns the number of child elements.""" return len(self.data) def __repr__(self): return "<Element '%s' at 0x%x>" % (self.tag, id(self)) def __getitem__(self, index): """Return one or more child elements. The index could be a slice.""" if self.parser==xmlNode.etree: if type(index) is slice: # can't slice an iterator, must call getchildren() first: return [xmlNode(a, self.parser) for a in self.data.getchildren()[ index ] ] return xmlNode( self.data[index], self.parser )
[docs] def get(self, attribute, defaultValue=None): """Returns the value of the specified attribute. If the attribute is not present, returns defaultValue.""" if self.parser==xmlNode.etree: return self.data.get(attribute, defaultValue)
[docs] def keys(self): """Returns the name of all attributes in this element, as a list.""" if self.parser==xmlNode.etree: return self.data.keys()
[docs] def items(self): """Returns the name and value of all attributes in this element, as a list of tuples.""" if self.parser==xmlNode.etree: return self.data.items()
[docs] def getchildren(self): """Returns a list of all child elements.""" if self.parser==xmlNode.etree: return [xmlNode(a,self.parser) for a in self.data.getchildren()]
[docs] def find(self, path): """Searches for child elements matching the path, and returns the first match.""" if self.parser==xmlNode.etree: findResult = self.data.find(path) if findResult is None: return return xmlNode( findResult, self.parser )
[docs] def findall(self, path): """Searches for child elements matching the path, and returns all matches.""" if self.parser==xmlNode.etree: return [xmlNode(a,self.parser) for a in self.data.findall(path)]
[docs] def xpath(self, xpath): """Searches for child elements matching the xpath, and returns all matches.""" if self.parser==xmlNode.etree: # etree doesn't currently have native support for xpath. # Make my own, using regex for xpath expressions like "reaction[@label='2']": import itertools import re regex = re.compile("([a-zA-Z]+)\[@([a-zA-Z0-9_]+)=['\"]([a-zA-Z0-9_]+|[a-zA-Z]*\([a-zA-Z0-9_,]+\))['\"]\]") def xpath2( nextInPath, node ): match = regex.match( nextInPath ) if match: element, attr, val = regex.match( nextInPath ).groups() children = [v for v in node.findall(element) if v.get(attr)==val] else: try: children = node.findall( nextInPath ) except SyntaxError: raise SyntaxError("%s is not a valid xPath expression!" % nextInPath) return children xPathList = xpath.split('/') results = [self] for element in xPathList: results = list( itertools.chain(*[xpath2( element, res ) for res in results]) ) return results