diff --git a/src/common/xmpp/simplexml.py b/src/common/xmpp/simplexml.py index 3d360ab58..201809ff9 100644 --- a/src/common/xmpp/simplexml.py +++ b/src/common/xmpp/simplexml.py @@ -20,362 +20,380 @@ I'm personally using it in many other separate projects. It is designed to be as import xml.parsers.expat def XMLescape(txt): - """Returns provided string with symbols & < > " replaced by their respective XML entities.""" - return txt.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """) + """Returns provided string with symbols & < > " replaced by their respective XML entities.""" + return txt.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """) ENCODING='utf-8' def ustr(what): - """Converts object "what" to unicode string using it's own __str__ method if accessible or unicode method otherwise.""" - if type(what) == type(u''): return what - try: r=what.__str__() - except AttributeError: r=str(what) - if type(r)<>type(u''): return unicode(r,ENCODING) - return r + """Converts object "what" to unicode string using it's own __str__ method if accessible or unicode method otherwise.""" + if type(what) == type(u''): return what + try: r=what.__str__() + except AttributeError: r=str(what) + if type(r)<>type(u''): return unicode(r,ENCODING) + return r class Node: - """ Node class describes syntax of separate XML Node. It have a constructor that permits node creation - from set of "namespace name", attributes and payload of text strings and other nodes. - It does not natively support building node from text string and uses NodeBuilder class for that purpose. - After creation node can be mangled in many ways so it can be completely changed. - Also node can be serialised into string in one of two modes: default (where the textual representation - of node describes it exactly) and "fancy" - with whitespace added to make indentation and thus make - result more readable by human. + """ Node class describes syntax of separate XML Node. It have a constructor that permits node creation + from set of "namespace name", attributes and payload of text strings and other nodes. + It does not natively support building node from text string and uses NodeBuilder class for that purpose. + After creation node can be mangled in many ways so it can be completely changed. + Also node can be serialised into string in one of two modes: default (where the textual representation + of node describes it exactly) and "fancy" - with whitespace added to make indentation and thus make + result more readable by human. - Node class have attribute FORCE_NODE_RECREATION that is defaults to False thus enabling fast node - replication from the some other node. The drawback of the fast way is that new node shares some - info with the "original" node that is changing the one node may influence the other. Though it is - rarely needed (in xmpppy it is never needed at all since I'm usually never using original node after - replication (and using replication only to move upwards on the classes tree). - """ - FORCE_NODE_RECREATION=0 - def __init__(self, tag=None, attrs={}, payload=[], parent=None, node=None): - """ Takes "tag" argument as the name of node (prepended by namespace, if needed and separated from it - by a space), attrs dictionary as the set of arguments, payload list as the set of textual strings - and child nodes that this node carries within itself and "parent" argument that is another node - that this one will be the child of. Also the __init__ can be provided with "node" argument that is - either a text string containing exactly one node or another Node instance to begin with. If both - "node" and other arguments is provided then the node initially created as replica of "node" - provided and then modified to be compliant with other arguments.""" - if node: - if self.FORCE_NODE_RECREATION and type(node)==type(self): node=str(node) - if type(node)<>type(self): node=NodeBuilder(node,self) - else: - self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = node.name,node.namespace,{},[],[],node.parent - for key in node.attrs.keys(): self.attrs[key]=node.attrs[key] - for data in node.data: self.data.append(data) - for kid in node.kids: self.kids.append(kid) - else: self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = 'tag','',{},[],[],None + Node class have attribute FORCE_NODE_RECREATION that is defaults to False thus enabling fast node + replication from the some other node. The drawback of the fast way is that new node shares some + info with the "original" node that is changing the one node may influence the other. Though it is + rarely needed (in xmpppy it is never needed at all since I'm usually never using original node after + replication (and using replication only to move upwards on the classes tree). + """ + FORCE_NODE_RECREATION=0 + def __init__(self, tag=None, attrs={}, payload=[], parent=None, node=None): + """ Takes "tag" argument as the name of node (prepended by namespace, if needed and separated from it + by a space), attrs dictionary as the set of arguments, payload list as the set of textual strings + and child nodes that this node carries within itself and "parent" argument that is another node + that this one will be the child of. Also the __init__ can be provided with "node" argument that is + either a text string containing exactly one node or another Node instance to begin with. If both + "node" and other arguments is provided then the node initially created as replica of "node" + provided and then modified to be compliant with other arguments.""" + if node: + if self.FORCE_NODE_RECREATION and type(node)==type(self): + node=str(node) + if type(node)<>type(self): + node=NodeBuilder(node,self) + else: + self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = node.name,node.namespace,{},[],[],node.parent + for key in node.attrs.keys(): self.attrs[key]=node.attrs[key] + for data in node.data: self.data.append(data) + for kid in node.kids: self.kids.append(kid) + else: self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = 'tag','',{},[],[],None - if tag: self.namespace, self.name = ([self.namespace]+tag.split())[-2:] - if parent: self.parent = parent - if self.parent and not self.namespace: self.namespace=self.parent.namespace - for attr in attrs.keys(): - self.attrs[attr]=attrs[attr] - if type(payload) in (type(''),type(u'')): payload=[payload] - for i in payload: - if type(i)==type(self): self.addChild(node=i) - else: self.data.append(ustr(i)) + if tag: self.namespace, self.name = ([self.namespace]+tag.split())[-2:] + if parent: self.parent = parent + if self.parent and not self.namespace: self.namespace=self.parent.namespace + for attr in attrs.keys(): + self.attrs[attr]=attrs[attr] + if type(payload) in (type(''),type(u'')): payload=[payload] + for i in payload: + if type(i)==type(self): self.addChild(node=i) + else: self.data.append(ustr(i)) - def __str__(self,fancy=0): - """ Method used to dump node into textual representation. - if "fancy" argument is set to True produces indented output for readability.""" - s = (fancy-1) * 2 * ' ' + "<" + self.name - if self.namespace: - if not self.parent or self.parent.namespace!=self.namespace: - s = s + ' xmlns="%s"'%self.namespace - for key in self.attrs.keys(): - val = ustr(self.attrs[key]) - s = s + ' %s="%s"' % ( key, XMLescape(val) ) - s = s + ">" - cnt = 0 - if self.kids: - if fancy: s = s + "\n" - for a in self.kids: - if not fancy and (len(self.data)-1)>=cnt: s=s+XMLescape(self.data[cnt]) - elif (len(self.data)-1)>=cnt: s=s+XMLescape(self.data[cnt].strip()) - s = s + a.__str__(fancy and fancy+1) - cnt=cnt+1 - if not fancy and (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt]) - elif (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt].strip()) - if not self.kids and s[-1:]=='>': - s=s[:-1]+' />' - if fancy: s = s + "\n" - else: - if fancy and not self.data: s = s + (fancy-1) * 2 * ' ' - s = s + "" - if fancy: s = s + "\n" - return s - def addChild(self, name=None, attrs={}, payload=[], namespace=None, node=None): - """ If "node" argument is provided, adds it as child node. Else creates new node from - the other arguments' values and adds it as well.""" - if namespace: name=namespace+' '+name - if node: - newnode=node - node.parent = self - else: newnode=Node(tag=name, parent=self, attrs=attrs, payload=payload) - self.kids.append(newnode) - return newnode - def addData(self, data): - """ Adds some CDATA to node. """ - self.data.append(ustr(data)) - def clearData(self): - """ Removes all CDATA from the node. """ - self.data=[] - def delAttr(self, key): - """ Deletes an attribute "key" """ - del self.attrs[key] - def delChild(self, node, attrs={}): - """ Deletes the "node" from the node's childs list, if "node" is an instance. - Else deletes the first node that have specified name and (optionally) attributes. """ - if type(node)<>type(self): node=self.getTag(node,attrs) - self.kids.remove(node) - return node - def getAttrs(self): - """ Returns all node's attributes as dictionary. """ - return self.attrs - def getAttr(self, key): - """ Returns value of specified attribute. """ - try: return self.attrs[key] - except: return None - def getChildren(self): - """ Returns all node's child nodes as list. """ - return self.kids - def getData(self): - """ Returns all node CDATA as string (concatenated). """ - return ''.join(self.data) - def getName(self): - """ Returns the name of node """ - return self.name - def getNamespace(self): - """ Returns the namespace of node """ - return self.namespace - def getParent(self): - """ Returns the parent of node (if present). """ - return self.parent - def getPayload(self): - """ Return the payload of node i.e. list of child nodes and CDATA entries. - F.e. for "text1 text2" will be returned list: - ['text1', , , ' text2']. """ - ret=[] - for i in range(len(self.kids)+len(self.data)+1): - try: - if self.data[i]: ret.append(self.data[i]) - except IndexError: pass - try: ret.append(self.kids[i]) - except IndexError: pass - return ret - def getTag(self, name, attrs={}, namespace=None): - """ Filters all child nodes using specified arguments as filter. - Returns the first found or None if not found. """ - return self.getTags(name, attrs, namespace, one=1) - def getTagAttr(self,tag,attr): - """ Returns attribute value of the child with specified name (or None if no such attribute).""" - try: return self.getTag(tag).attrs[attr] - except: return None - def getTagData(self,tag): - """ Returns cocatenated CDATA of the child with specified name.""" - try: return self.getTag(tag).getData() - except: return None - def getTags(self, name, attrs={}, namespace=None, one=0): - """ Filters all child nodes using specified arguments as filter. - Returns the list of nodes found. """ - nodes=[] - for node in self.kids: - if namespace and namespace<>node.getNamespace(): continue - if node.getName() == name: - for key in attrs.keys(): - if not node.attrs.has_key(key) or node.attrs[key]<>attrs[key]: break - else: nodes.append(node) - if one and nodes: return nodes[0] - if not one: return nodes - def setAttr(self, key, val): - """ Sets attribute "key" with the value "val". """ - self.attrs[key]=val - def setData(self, data): - """ Sets node's CDATA to provided string. Resets all previous CDATA!""" - self.data=[ustr(data)] - def setName(self,val): - """ Changes the node name. """ - self.name = val - def setNamespace(self, namespace): - """ Changes the node namespace. """ - self.namespace=namespace - def setParent(self, node): - """ Sets node's parent to "node". WARNING: do not checks if the parent already present - and not removes the node from the list of childs of previous parent. """ - self.parent = node - def setPayload(self,payload,add=0): - """ Sets node payload according to the list specified. WARNING: completely replaces all node's - previous content. If you wish just to add child or CDATA - use addData or addChild methods. """ - if type(payload) in (type(''),type(u'')): payload=[payload] - if add: self.kids+=payload - else: self.kids=payload - def setTag(self, name, attrs={}, namespace=None): - """ Same as getTag but if the node with specified namespace/attributes not found, creates such - node and returns it. """ - node=self.getTags(name, attrs, namespace=namespace, one=1) - if node: return node - else: return self.addChild(name, attrs, namespace=namespace) - def setTagAttr(self,tag,attr,val): - """ Creates new node (if not already present) with name "tag" - and sets it's attribute "attr" to value "val". """ - try: self.getTag(tag).attrs[attr]=val - except: self.addChild(tag,attrs={attr:val}) - def setTagData(self,tag,val,attrs={}): - """ Creates new node (if not already present) with name "tag" and (optionally) attributes "attrs" - and sets it's CDATA to string "val". """ - try: self.getTag(tag,attrs).setData(ustr(val)) - except: self.addChild(tag,attrs,payload=[ustr(val)]) - def has_attr(self,key): - """ Checks if node have attribute "key".""" - return self.attrs.has_key(key) - def __getitem__(self,item): - """ Returns node's attribute "item" value. """ - return self.getAttr(item) - def __setitem__(self,item,val): - """ Sets node's attribute "item" value. """ - return self.setAttr(item,val) - def __delitem__(self,item): - """ Deletes node's attribute "item". """ - return self.delAttr(item,val) - def __getattr__(self,attr): - """ Reduce memory usage caused by T/NT classes - use memory only when needed. """ - if attr=='T': - self.T=T(self) - return self.T - if attr=='NT': - self.NT=NT(self) - return self.NT - raise AttributeError + def __str__(self,fancy=0): + """ Method used to dump node into textual representation. + if "fancy" argument is set to True produces indented output for readability.""" + s = (fancy-1) * 2 * ' ' + "<" + self.name + if self.namespace: + if not self.parent or self.parent.namespace!=self.namespace: + s = s + ' xmlns="%s"'%self.namespace + for key in self.attrs.keys(): + val = ustr(self.attrs[key]) + s = s + ' %s="%s"' % ( key, XMLescape(val) ) + s = s + ">" + cnt = 0 + if self.kids: + if fancy: s = s + "\n" + for a in self.kids: + if not fancy and (len(self.data)-1)>=cnt: s=s+XMLescape(self.data[cnt]) + elif (len(self.data)-1)>=cnt: s=s+XMLescape(self.data[cnt].strip()) + s = s + a.__str__(fancy and fancy+1) + cnt=cnt+1 + if not fancy and (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt]) + elif (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt].strip()) + if not self.kids and s[-1:]=='>': + s=s[:-1]+' />' + if fancy: s = s + "\n" + else: + if fancy and not self.data: s = s + (fancy-1) * 2 * ' ' + s = s + "" + if fancy: s = s + "\n" + return s + def addChild(self, name=None, attrs={}, payload=[], namespace=None, node=None): + """ If "node" argument is provided, adds it as child node. Else creates new node from + the other arguments' values and adds it as well.""" + if namespace: name=namespace+' '+name + if node: + newnode=node + node.parent = self + else: newnode=Node(tag=name, parent=self, attrs=attrs, payload=payload) + self.kids.append(newnode) + return newnode + def addData(self, data): + """ Adds some CDATA to node. """ + self.data.append(ustr(data)) + def clearData(self): + """ Removes all CDATA from the node. """ + self.data=[] + def delAttr(self, key): + """ Deletes an attribute "key" """ + del self.attrs[key] + def delChild(self, node, attrs={}): + """ Deletes the "node" from the node's childs list, if "node" is an instance. + Else deletes the first node that have specified name and (optionally) attributes. """ + if type(node)<>type(self): node=self.getTag(node,attrs) + self.kids.remove(node) + return node + def getAttrs(self): + """ Returns all node's attributes as dictionary. """ + return self.attrs + def getAttr(self, key): + """ Returns value of specified attribute. """ + try: return self.attrs[key] + except: return None + def getChildren(self): + """ Returns all node's child nodes as list. """ + return self.kids + def getData(self): + """ Returns all node CDATA as string (concatenated). """ + return ''.join(self.data) + def getName(self): + """ Returns the name of node """ + return self.name + def getNamespace(self): + """ Returns the namespace of node """ + return self.namespace + def getParent(self): + """ Returns the parent of node (if present). """ + return self.parent + def getPayload(self): + """ Return the payload of node i.e. list of child nodes and CDATA entries. + F.e. for "text1 text2" will be returned list: + ['text1', , , ' text2']. """ + ret=[] + for i in range(len(self.kids)+len(self.data)+1): + try: + if self.data[i]: ret.append(self.data[i]) + except IndexError: pass + try: ret.append(self.kids[i]) + except IndexError: pass + return ret + def getTag(self, name, attrs={}, namespace=None): + """ Filters all child nodes using specified arguments as filter. + Returns the first found or None if not found. """ + return self.getTags(name, attrs, namespace, one=1) + def getTagAttr(self,tag,attr): + """ Returns attribute value of the child with specified name (or None if no such attribute).""" + try: return self.getTag(tag).attrs[attr] + except: return None + def getTagData(self,tag): + """ Returns cocatenated CDATA of the child with specified name.""" + try: return self.getTag(tag).getData() + except: return None + def getTags(self, name, attrs={}, namespace=None, one=0): + """ Filters all child nodes using specified arguments as filter. + Returns the list of nodes found. """ + nodes=[] + for node in self.kids: + if namespace and namespace<>node.getNamespace(): continue + if node.getName() == name: + for key in attrs.keys(): + if not node.attrs.has_key(key) or node.attrs[key]<>attrs[key]: break + else: nodes.append(node) + if one and nodes: return nodes[0] + if not one: return nodes + def setAttr(self, key, val): + """ Sets attribute "key" with the value "val". """ + self.attrs[key]=val + def setData(self, data): + """ Sets node's CDATA to provided string. Resets all previous CDATA!""" + self.data=[ustr(data)] + def setName(self,val): + """ Changes the node name. """ + self.name = val + def setNamespace(self, namespace): + """ Changes the node namespace. """ + self.namespace=namespace + def setParent(self, node): + """ Sets node's parent to "node". WARNING: do not checks if the parent already present + and not removes the node from the list of childs of previous parent. """ + self.parent = node + def setPayload(self,payload,add=0): + """ Sets node payload according to the list specified. WARNING: completely replaces all node's + previous content. If you wish just to add child or CDATA - use addData or addChild methods. """ + if type(payload) in (type(''),type(u'')): payload=[payload] + if add: self.kids+=payload + else: self.kids=payload + def setTag(self, name, attrs={}, namespace=None): + """ Same as getTag but if the node with specified namespace/attributes not found, creates such + node and returns it. """ + node=self.getTags(name, attrs, namespace=namespace, one=1) + if node: return node + else: return self.addChild(name, attrs, namespace=namespace) + def setTagAttr(self,tag,attr,val): + """ Creates new node (if not already present) with name "tag" + and sets it's attribute "attr" to value "val". """ + try: self.getTag(tag).attrs[attr]=val + except: self.addChild(tag,attrs={attr:val}) + def setTagData(self,tag,val,attrs={}): + """ Creates new node (if not already present) with name "tag" and (optionally) attributes "attrs" + and sets it's CDATA to string "val". """ + try: self.getTag(tag,attrs).setData(ustr(val)) + except: self.addChild(tag,attrs,payload=[ustr(val)]) + def has_attr(self,key): + """ Checks if node have attribute "key".""" + return self.attrs.has_key(key) + def __getitem__(self,item): + """ Returns node's attribute "item" value. """ + return self.getAttr(item) + def __setitem__(self,item,val): + """ Sets node's attribute "item" value. """ + return self.setAttr(item,val) + def __delitem__(self,item): + """ Deletes node's attribute "item". """ + return self.delAttr(item,val) + def __getattr__(self,attr): + """ Reduce memory usage caused by T/NT classes - use memory only when needed. """ + if attr=='T': + self.T=T(self) + return self.T + if attr=='NT': + self.NT=NT(self) + return self.NT + raise AttributeError class T: - """ Auxiliary class used to quick access to node's child nodes. """ - def __init__(self,node): self.__dict__['node']=node - def __getattr__(self,attr): return self.node.setTag(attr) - def __setattr__(self,attr,val): - if isinstance(val,Node): Node.__init__(self.node.setTag(attr),node=val) - else: return self.node.setTagData(attr,val) - def __delattr__(self,attr): return self.node.delChild(attr) + """ Auxiliary class used to quick access to node's child nodes. """ + def __init__(self,node): self.__dict__['node']=node + def __getattr__(self,attr): return self.node.setTag(attr) + def __setattr__(self,attr,val): + if isinstance(val,Node): Node.__init__(self.node.setTag(attr),node=val) + else: return self.node.setTagData(attr,val) + def __delattr__(self,attr): return self.node.delChild(attr) class NT(T): - """ Auxiliary class used to quick create node's child nodes. """ - def __getattr__(self,attr): return self.node.addChild(attr) - def __setattr__(self,attr,val): - if isinstance(val,Node): self.node.addChild(attr,node=val) - else: return self.node.addChild(attr,payload=[val]) + """ Auxiliary class used to quick create node's child nodes. """ + def __getattr__(self,attr): return self.node.addChild(attr) + def __setattr__(self,attr,val): + if isinstance(val,Node): self.node.addChild(attr,node=val) + else: return self.node.addChild(attr,payload=[val]) DBG_NODEBUILDER = 'nodebuilder' class NodeBuilder: - """ Builds a Node class minidom from data parsed to it. This class used for two purposes: - 1. Creation an XML Node from a textual representation. F.e. reading a config file. See an XML2Node method. - 2. Handling an incoming XML stream. This is done by mangling - the __dispatch_depth parameter and redefining the dispatch method. - You do not need to use this class directly if you do not designing your own XML handler.""" - def __init__(self,data=None,initial_node=None): - """ Takes two optional parameters: "data" and "initial_node". - By default class initialised with empty Node class instance. - Though, if "initial_node" is provided it used as "starting point". - You can think about it as of "node upgrade". - "data" (if provided) feeded to parser immidiatedly after instance init. - """ - self.DEBUG(DBG_NODEBUILDER, "Preparing to handle incoming XML stream.", 'start') - self._parser = xml.parsers.expat.ParserCreate(namespace_separator=' ') - self._parser.StartElementHandler = self.starttag - self._parser.EndElementHandler = self.endtag - self._parser.CharacterDataHandler = self.handle_data - self._parser.StartNamespaceDeclHandler = self.handle_namespace_start - self.Parse = self._parser.Parse + """ Builds a Node class minidom from data parsed to it. This class used for two purposes: + 1. Creation an XML Node from a textual representation. F.e. reading a config file. See an XML2Node method. + 2. Handling an incoming XML stream. This is done by mangling + the __dispatch_depth parameter and redefining the dispatch method. + You do not need to use this class directly if you do not designing your own XML handler.""" + def __init__(self,data=None,initial_node=None): + """ Takes two optional parameters: "data" and "initial_node". + By default class initialised with empty Node class instance. + Though, if "initial_node" is provided it used as "starting point". + You can think about it as of "node upgrade". + "data" (if provided) feeded to parser immidiatedly after instance init. + """ + self.DEBUG(DBG_NODEBUILDER, "Preparing to handle incoming XML stream.", 'start') + self._parser = xml.parsers.expat.ParserCreate(namespace_separator=' ') + self._parser.StartElementHandler = self.starttag + self._parser.EndElementHandler = self.endtag + self._parser.StartNamespaceDeclHandler = self.handle_namespace_start + self._parser.CharacterDataHandler = self.handle_cdata + self.Parse = self._parser.Parse - self.__depth = 0 - self._dispatch_depth = 1 - self._document_attrs = None - self._mini_dom=initial_node - self.last_is_data = 1 - self._ptr=None - self.namespaces={"http://www.w3.org/XML/1998/namespace":'xml:'} - self.xmlns="http://www.w3.org/XML/1998/namespace" + self.__depth = 0 + self._dispatch_depth = 1 + self._document_attrs = None + self._mini_dom=initial_node + self.last_is_data = 1 + self._ptr=None + self.data_buffer = None + self.namespaces={"http://www.w3.org/XML/1998/namespace":'xml:'} + self.xmlns="http://www.w3.org/XML/1998/namespace" - if data: self._parser.Parse(data,1) + if data: + self._parser.Parse(data,1) + + + def destroy(self): + """ Method used to allow class instance to be garbage-collected. """ + self._parser.StartElementHandler = None + self._parser.EndElementHandler = None + self._parser.CharacterDataHandler = None + self._parser.StartNamespaceDeclHandler = None - def destroy(self): - """ Method used to allow class instance to be garbage-collected. """ - self._parser.StartElementHandler = None - self._parser.EndElementHandler = None - self._parser.CharacterDataHandler = None - self._parser.StartNamespaceDeclHandler = None + def starttag(self, tag, attrs): + """XML Parser callback. Used internally""" + attlist=attrs.keys() # + for attr in attlist: # FIXME: Crude hack. And it also slows down the whole library considerably. + sp=attr.rfind(" ") # + if sp==-1: continue # + ns=attr[:sp] # + attrs[self.namespaces[ns]+attr[sp+1:]]=attrs[attr] + del attrs[attr] # + self.__depth += 1 + self.DEBUG(DBG_NODEBUILDER, "DEPTH -> %i , tag -> %s, attrs -> %s" % (self.__depth, tag, `attrs`), 'down') + if self.__depth == self._dispatch_depth: + if not self._mini_dom : + self._mini_dom = Node(tag=tag, attrs=attrs) + else: + Node.__init__(self._mini_dom,tag=tag, attrs=attrs) + self._ptr = self._mini_dom + elif self.__depth > self._dispatch_depth: + self._ptr.kids.append(Node(tag=tag,parent=self._ptr,attrs=attrs)) + self._ptr = self._ptr.kids[-1] + if self.__depth == 1: + self._document_attrs = attrs + ns, name = (['']+tag.split())[-2:] + self.stream_header_received(ns, name, attrs) + if not self.last_is_data and self._ptr.parent: + self._ptr.parent.data.append('') + self.last_is_data = 0 + def endtag(self, tag ): + """XML Parser callback. Used internally""" + self.DEBUG(DBG_NODEBUILDER, "DEPTH -> %i , tag -> %s" % (self.__depth, tag), 'up') + if self.data_buffer: + self._ptr.data.append(''.join(self.data_buffer)) + self.data_buffer = None + if self.__depth == self._dispatch_depth: + self.dispatch(self._mini_dom) + elif self.__depth > self._dispatch_depth: + self._ptr = self._ptr.parent + else: + self.DEBUG(DBG_NODEBUILDER, "Got higher than dispatch level. Stream terminated?", 'stop') + self.__depth -= 1 + self.last_is_data = 0 + if self.__depth == 0: self.stream_footer_received() + + def handle_cdata(self, data): + if self.last_is_data: + if self.data_buffer: + self.data_buffer.append(data) + else: + self.data_buffer = [data] + self.last_is_data = 1 + + def handle_data(self, data, *args): + """XML Parser callback. Used internally""" + self.DEBUG(DBG_NODEBUILDER, data, 'data') + if not self._ptr: return + if self.last_is_data: + self._ptr.data[-1] += data + else: + self._ptr.data.append(data) + self.last_is_data = 1 - def starttag(self, tag, attrs): - """XML Parser callback. Used internally""" - attlist=attrs.keys() # - for attr in attlist: # FIXME: Crude hack. And it also slows down the whole library considerably. - sp=attr.rfind(" ") # - if sp==-1: continue # - ns=attr[:sp] # - attrs[self.namespaces[ns]+attr[sp+1:]]=attrs[attr] - del attrs[attr] # - self.__depth += 1 - self.DEBUG(DBG_NODEBUILDER, "DEPTH -> %i , tag -> %s, attrs -> %s" % (self.__depth, tag, `attrs`), 'down') - if self.__depth == self._dispatch_depth: - if not self._mini_dom : self._mini_dom = Node(tag=tag, attrs=attrs) - else: Node.__init__(self._mini_dom,tag=tag, attrs=attrs) - self._ptr = self._mini_dom - elif self.__depth > self._dispatch_depth: - self._ptr.kids.append(Node(tag=tag,parent=self._ptr,attrs=attrs)) - self._ptr = self._ptr.kids[-1] - if self.__depth == 1: - self._document_attrs = attrs - ns, name = (['']+tag.split())[-2:] - self.stream_header_received(ns, name, attrs) - if not self.last_is_data and self._ptr.parent: self._ptr.parent.data.append('') - self.last_is_data = 0 - - def endtag(self, tag ): - """XML Parser callback. Used internally""" - self.DEBUG(DBG_NODEBUILDER, "DEPTH -> %i , tag -> %s" % (self.__depth, tag), 'up') - if self.__depth == self._dispatch_depth: - self.dispatch(self._mini_dom) - elif self.__depth > self._dispatch_depth: - self._ptr = self._ptr.parent - else: - self.DEBUG(DBG_NODEBUILDER, "Got higher than dispatch level. Stream terminated?", 'stop') - self.__depth -= 1 - self.last_is_data = 0 - if self.__depth == 0: self.stream_footer_received() - - def handle_data(self, data): - """XML Parser callback. Used internally""" - self.DEBUG(DBG_NODEBUILDER, data, 'data') - if not self._ptr: return - if self.last_is_data: - self._ptr.data[-1] += data - else: - self._ptr.data.append(data) - self.last_is_data = 1 - - def handle_namespace_start(self, prefix, uri): - """XML Parser callback. Used internally""" - if prefix: self.namespaces[uri]=prefix+':' - else: self.xmlns=uri - def DEBUG(self, level, text, comment=None): - """ Gets all NodeBuilder walking events. Can be used for debugging if redefined.""" - def getDom(self): - """ Returns just built Node. """ - return self._mini_dom - def dispatch(self,stanza): - """ Gets called when the NodeBuilder reaches some level of depth on it's way up with the built - node as argument. Can be redefined to convert incoming XML stanzas to program events. """ - def stream_header_received(self,ns,tag,attrs): - """ Method called when stream just opened. """ - def stream_footer_received(self): - """ Method called when stream just closed. """ + def handle_namespace_start(self, prefix, uri): + """XML Parser callback. Used internally""" + if prefix: self.namespaces[uri]=prefix+':' + else: self.xmlns=uri + def DEBUG(self, level, text, comment=None): + """ Gets all NodeBuilder walking events. Can be used for debugging if redefined.""" + def getDom(self): + """ Returns just built Node. """ + return self._mini_dom + def dispatch(self,stanza): + """ Gets called when the NodeBuilder reaches some level of depth on it's way up with the built + node as argument. Can be redefined to convert incoming XML stanzas to program events. """ + def stream_header_received(self,ns,tag,attrs): + """ Method called when stream just opened. """ + def stream_footer_received(self): + """ Method called when stream just closed. """ def XML2Node(xml): - """ Converts supplied textual string into XML node. Handy f.e. for reading configuration file. - Raises xml.parser.expat.parsererror if provided string is not well-formed XML. """ - return NodeBuilder(xml).getDom() + """ Converts supplied textual string into XML node. Handy f.e. for reading configuration file. + Raises xml.parser.expat.parsererror if provided string is not well-formed XML. """ + return NodeBuilder(xml).getDom() def BadXML2Node(xml): - """ Converts supplied textual string into XML node. Survives if xml data is cutted half way round. - I.e. "some text
some more text". Will raise xml.parser.expat.parsererror on misplaced - tags though. F.e. "some text
some more text
" will not work.""" - return NodeBuilder(xml).getDom() + """ Converts supplied textual string into XML node. Survives if xml data is cutted half way round. + I.e. "some text
some more text". Will raise xml.parser.expat.parsererror on misplaced + tags though. F.e. "some text
some more text
" will not work.""" + return NodeBuilder(xml).getDom()