From 14aa280de23ecb6c31ffbde4a12c1a0a6f5493e0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= <martin@v.loewis.de>
Date: Sun, 19 Feb 2012 20:25:12 +0100
Subject: [PATCH] Use __slots__ throughout instead of __dict__, to reduce the
 memory usage.

---
 Lib/xml/dom/expatbuilder.py |  39 +++----
 Lib/xml/dom/minidom.py      | 216 +++++++++++++++++-------------------
 2 files changed, 114 insertions(+), 141 deletions(-)

diff --git a/Lib/xml/dom/expatbuilder.py b/Lib/xml/dom/expatbuilder.py
index a98fe03235e..b4b28e4d553 100644
--- a/Lib/xml/dom/expatbuilder.py
+++ b/Lib/xml/dom/expatbuilder.py
@@ -283,27 +283,23 @@ class ExpatBuilder:
         elif childNodes and childNodes[-1].nodeType == TEXT_NODE:
             node = childNodes[-1]
             value = node.data + data
-            d = node.__dict__
-            d['data'] = d['nodeValue'] = value
+            node.data = value
             return
         else:
             node = minidom.Text()
-            d = node.__dict__
-            d['data'] = d['nodeValue'] = data
-            d['ownerDocument'] = self.document
+            node.data = data
+            node.ownerDocument = self.document
         _append_child(self.curNode, node)
 
     def character_data_handler(self, data):
         childNodes = self.curNode.childNodes
         if childNodes and childNodes[-1].nodeType == TEXT_NODE:
             node = childNodes[-1]
-            d = node.__dict__
-            d['data'] = d['nodeValue'] = node.data + data
+            node.data = node.data + data
             return
         node = minidom.Text()
-        d = node.__dict__
-        d['data'] = d['nodeValue'] = node.data + data
-        d['ownerDocument'] = self.document
+        node.data = node.data + data
+        node.ownerDocument = self.document
         _append_child(self.curNode, node)
 
     def entity_decl_handler(self, entityName, is_parameter_entity, value,
@@ -363,11 +359,8 @@ class ExpatBuilder:
                 a = minidom.Attr(attributes[i], EMPTY_NAMESPACE,
                                  None, EMPTY_PREFIX)
                 value = attributes[i+1]
-                d = a.childNodes[0].__dict__
-                d['data'] = d['nodeValue'] = value
-                d = a.__dict__
-                d['value'] = d['nodeValue'] = value
-                d['ownerDocument'] = self.document
+                a.value = value
+                a.ownerDocument = self.document
                 _set_attribute_node(node, a)
 
         if node is not self.document.documentElement:
@@ -761,11 +754,8 @@ class Namespaces:
                 else:
                     a = minidom.Attr("xmlns", XMLNS_NAMESPACE,
                                      "xmlns", EMPTY_PREFIX)
-                d = a.childNodes[0].__dict__
-                d['data'] = d['nodeValue'] = uri
-                d = a.__dict__
-                d['value'] = d['nodeValue'] = uri
-                d['ownerDocument'] = self.document
+                a.value = uri
+                a.ownerDocuemnt = self.document
                 _set_attribute_node(node, a)
             del self._ns_ordered_prefixes[:]
 
@@ -785,12 +775,9 @@ class Namespaces:
                                      aname, EMPTY_PREFIX)
                     _attrs[aname] = a
                     _attrsNS[(EMPTY_NAMESPACE, aname)] = a
-                d = a.childNodes[0].__dict__
-                d['data'] = d['nodeValue'] = value
-                d = a.__dict__
-                d['ownerDocument'] = self.document
-                d['value'] = d['nodeValue'] = value
-                d['ownerElement'] = node
+                a.ownerDocument = self.document
+                a.value = value
+                a.ownerElement = node
 
     if __debug__:
         # This only adds some asserts to the original
diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py
index f23ad053333..c7de12c89cd 100644
--- a/Lib/xml/dom/minidom.py
+++ b/Lib/xml/dom/minidom.py
@@ -286,10 +286,10 @@ def _append_child(self, node):
     childNodes = self.childNodes
     if childNodes:
         last = childNodes[-1]
-        node.__dict__["previousSibling"] = last
-        last.__dict__["nextSibling"] = node
+        node.previousSibling = last
+        last.nextSibling = node
     childNodes.append(node)
-    node.__dict__["parentNode"] = self
+    node.parentNode = self
 
 def _in_document(node):
     # return True iff node is part of a document tree
@@ -342,9 +342,10 @@ class DocumentFragment(Node):
 
 
 class Attr(Node):
+    __slots__=('_name', '_value', 'namespaceURI',
+               '_prefix', 'childNodes', '_localName', 'ownerDocument', 'ownerElement')
     nodeType = Node.ATTRIBUTE_NODE
     attributes = None
-    ownerElement = None
     specified = False
     _is_id = False
 
@@ -352,12 +353,11 @@ class Attr(Node):
 
     def __init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None,
                  prefix=None):
-        # skip setattr for performance
-        d = self.__dict__
-        d["nodeName"] = d["name"] = qName
-        d["namespaceURI"] = namespaceURI
-        d["prefix"] = prefix
-        d['childNodes'] = NodeList()
+        self.ownerElement = None
+        self._name = qName
+        self.namespaceURI = namespaceURI
+        self._prefix = prefix
+        self.childNodes = NodeList()
 
         # Add the single child node that represents the value of the attr
         self.childNodes.append(Text())
@@ -365,9 +365,10 @@ class Attr(Node):
         # nodeValue and value are set elsewhere
 
     def _get_localName(self):
-        if 'localName' in self.__dict__:
-            return self.__dict__['localName']
-        return self.nodeName.split(":", 1)[-1]
+        try:
+            return self._localName
+        except AttributeError:
+            return self.nodeName.split(":", 1)[-1]
 
     def _get_name(self):
         return self.name
@@ -375,20 +376,30 @@ class Attr(Node):
     def _get_specified(self):
         return self.specified
 
-    def __setattr__(self, name, value):
-        d = self.__dict__
-        if name in ("value", "nodeValue"):
-            d["value"] = d["nodeValue"] = value
-            d2 = self.childNodes[0].__dict__
-            d2["data"] = d2["nodeValue"] = value
-            if self.ownerElement is not None:
-                _clear_id_cache(self.ownerElement)
-        elif name in ("name", "nodeName"):
-            d["name"] = d["nodeName"] = value
-            if self.ownerElement is not None:
-                _clear_id_cache(self.ownerElement)
-        else:
-            d[name] = value
+    def _get_name(self):
+        return self._name
+
+    def _set_name(self, value):
+        self._name = value
+        if self.ownerElement is not None:
+            _clear_id_cache(self.ownerElement)
+
+    nodeName = name = property(_get_name, _set_name)
+
+    def _get_value(self):
+        return self._value
+
+    def _set_value(self, value):
+        self._value = value
+        self.childNodes[0].data = value
+        if self.ownerElement is not None:
+            _clear_id_cache(self.ownerElement)
+        self.childNodes[0].data = value
+
+    nodeValue = value = property(_get_value, _set_value)
+
+    def _get_prefix(self):
+        return self._prefix
 
     def _set_prefix(self, prefix):
         nsuri = self.namespaceURI
@@ -396,22 +407,16 @@ class Attr(Node):
             if nsuri and nsuri != XMLNS_NAMESPACE:
                 raise xml.dom.NamespaceErr(
                     "illegal use of 'xmlns' prefix for the wrong namespace")
-        d = self.__dict__
-        d['prefix'] = prefix
+        self._prefix = prefix
         if prefix is None:
             newName = self.localName
         else:
             newName = "%s:%s" % (prefix, self.localName)
         if self.ownerElement:
             _clear_id_cache(self.ownerElement)
-        d['nodeName'] = d['name'] = newName
+        self.name = newName
 
-    def _set_value(self, value):
-        d = self.__dict__
-        d['value'] = d['nodeValue'] = value
-        if self.ownerElement:
-            _clear_id_cache(self.ownerElement)
-        self.childNodes[0].data = value
+    prefix = property(_get_prefix, _set_prefix)
 
     def unlink(self):
         # This implementation does not call the base implementation
@@ -586,8 +591,8 @@ class NamedNodeMap(object):
             _clear_id_cache(self._ownerElement)
             del self._attrs[n.nodeName]
             del self._attrsNS[(n.namespaceURI, n.localName)]
-            if 'ownerElement' in n.__dict__:
-                n.__dict__['ownerElement'] = None
+            if hasattr(n, 'ownerElement'):
+                n.ownerElement = None
             return n
         else:
             raise xml.dom.NotFoundErr()
@@ -598,8 +603,8 @@ class NamedNodeMap(object):
             _clear_id_cache(self._ownerElement)
             del self._attrsNS[(n.namespaceURI, n.localName)]
             del self._attrs[n.nodeName]
-            if 'ownerElement' in n.__dict__:
-                n.__dict__['ownerElement'] = None
+            if hasattr(n, 'ownerElement'):
+                n.ownerElement = None
             return n
         else:
             raise xml.dom.NotFoundErr()
@@ -659,6 +664,9 @@ class TypeInfo(object):
 _no_type = TypeInfo(None, None)
 
 class Element(Node):
+    __slots__=('ownerDocument', 'parentNode', 'tagName', 'nodeName', 'prefix',
+               'namespaceURI', '_localName', 'childNodes', '_attrs', '_attrsNS',
+               'nextSibling', 'previousSibling')
     nodeType = Node.ELEMENT_NODE
     nodeValue = None
     schemaType = _no_type
@@ -674,10 +682,12 @@ class Element(Node):
 
     def __init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, prefix=None,
                  localName=None):
+        self.parentNode = None
         self.tagName = self.nodeName = tagName
         self.prefix = prefix
         self.namespaceURI = namespaceURI
         self.childNodes = NodeList()
+        self.nextSibling = self.previousSibling = None
 
         self._attrs = {}   # attributes are double-indexed:
         self._attrsNS = {} #    tagName -> Attribute
@@ -688,9 +698,10 @@ class Element(Node):
                            # namespaces.
 
     def _get_localName(self):
-        if 'localName' in self.__dict__:
-            return self.__dict__['localName']
-        return self.tagName.split(":", 1)[-1]
+        try:
+            return self._localName
+        except AttributeError:
+            return self.tagName.split(":", 1)[-1]
 
     def _get_tagName(self):
         return self.tagName
@@ -718,14 +729,11 @@ class Element(Node):
         attr = self.getAttributeNode(attname)
         if attr is None:
             attr = Attr(attname)
-            # for performance
-            d = attr.__dict__
-            d["value"] = d["nodeValue"] = value
-            d["ownerDocument"] = self.ownerDocument
+            attr.value = value # also sets nodeValue
+            attr.ownerDocument = self.ownerDocument
             self.setAttributeNode(attr)
         elif value != attr.value:
-            d = attr.__dict__
-            d["value"] = d["nodeValue"] = value
+            attr.value = value
             if attr.isId:
                 _clear_id_cache(self)
 
@@ -733,23 +741,18 @@ class Element(Node):
         prefix, localname = _nssplit(qualifiedName)
         attr = self.getAttributeNodeNS(namespaceURI, localname)
         if attr is None:
-            # for performance
             attr = Attr(qualifiedName, namespaceURI, localname, prefix)
-            d = attr.__dict__
-            d["prefix"] = prefix
-            d["nodeName"] = qualifiedName
-            d["value"] = d["nodeValue"] = value
-            d["ownerDocument"] = self.ownerDocument
+            attr.value = value
+            attr.ownerDocument = self.ownerDocument
             self.setAttributeNode(attr)
         else:
-            d = attr.__dict__
             if value != attr.value:
-                d["value"] = d["nodeValue"] = value
+                attr.value = value
                 if attr.isId:
                     _clear_id_cache(self)
             if attr.prefix != prefix:
-                d["prefix"] = prefix
-                d["nodeName"] = qualifiedName
+                attr.prefix = prefix
+                attr.nodeName = qualifiedName
 
     def getAttributeNode(self, attrname):
         return self._attrs.get(attrname)
@@ -874,7 +877,7 @@ class Element(Node):
         if _get_containing_entref(self) is not None:
             raise xml.dom.NoModificationAllowedErr()
         if not idAttr._is_id:
-            idAttr.__dict__['_is_id'] = True
+            idAttr._is_id = True
             self._magic_id_nodes += 1
             self.ownerDocument._magic_id_count += 1
             _clear_id_cache(self)
@@ -893,8 +896,7 @@ def _set_attribute_node(element, attr):
     # This creates a circular reference, but Element.unlink()
     # breaks the cycle since the references to the attribute
     # dictionaries are tossed.
-    attr.__dict__['ownerElement'] = element
-
+    attr.ownerElement = element
 
 class Childless:
     """Mixin that makes childless-ness easy to implement and avoids
@@ -938,54 +940,49 @@ class Childless:
 
 class ProcessingInstruction(Childless, Node):
     nodeType = Node.PROCESSING_INSTRUCTION_NODE
+    __slots__ = ('target', 'data')
 
     def __init__(self, target, data):
-        self.target = self.nodeName = target
-        self.data = self.nodeValue = data
+        self.target = target
+        self.data = data
 
-    def _get_data(self):
+    # nodeValue is an alias for data
+    def _get_nodeValue(self):
         return self.data
-    def _set_data(self, value):
-        d = self.__dict__
-        d['data'] = d['nodeValue'] = value
+    def _set_nodeValue(self, value):
+        self.data = data
+    nodeValue = property(_get_nodeValue, _set_nodeValue)
 
-    def _get_target(self):
+    # nodeName is an alias for target
+    def _get_nodeName(self):
         return self.target
-    def _set_target(self, value):
-        d = self.__dict__
-        d['target'] = d['nodeName'] = value
-
-    def __setattr__(self, name, value):
-        if name == "data" or name == "nodeValue":
-            self.__dict__['data'] = self.__dict__['nodeValue'] = value
-        elif name == "target" or name == "nodeName":
-            self.__dict__['target'] = self.__dict__['nodeName'] = value
-        else:
-            self.__dict__[name] = value
+    def _set_nodeName(self, value):
+        self.target = value
+    nodeName = property(_get_nodeName, _set_nodeName)
 
     def writexml(self, writer, indent="", addindent="", newl=""):
         writer.write("%s<?%s %s?>%s" % (indent,self.target, self.data, newl))
 
 
 class CharacterData(Childless, Node):
+    __slots__=('_data', 'ownerDocument','parentNode', 'previousSibling', 'nextSibling')
+
+    def __init__(self):
+        self.ownerDocument = self.parentNode = None
+        self.previousSibling = self.nextSibling = None
+        self._data = ''
+        Node.__init__(self)
+
     def _get_length(self):
         return len(self.data)
     __len__ = _get_length
 
     def _get_data(self):
-        return self.__dict__['data']
+        return self._data
     def _set_data(self, data):
-        d = self.__dict__
-        d['data'] = d['nodeValue'] = data
+        self._data = data
 
-    _get_nodeValue = _get_data
-    _set_nodeValue = _set_data
-
-    def __setattr__(self, name, value):
-        if name == "data" or name == "nodeValue":
-            self.__dict__['data'] = self.__dict__['nodeValue'] = value
-        else:
-            self.__dict__[name] = value
+    data = nodeValue = property(_get_data, _set_data)
 
     def __repr__(self):
         data = self.data
@@ -1042,11 +1039,6 @@ defproperty(CharacterData, "length", doc="Length of the string data.")
 
 
 class Text(CharacterData):
-    # Make sure we don't add an instance __dict__ if we don't already
-    # have one, at least when that's possible:
-    # XXX this does not work, CharacterData is an old-style class
-    # __slots__ = ()
-
     nodeType = Node.TEXT_NODE
     nodeName = "#text"
     attributes = None
@@ -1112,9 +1104,7 @@ class Text(CharacterData):
             else:
                 break
         if content:
-            d = self.__dict__
-            d['data'] = content
-            d['nodeValue'] = content
+            self.data = content
             return self
         else:
             return None
@@ -1160,7 +1150,8 @@ class Comment(CharacterData):
     nodeName = "#comment"
 
     def __init__(self, data):
-        self.data = self.nodeValue = data
+        CharacterData.__init__(self)
+        self._data = data
 
     def writexml(self, writer, indent="", addindent="", newl=""):
         if "--" in self.data:
@@ -1169,11 +1160,6 @@ class Comment(CharacterData):
 
 
 class CDATASection(Text):
-    # Make sure we don't add an instance __dict__ if we don't already
-    # have one, at least when that's possible:
-    # XXX this does not work, Text is an old-style class
-    # __slots__ = ()
-
     nodeType = Node.CDATA_SECTION_NODE
     nodeName = "#cdata-section"
 
@@ -1504,18 +1490,19 @@ def _clear_id_cache(node):
         node.ownerDocument._id_search_stack= None
 
 class Document(Node, DocumentLS):
+    __slots__ = ('_elem_info', 'doctype',
+                 '_id_search_stack', 'childNodes', '_id_cache')
     _child_node_types = (Node.ELEMENT_NODE, Node.PROCESSING_INSTRUCTION_NODE,
                          Node.COMMENT_NODE, Node.DOCUMENT_TYPE_NODE)
 
+    implementation = DOMImplementation()
     nodeType = Node.DOCUMENT_NODE
     nodeName = "#document"
     nodeValue = None
     attributes = None
-    doctype = None
     parentNode = None
     previousSibling = nextSibling = None
 
-    implementation = DOMImplementation()
 
     # Document attributes from Level 3 (WD 9 April 2002)
 
@@ -1530,6 +1517,7 @@ class Document(Node, DocumentLS):
     _magic_id_count = 0
 
     def __init__(self):
+        self.doctype = None
         self.childNodes = NodeList()
         # mapping of (namespaceURI, localName) -> ElementInfo
         #        and tagName -> ElementInfo
@@ -1815,17 +1803,15 @@ class Document(Node, DocumentLS):
                 element.removeAttributeNode(n)
         else:
             element = None
-        # avoid __setattr__
-        d = n.__dict__
-        d['prefix'] = prefix
-        d['localName'] = localName
-        d['namespaceURI'] = namespaceURI
-        d['nodeName'] = name
+        n.prefix = prefix
+        n._localName = localName
+        n.namespaceURI = namespaceURI
+        n.nodeName = name
         if n.nodeType == Node.ELEMENT_NODE:
-            d['tagName'] = name
+            n.tagName = name
         else:
             # attribute node
-            d['name'] = name
+            n.name = name
             if element is not None:
                 element.setAttributeNode(n)
                 if is_id:
-- 
2.30.9