For Python 2.2, do not use __getattr__(), only use computed properties.
This is probably a little bit faster, but mostly is just cleaner code.
The old-style support is still used for Python versions < 2.2 so this
source file can be shared with PyXML.
diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py
index 4bc83a5..8a84d0f 100644
--- a/Lib/xml/dom/minidom.py
+++ b/Lib/xml/dom/minidom.py
@@ -68,33 +68,6 @@
                 #open("debug4.out", "w")
             Node.debug.write("create %s\n" % index)
 
-    def __getattr__(self, key):
-        if key[0:2] == "__":
-            raise AttributeError, key
-        # getattr should never call getattr!
-        if self.__dict__.has_key("inGetAttr"):
-            del self.inGetAttr
-            raise AttributeError, key
-
-        prefix, attrname = key[:5], key[5:]
-        if prefix == "_get_":
-            self.inGetAttr = 1
-            if hasattr(self, attrname):
-                del self.inGetAttr
-                return (lambda self=self, attrname=attrname:
-                                getattr(self, attrname))
-            else:
-                del self.inGetAttr
-                raise AttributeError, key
-        else:
-            self.inGetAttr = 1
-            try:
-                func = getattr(self, "_get_" + key)
-            except AttributeError:
-                raise AttributeError, key
-            del self.inGetAttr
-            return func()
-
     def __nonzero__(self):
         return 1
 
@@ -124,6 +97,41 @@
         if self.childNodes:
             return self.childNodes[-1]
 
+    try:
+        property
+    except NameError:
+        def __getattr__(self, key):
+            if key[0:2] == "__":
+                raise AttributeError, key
+            # getattr should never call getattr!
+            if self.__dict__.has_key("inGetAttr"):
+                del self.inGetAttr
+                raise AttributeError, key
+
+            prefix, attrname = key[:5], key[5:]
+            if prefix == "_get_":
+                self.inGetAttr = 1
+                if hasattr(self, attrname):
+                    del self.inGetAttr
+                    return (lambda self=self, attrname=attrname:
+                                    getattr(self, attrname))
+                else:
+                    del self.inGetAttr
+                    raise AttributeError, key
+            else:
+                self.inGetAttr = 1
+                try:
+                    func = getattr(self, "_get_" + key)
+                except AttributeError:
+                    raise AttributeError, key
+                del self.inGetAttr
+                return func()
+    else:
+        firstChild = property(_get_firstChild,
+                              doc="First child node, or None.")
+        lastChild = property(_get_lastChild,
+                             doc="Last child node, or None.")
+
     def insertBefore(self, newChild, refChild):
         if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
             for c in newChild.childNodes:
@@ -362,10 +370,16 @@
         self._attrs = attrs
         self._attrsNS = attrsNS
 
-    def __getattr__(self, name):
-        if name == "length":
-            return len(self._attrs)
-        raise AttributeError, name
+    try:
+        property
+    except NameError:
+        def __getattr__(self, name):
+            if name == "length":
+                return len(self._attrs)
+            raise AttributeError, name
+    else:
+        length = property(lambda self: len(self._attrs),
+                          doc="Number of nodes in the NamedNodeMap.")
 
     def item(self, index):
         try:
@@ -598,6 +612,14 @@
     def _get_attributes(self):
         return AttributeList(self._attrs, self._attrsNS)
 
+    try:
+        property
+    except NameError:
+        pass
+    else:
+        attributes = property(_get_attributes,
+                              doc="NamedNodeMap of attributes on the element.")
+
     def hasAttributes(self):
         if self._attrs or self._attrsNS:
             return 1
@@ -841,6 +863,14 @@
             if node.nodeType == Node.ELEMENT_NODE:
                 return node
 
+    try:
+        property
+    except NameError:
+        pass
+    else:
+        documentElement = property(_get_documentElement,
+                                   doc="Top-level element of this document.")
+
     def unlink(self):
         if self.doctype is not None:
             self.doctype.unlink()