<commit_before>Index: twisted/web2/dav/element/parser.py
===================================================================
--- twisted/web2/dav/element/parser.py	(revision 19773)
+++ twisted/web2/dav/element/parser.py	(working copy)
@@ -37,7 +37,7 @@
     "WebDAVDocument",
 ]
 
-import StringIO
+import cStringIO as StringIO
 import xml.dom.minidom
 import xml.sax
 
@@ -106,6 +106,26 @@
             "children"   : [],
         }]
 
+        # Keep a cache of the subclasses we create for unknown XML
+        # elements, so that we don't create multiple classes for the
+        # same element; it's fairly typical for elements to appear
+        # multiple times in a document.
+        self.unknownElementClasses = {}
+
+        # New-style classes keep weak references to all subclasses.
+        # As a result, the subclasses we create continue to effect the
+        # footprint of WebDAVUnknownElement even after they are
+        # deallocated, up to the maximum number of subclasses that exist
+        # simultaneously.  The number of such weak references doesn't appear
+        # to decrease.  In order to avoid growing it unneccessarily, create a
+        # subclass for use by this document, subclass the new class instead,
+        # and then delete it when we're done, thereby adding only one weak
+        # reference to WebDAVUnknownElement's list.
+        # http://trac.macosforge.org/projects/calendarserver/ticket/101
+        class UnknownElement (WebDAVUnknownElement):
+            pass
+        self.unknownElementClass = UnknownElement
+
     def endDocument(self):
         top = self.stack[-1]
 
@@ -115,6 +135,8 @@
         assert len(top["children"]) is 1, "Must have exactly one root element, got %d" % len(top["children"])
 
         self.dom = WebDAVDocument(top["children"][0])
+        del(self.unknownElementClasses)
+        del(self.unknownElementClass)
 
     def startElementNS(self, name, qname, attributes):
         attributes_dict = {}
@@ -125,13 +147,16 @@
 
         tag_namespace, tag_name = name
 
-        if (name not in elements_by_tag_name):
-            class UnknownElement (WebDAVUnknownElement):
+        if name in elements_by_tag_name:
+            element_class = elements_by_tag_name[name]
+        elif name in self.unknownElementClasses:
+            element_class = self.unknownElementClasses[name]
+        else:
+            class UnknownElement (self.unknownElementClass):
                 namespace = tag_namespace
                 name      = tag_name
             element_class = UnknownElement
-        else:
-            element_class = elements_by_tag_name[name]
+            self.unknownElementClasses[name] = UnknownElement
 
         self.stack.append({
             "name"       : name,
@@ -158,7 +183,12 @@
         self.stack[-1]["children"].append(element)
 
     def characters(self, content):
-        self.stack[-1]["children"].append(PCDATAElement(content))
+        # Coalesce adjacent PCDATAElements
+        pcdata = PCDATAElement(content)
+        if len(self.stack[-1]["children"]) and isinstance(self.stack[-1]["children"][-1], PCDATAElement):
+            self.stack[-1]["children"][-1] = self.stack[-1]["children"][-1] + pcdata
+        else:
+            self.stack[-1]["children"].append(pcdata)
 
     def ignorableWhitespace(self, whitespace):
         self.characters(self, whitespace)
@@ -194,6 +224,8 @@
             except xml.sax.SAXParseException, e:
                 raise ValueError(e)
 
+            #handler.dom.root_element.validate()
+<commit_msg>Get rid of nested subclasses of WebDAVElement.<commit_after>Index: twisted/web2/dav/element/parser.py
===================================================================
--- twisted/web2/dav/element/parser.py	(revision 19773)
+++ twisted/web2/dav/element/parser.py	(working copy)
@@ -37,7 +37,7 @@
     "WebDAVDocument",
 ]
 
-import StringIO
+import cStringIO as StringIO
 import xml.dom.minidom
 import xml.sax
 
@@ -106,6 +106,12 @@
             "children"   : [],
         }]
 
+        # Keep a cache of the subclasses we create for unknown XML
+        # elements, so that we don't create multiple classes for the
+        # same element; it's fairly typical for elements to appear
+        # multiple times in a document.
+        self.unknownElementClasses = {}
+
     def endDocument(self):
         top = self.stack[-1]
 
@@ -115,6 +121,7 @@
         assert len(top["children"]) is 1, "Must have exactly one root element, got %d" % len(top["children"])
 
         self.dom = WebDAVDocument(top["children"][0])
+        del(self.unknownElementClasses)
 
     def startElementNS(self, name, qname, attributes):
         attributes_dict = {}
@@ -125,13 +132,17 @@
 
         tag_namespace, tag_name = name
 
-        if (name not in elements_by_tag_name):
-            class UnknownElement (WebDAVUnknownElement):
-                namespace = tag_namespace
-                name      = tag_name
-            element_class = UnknownElement
+        if name in elements_by_tag_name:
+            element_class = elements_by_tag_name[name]
+        elif name in self.unknownElementClasses:
+            element_class = self.unknownElementClasses[name]
         else:
-            element_class = elements_by_tag_name[name]
+            def element_class(*args, **kwargs):
+                element = WebDAVUnknownElement(*args, **kwargs)
+                element.namespace = tag_namespace
+                element.name      = tag_name
+                return element
+            self.unknownElementClasses[name] = element_class
 
         self.stack.append({
             "name"       : name,
@@ -158,7 +169,12 @@
         self.stack[-1]["children"].append(element)
 
     def characters(self, content):
-        self.stack[-1]["children"].append(PCDATAElement(content))
+        # Coalesce adjacent PCDATAElements
+        pcdata = PCDATAElement(content)
+        if len(self.stack[-1]["children"]) and isinstance(self.stack[-1]["children"][-1], PCDATAElement):
+            self.stack[-1]["children"][-1] = self.stack[-1]["children"][-1] + pcdata
+        else:
+            self.stack[-1]["children"].append(pcdata)
 
     def ignorableWhitespace(self, whitespace):
         self.characters(self, whitespace)
@@ -194,6 +210,8 @@
             except xml.sax.SAXParseException, e:
                 raise ValueError(e)
 
+            #handler.dom.root_element.validate()
+
{'fraction_non_alphanumeric': 0.1372183372183372, 'fraction_numerical': 0.022533022533022532, 'mean_word_length': 2.4808004326663062, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2366286', 'n_tokens_mistral': 1961, 'n_tokens_neox': 1812, 'n_words': 437}