[Zope-Checkins] CVS: Packages/webdav - xmltools.py:1.15.2.1
Andreas Jung
andreas at andreas-jung.com
Sun Oct 31 13:51:01 EST 2004
Update of /cvs-repository/Packages/webdav
In directory cvs.zope.org:/tmp/cvs-serv32435/lib/python/webdav
Modified Files:
Tag: Zope-2_7-branch
xmltools.py
Log Message:
- Collector #1443: Applied patch by Simon Eisenmann that reimplements
the XML parser used in WebDAV fixing a memory leak.
=== Packages/webdav/xmltools.py 1.15 => 1.15.2.1 ===
--- Packages/webdav/xmltools.py:1.15 Tue Apr 8 14:48:22 2003
+++ Packages/webdav/xmltools.py Sun Oct 31 13:51:01 2004
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
@@ -11,348 +11,94 @@
#
##############################################################################
-"""WebDAV XML parsing tools. Note that this module does just
- enough for the purposes of DAV - it is not intended as a
- general xml toolkit, and will probably eventually go away
- in favor of a standard xml package once some issues are
- worked out."""
+
+"""
+WebDAV XML request parsing tool using xml.minidom as xml parser.
+Code contributed by Simon Eisenmann, struktur AG, Stuttgart, Germany
+"""
__version__='$Revision$'[11:-2]
-import sys, os
-import Shared.DC.xml.xmllib
-from Acquisition import Implicit
-
-
-type_document=0
-type_element=1
-type_attribute=2
-type_text=3
-type_cdata=4
-type_entityref=5
-type_entity=6
-type_procinst=7
-type_comment=8
-type_notation=9
-
-
-class Node(Implicit):
- """Common base class for Node objects."""
- __name__=''
- __value__=''
- __attrs__=[]
- __nodes__=[]
- __nskey__=''
-
- def name(self): return self.__name__
- def attrs(self): return self.__attrs__
- def value(self): return self.__value__
- def nodes(self): return self.__nodes__
- def nskey(self): return self.__nskey__
+"""
+TODO:
- def addNode(self, node):
- self.__nodes__.append(node.__of__(self))
+ - Check the methods Node.addNode, Node.remap and Node del_attr
+ and find out if some code uses/requires these methods.
- def namespace(self):
- nskey=self.__nskey__
- while 1:
- if hasattr(self, '__nsdef__'):
- val=self.__nsdef__.get(nskey, None)
- if val is not None: return val
- if not hasattr(self, 'aq_parent'):
- return ''
- self=self.aq_parent
-
- def elements(self, name=None, ns=None ):
- nodes=[]
- name=name and name.lower()
- for node in self.__nodes__:
- if node.__type__==type_element and \
- ((name is None) or ((node.__name__.lower())==name)) and \
- ((ns is None) or (node.namespace()==ns)):
- nodes.append(node)
+ => If yes implement them, else forget them.
+
+ NOTE: So far i didn't have any problems.
+ If you have problems please report them.
+
+"""
+
+from xml.dom import minidom
+
+class Node:
+ """ our nodes no matter what type """
+
+ node = None
+
+ def __init__(self, node):
+ self.node=node
+
+ def elements(self, name=None, ns=None):
+ nodes=[ Node(n) for n in self.node.childNodes if n.nodeType == n.ELEMENT_NODE and \
+ ((name is None) or ((n.localName.lower())==name)) and \
+ ((ns is None) or (n.namespaceURI==ns)) ]
return nodes
- def __getitem__(self, n):
- return self.__nodes__[n]
-
def qname(self):
- ns=self.__nskey__
- if ns: ns='%s:' % ns
- return '%s%s' % (ns, self.__name__)
+ return '%s%s' % (self.namespace(), self.name())
+
+ def addNode(self, node):
+ # XXX: no support for adding nodes here
+ raise NotImplementedError, 'addNode not implemented'
def toxml(self):
- return self.__value__
-
+ return self.node.toxml()
+
def strval(self):
return self.toxml()
-
-
-class Document(Node):
- def __init__(self, encoding='utf-8', stdalone=''):
- self.__name__ ='document'
- self.__nodes__=[]
- self.encoding=encoding
- self.stdalone=stdalone
- self.document=self
-
- def toxml(self):
- result=['<?xml version="1.0" encoding="%s"?>' % self.encoding]
- for node in self.__nodes__:
- result.append(node.toxml())
- return ''.join(result)
-
-class Element(Node):
- __type__=type_element
-
- def __init__(self, name, attrs={}):
- self.__name__ =name
- self.__attrs__=[]
- self.__nodes__=[]
- self.__nsdef__={}
- self.__nskey__=''
- for name, val in attrs.items():
- attr=Attribute(name, val)
- self.__attrs__.append(attr)
- self.ns_parse()
- parts=self.__name__.split(':')
- if len(parts) > 1:
- self.__nskey__=parts[0]
- self.__name__=':'.join(parts[1:])
-
- def ns_parse(self):
- nsdef=self.__nsdef__={}
- for attr in self.attrs():
- name, val=attr.name(), attr.value()
- key=name.lower()
- if key[:6]=='xmlns:':
- nsdef[name[6:]]=val
- elif key=='xmlns':
- nsdef['']=val
-
- def fixup(self):
- self.__attrs__=map(lambda n, s=self: n.__of__(s), self.__attrs__)
-
- def get_attr(self, name, ns=None, default=None):
- for attr in self.__attrs__:
- if attr.name()==name and (ns is None) or (ns==attr.namespace()):
- return attr
- return default
-
+
+ def name(self): return self.node.localName
+ def attrs(self): return self.node.attributes
+ def value(self): return self.node.nodeValue
+ def nodes(self): return self.node.childNodes
+ def nskey(self): return self.node.namespaceURI
+
+ def namespace(self): return self.nskey()
+
def del_attr(self, name):
- attrs=[]
- for attr in self.__attrs__:
- if attr.name() != name:
- attrs.append(attr)
- self.__attrs__=attrs
-
+ # XXX: no support for removing attributes
+ # zope can calls this after remapping to remove namespace
+ # haven't seen this happening though
+ return None
+
def remap(self, dict, n=0, top=1):
- # The remap method effectively rewrites an element and all of its
- # children, consolidating namespace declarations into the element
- # on which the remap function is called and fixing up namespace
- # lookup structures.
- nsval=self.namespace()
- if not nsval: nsid=''
- elif not dict.has_key(nsval):
- nsid='ns%d' % n
- dict[nsval]=nsid
- n=n+1
- else: nsid=dict[nsval]
- for attr in self.__attrs__:
- dict, n=attr.remap(dict, n, 0)
- for node in self.elements():
- dict, n=node.remap(dict, n, 0)
- attrs=[]
- for attr in self.__attrs__:
- name=attr.__name__
- if not (((len(name) >= 6) and (name[:6]=='xmlns:')) or \
- name=='xmlns'):
- attrs.append(attr)
- self.__attrs__=attrs
- self.__nsdef__={}
- self.__nskey__=nsid
- if top:
- attrs=self.__attrs__
- keys=dict.keys()
- keys.sort()
- for key in keys:
- attr=Attribute('xmlns:%s' % dict[key], key)
- attrs.append(attr.__of__(self))
- self.__attrs__=attrs
- self.ns_parse()
- return dict, n
-
- def toxml(self):
- qname=self.qname()
- result=['<%s' % qname]
- for attr in self.__attrs__:
- result.append(attr.toxml())
- if not self.__value__ and not self.__nodes__:
- result.append('/>')
- else:
- result.append('>')
- for node in self.__nodes__:
- result.append(node.toxml())
- result.append('</%s>' % qname)
- return ''.join(result)
-
- def strval(self, top=1):
- if not self.__value__ and not self.__nodes__:
- return ''
- result=map(lambda n: n.toxml(), self.__nodes__)
- return ''.join(result)
-
-class Attribute(Node):
- __type__=type_attribute
- def __init__(self, name, val):
- self.__name__=name
- self.__value__=val
- self.__nskey__=''
- parts=name.split(':')
- if len(parts) > 1:
- pre=parts[0].lower()
- if not (pre in ('xml', 'xmlns')):
- self.__nskey__=parts[0]
- self.__name__=':'.join(parts[1:])
-
- def remap(self, dict, n=0, top=1):
- nsval=self.namespace()
- if not nsval: nsid=''
- elif not dict.has_key(nsval):
- nsid='ns%d' % n
- dict[nsval]=nsid
- n=n+1
- else: nsid=dict[nsval]
- self.__nskey__=nsid
- return dict, n
-
- def toxml(self):
- ns=self.__nskey__
- if ns: ns='%s:' % ns
- return ' %s%s="%s"' % (ns, self.__name__, self.__value__)
-
-class Text(Node):
- __name__='#text'
- __type__=type_text
- def __init__(self, val):
- self.__value__=val
- def toxml(self):
- return escape(self.__value__)
-
-class CData(Node):
- __type__=type_cdata
- __name__='#cdata'
- def __init__(self, val):
- self.__value__=val
- def toxml(self):
- return '<![CDATA[%s]]>' % self.__value__
-
-class EntityRef(Node):
- __name__='#entityref'
- __type__=type_entityref
- def __init__(self, val):
- self.__value__=val
- def toxml(self):
- return '&%s;' % self.__value__
-
-class Entity(Node):
- __name__='#entity'
- __type__=type_entity
- def __init__(self, name, pubid, sysid, nname):
- self.__value__=val
- def toxml(self):
- return ''
-
-class ProcInst(Node):
- __type__=type_procinst
- def __init__(self, name, val):
- self.__name__=name
- self.__value__=val
- def toxml(self):
- return '<?%s %s?>' % (self.__name__, self.__value__)
-
-class Comment(Node):
- __name__='#comment'
- __type__=type_comment
- def __init__(self, val):
- self.__value__=val
- def toxml(self):
- return '<!--%s-->' % self.__value__
-
+ # XXX: this method is used to do some strange remapping of elements
+ # and namespaces .. not sure how to do this with minidom
+ # and if this is even required for something
+ # zope calls this to change namespaces in PropPatch and Lock
+ return {},0
+
+ def __repr__(self):
+ if self.namespace():
+ return "<Node %s (from %s)>" % (self.name(), self.namespace())
+ else: return "<Node %s>" % self.name()
+
+
+class XmlParser:
+ """ simple wrapper around minidom to support the required
+ interfaces for zope.webdav
+ """
-
-
-
-class XmlParser(Shared.DC.xml.xmllib.XMLParser):
+ dom = None
+
def __init__(self):
- Shared.DC.xml.xmllib.XMLParser.__init__(self)
- self.root=None
- self.node=None
-
- def parse(self, data):
- # prepending a XML preample to make xmllib happy
- # (Collector #863)
- if not data.startswith("<?xml"):
- data = '<?xml version="1.0" ?>\n' + data
- self.feed(data)
- self.close()
- return self.root
-
- def add(self, node):
- self.node.addNode(node)
-
- def push(self, node):
- self.node.addNode(node)
- self.node=self.node.__nodes__[-1]
-
- def pop(self):
- self.node=self.node.aq_parent
-
- def unknown_starttag(self, name, attrs):
- node=Element(name, attrs)
- self.push(node)
- # Fixup aq chain!
- self.node.fixup()
-
- def unknown_endtag(self, name):
- self.pop()
-
- def handle_xml(self, encoding, stdalone):
- self.root=Document(encoding, stdalone)
- self.node=self.root
-
- def handle_doctype(self, tag, pubid, syslit, data):
pass
-
- def handle_entity(self, name, strval, pubid, syslit, ndata):
- self.add(Entity(name, strval, pubid, syslit, ndata))
-
- def handle_cdata(self, data):
- self.add(CData(data))
-
- def handle_proc(self, name, data):
- self.add(ProcInst(name, data))
-
- def handle_comment(self, data):
- self.add(Comment(data))
-
- def handle_data(self, data):
- self.add(Text(data))
-
- def unknown_entityref(self, data):
- self.add(EntityRef(data))
-
-
-
-def escape(data, rmap={}):
- data=data.replace( "&", "&")
- data=data.replace( "<", "<")
- data=data.replace( ">", ">")
- for key, val in rmap.items():
- data=data.replace( key, val)
- return data
-
-def remap(data, dict={'DAV:': 'd'}):
- root=XmlParser().parse(data)
- root.elements()[0].remap(dict, 0)
- return root.toxml()
+
+ def parse(self, data):
+ self.dom=minidom.parseString(data)
+ return Node(self.dom)
+
More information about the Zope-Checkins
mailing list