Connecting Tech Pros Worldwide Forums | Help | Site Map

Minidom.Node doesn't call __getattr__

Newbie
 
Join Date: Mar 2008
Posts: 5
#1: Mar 7 '08
I'm using minidom to parse XML and to simplify accessing child nodes, I'm trying to implement __getattr__ for the Node class. Yes, I know what most of you will think of that; I'll just have to be careful, won't I. ;)

Expand|Select|Wrap|Line Numbers
  1. import xml
  2. from xml.dom.minidom import parseString
  3.  
  4. def getChild(self, name):
  5.     print "Getting child"
  6.     return self.getElementsByTagName(name)[0]
  7.  
  8. xml.dom.minidom.Node.__getattr__ = getChild
  9.  
  10. xmlDoc = parseString('''<?xml version="1.0" encoding="ISO-8859-1"?>
  11. <CATALOG>
  12.   <cd>
  13.     <title>Empire Burlesque</title>
  14.     <ARTIST>Bob Dylan</ARTIST>
  15.     <COUNTRY>USA</COUNTRY>
  16.     <COMPANY>Columbia</COMPANY>
  17.     <PRICE>10.90</PRICE>
  18.     <YEAR>1985</YEAR>
  19.   </cd>
  20.   <cd>
  21.     <title>Hide your heart</title>
  22.     <ARTIST>Bonnie Tylor</ARTIST>
  23.     <COUNTRY>UK</COUNTRY>
  24.     <COMPANY>CBS Records</COMPANY>
  25.     <PRICE>9.90</PRICE>
  26.     <YEAR>1988</YEAR>
  27.   </cd>
  28. </CATALOG>''')
  29.  
  30. xmlRoot = xmlDoc.childNodes[0]
  31. print xmlRoot.__getattr__
  32. print xmlRoot.cd.title.childNodes[0].nodeValue
outputs:
Expand|Select|Wrap|Line Numbers
  1. <bound method Element.getChild of <DOM Element: CATALOG at 0x1c50850>>
  2.   File "C:\Users\Tuomas\Projektit\wrestlevania\ezXML.py", line 31, in <module>
  3.     print xmlRoot.cd.title.childNodes[0].nodeValue
  4. AttributeError: Element instance has no attribute 'cd'
As far as I can tell, everything works fine, except __getattr__ never gets called. Can anyone help me understand why that is or how to work around it?

jlm699's Avatar
Needs Regular Fix
 
Join Date: Jul 2007
Location: Durham, NC
Posts: 313
#2: Mar 7 '08

re: Minidom.Node doesn't call __getattr__


If you're trying to call __getattr__ you need ().
Newbie
 
Join Date: Mar 2008
Posts: 5
#3: Mar 7 '08

re: Minidom.Node doesn't call __getattr__


Quote:

Originally Posted by jlm699

If you're trying to call __getattr__ you need ().

Not trying to call it - I'm overriding it.
jlm699's Avatar
Needs Regular Fix
 
Join Date: Jul 2007
Location: Durham, NC
Posts: 313
#4: Mar 7 '08

re: Minidom.Node doesn't call __getattr__


Quote:

Originally Posted by ArseAssassin

Not trying to call it - I'm overriding it.

No I know that... but you still need the (name) arguments that getChild needs
Newbie
 
Join Date: Mar 2008
Posts: 5
#5: Mar 7 '08

re: Minidom.Node doesn't call __getattr__


Quote:

Originally Posted by jlm699

No I know that... but you still need the (name) arguments that getChild needs

I'm not sure what you're getting at. getChild is just an implementation of the special method __getattr__, which should be called when I'm trying to access attributes that aren't defined.
jlm699's Avatar
Needs Regular Fix
 
Join Date: Jul 2007
Location: Durham, NC
Posts: 313
#6: Mar 7 '08

re: Minidom.Node doesn't call __getattr__


Quote:

Originally Posted by ArseAssassin

I'm not sure what you're getting at. getChild is just an implementation of the special method __getattr__, which should be called when I'm trying to access attributes that aren't defined.

But aren't you over-riding __getattr__ with getChild ? The way that your code is setup, instead of __getattr__ being called, it calls for getChild... I don't know, maybe I'm misunderstanding what you're trying to accomplish in this code... Where does the .cd. method come from since it's not an attribute of the class that you are trying to call it from?
bvdet's Avatar
Moderator
 
Join Date: Oct 2006
Location: Nashville, TN
Posts: 1,563
#7: Mar 7 '08

re: Minidom.Node doesn't call __getattr__


Quote:

Originally Posted by ArseAssassin

I'm using minidom to parse XML and to simplify accessing child nodes, I'm trying to implement __getattr__ for the Node class. Yes, I know what most of you will think of that; I'll just have to be careful, won't I. ;)

Expand|Select|Wrap|Line Numbers
  1. import xml
  2. from xml.dom.minidom import parseString
  3.  
  4. def getChild(self, name):
  5.     print "Getting child"
  6.     return self.getElementsByTagName(name)[0]
  7.  
  8. xml.dom.minidom.Node.__getattr__ = getChild
  9.  
  10. xmlDoc = parseString('''<?xml version="1.0" encoding="ISO-8859-1"?>
  11. <CATALOG>
  12.   <cd>
  13.     <title>Empire Burlesque</title>
  14.     <ARTIST>Bob Dylan</ARTIST>
  15.     <COUNTRY>USA</COUNTRY>
  16.     <COMPANY>Columbia</COMPANY>
  17.     <PRICE>10.90</PRICE>
  18.     <YEAR>1985</YEAR>
  19.   </cd>
  20.   <cd>
  21.     <title>Hide your heart</title>
  22.     <ARTIST>Bonnie Tylor</ARTIST>
  23.     <COUNTRY>UK</COUNTRY>
  24.     <COMPANY>CBS Records</COMPANY>
  25.     <PRICE>9.90</PRICE>
  26.     <YEAR>1988</YEAR>
  27.   </cd>
  28. </CATALOG>''')
  29.  
  30. xmlRoot = xmlDoc.childNodes[0]
  31. print xmlRoot.__getattr__
  32. print xmlRoot.cd.title.childNodes[0].nodeValue
outputs:
Expand|Select|Wrap|Line Numbers
  1. <bound method Element.getChild of <DOM Element: CATALOG at 0x1c50850>>
  2.   File "C:\Users\Tuomas\Projektit\wrestlevania\ezXML.py", line 31, in <module>
  3.     print xmlRoot.cd.title.childNodes[0].nodeValue
  4. AttributeError: Element instance has no attribute 'cd'
As far as I can tell, everything works fine, except __getattr__ never gets called. Can anyone help me understand why that is or how to work around it?

Modify getChild() slightly:
Expand|Select|Wrap|Line Numbers
  1. def getChild(self, name):
  2.     print "Getting child"
  3.     return self.getElementsByTagName(name)
Modify the calls:
Expand|Select|Wrap|Line Numbers
  1. xmlRoot = xmlDoc.childNodes[0]
  2. print xmlRoot.__getattr__('cd')
  3. print xmlRoot.__getattr__('cd')[0].__getattr__('title')[0].firstChild.nodeValue
  4. print xmlRoot.__getattr__('ARTIST')[0].firstChild.nodeValue
Output:
Expand|Select|Wrap|Line Numbers
  1. >>> Getting child
  2. [<DOM Element: cd at 0x100d4b8>, <DOM Element: cd at 0x100d788>]
  3. Getting child
  4. Getting child
  5. Empire Burlesque
  6. Getting child
  7. Bob Dylan
  8. >>> 
jlm699's Avatar
Needs Regular Fix
 
Join Date: Jul 2007
Location: Durham, NC
Posts: 313
#8: Mar 7 '08

re: Minidom.Node doesn't call __getattr__


Quote:

Originally Posted by bvdet

Modify the calls:

Expand|Select|Wrap|Line Numbers
  1. print xmlRoot.__getattr__('cd')
  2.  

That's what I was trying to say by using the (name) argument
bvdet's Avatar
Moderator
 
Join Date: Oct 2006
Location: Nashville, TN
Posts: 1,563
#9: Mar 7 '08

re: Minidom.Node doesn't call __getattr__


Quote:

Originally Posted by jlm699

That's what I was trying to say by using the (name) argument

And you were correct!
jlm699's Avatar
Needs Regular Fix
 
Join Date: Jul 2007
Location: Durham, NC
Posts: 313
#10: Mar 7 '08

re: Minidom.Node doesn't call __getattr__


Quote:

Originally Posted by bvdet

Expand|Select|Wrap|Line Numbers
  1. xmlRoot = xmlDoc.childNodes[0]
  2. print xmlRoot.__getattr__('cd')
  3. print xmlRoot.__getattr__('cd')[0].__getattr__('title')[0].firstChild.nodeValue
  4. print xmlRoot.__getattr__('ARTIST')[0].firstChild.nodeValue
Output:
Expand|Select|Wrap|Line Numbers
  1. >>> Getting child
  2. [<DOM Element: cd at 0x100d4b8>, <DOM Element: cd at 0x100d788>]
  3. Getting child
  4. Getting child
  5. Empire Burlesque
  6. Getting child
  7. Bob Dylan
  8. >>> 

In my calls I needed to modify it a little bit...
Expand|Select|Wrap|Line Numbers
  1. >>> xmlRoot.__getattr__('cd').__getattr__('title').firstChild.nodeValue
  2. Getting child
  3. Getting child
  4. u'Empire Burlesque'
  5. >>> xmlRoot.__getattr__('ARTIST').firstChild.nodeValue
  6. Getting child
  7. u'Bob Dylan'
  8. >>> 
  9.  
bvdet's Avatar
Moderator
 
Join Date: Oct 2006
Location: Nashville, TN
Posts: 1,563
#11: Mar 7 '08

re: Minidom.Node doesn't call __getattr__


Quote:

Originally Posted by jlm699

In my calls I needed to modify it a little bit...

Expand|Select|Wrap|Line Numbers
  1. >>> xmlRoot.__getattr__('cd').__getattr__('title').firstChild.nodeValue
  2. Getting child
  3. Getting child
  4. u'Empire Burlesque'
  5. >>> xmlRoot.__getattr__('ARTIST').firstChild.nodeValue
  6. Getting child
  7. u'Bob Dylan'
  8. >>> 
  9.  

I had modified function getChild() to return the NodeList object. Otherwise you could not access the other elements in the object.
Expand|Select|Wrap|Line Numbers
  1. >>> for elem in xmlRoot.__getattr__('cd'):
  2. ...     print elem
  3. ...     
  4. Getting child
  5. <DOM Element: cd at 0xf854b8>
  6. <DOM Element: cd at 0xf8b490>
  7. >>> 
Newbie
 
Join Date: Mar 2008
Posts: 5
#12: Mar 8 '08

re: Minidom.Node doesn't call __getattr__


Quote:

Originally Posted by jlm699

In my calls I needed to modify it a little bit...

Expand|Select|Wrap|Line Numbers
  1. >>> xmlRoot.__getattr__('cd').__getattr__('title').firstChild.nodeValue
  2. Getting child
  3. Getting child
  4. u'Empire Burlesque'
  5. >>> xmlRoot.__getattr__('ARTIST').firstChild.nodeValue
  6. Getting child
  7. u'Bob Dylan'
  8. >>> 
  9.  

Well, obviously that'll work. Here's what I'm going for:
http://pyref.infogami.com/__getattr__
Quote:
Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self).
With any custom class I define, it will work:
Expand|Select|Wrap|Line Numbers
  1. >>> class example:
  2. ...     def __init__(self):
  3. ...         print 'Initiated'
  4. ...         
  5. >>> a = example()
  6. Initiated
  7. >>> def getAttribute(self, name):
  8. ...     print name, 'was not found'
  9. ...     
  10. >>> example.__getattr__ = getAttribute
  11. >>> print a.unknownAttribute
  12. unknownAttribute was not found
I'm having trouble understanding why it won't work for minidom nodes.
Newbie
 
Join Date: Mar 2008
Posts: 5
#13: Mar 8 '08

re: Minidom.Node doesn't call __getattr__


I was finally able to figure it out: since the nodes I'm accessing are actually instances of the Element class and __getattr__ isn't part of its parent's (Node class) original class definition, the new instances aren't, I suppose, aware of the method's existence. It's definitely inherited since I can access it; the instances just won't know they're supposed to call it. That's what I figure anyway. Here's the working code:
Expand|Select|Wrap|Line Numbers
  1. import xml
  2. from xml.dom.minidom import parseString
  3.  
  4. def getChild(self, name):
  5.     print "Getting child"
  6.     return self.getElementsByTagName(name)[0]
  7.  
  8. xml.dom.minidom.Element.__getattr__ = getChild
  9.  
  10. xmlDoc = parseString('''<?xml version="1.0" encoding="ISO-8859-1"?>
  11. <CATALOG>
  12.   <cd>
  13.     <title>Empire Burlesque</title>
  14.     <ARTIST>Bob Dylan</ARTIST>
  15.     <COUNTRY>USA</COUNTRY>
  16.     <COMPANY>Columbia</COMPANY>
  17.     <PRICE>10.90</PRICE>
  18.     <YEAR>1985</YEAR>
  19.   </cd>
  20.   <cd>
  21.     <title>Hide your heart</title>
  22.     <ARTIST>Bonnie Tylor</ARTIST>
  23.     <COUNTRY>UK</COUNTRY>
  24.     <COMPANY>CBS Records</COMPANY>
  25.     <PRICE>9.90</PRICE>
  26.     <YEAR>1988</YEAR>
  27.   </cd>
  28. </CATALOG>''')
  29.  
  30. xmlRoot = xmlDoc.childNodes[0]
  31. print xmlRoot.__getattr__
  32. print xmlRoot.cd.title.childNodes[0].nodeValue
Thanks for the help, guys. :)
bvdet's Avatar
Moderator
 
Join Date: Oct 2006
Location: Nashville, TN
Posts: 1,563
#14: Mar 9 '08

re: Minidom.Node doesn't call __getattr__


Quote:

Originally Posted by ArseAssassin

I was finally able to figure it out: since the nodes I'm accessing are actually instances of the Element class and __getattr__ isn't part of its parent's (Node class) original class definition, the new instances aren't, I suppose, aware of the method's existence. It's definitely inherited since I can access it; the instances just won't know they're supposed to call it. That's what I figure anyway. Here's the working code:

Expand|Select|Wrap|Line Numbers
  1. import xml
  2. from xml.dom.minidom import parseString
  3.  
  4. def getChild(self, name):
  5.     print "Getting child"
  6.     return self.getElementsByTagName(name)[0]
  7.  
  8. xml.dom.minidom.Element.__getattr__ = getChild
  9.  
  10. xmlDoc = parseString('''<?xml version="1.0" encoding="ISO-8859-1"?>
  11. <CATALOG>
  12.   <cd>
  13.     <title>Empire Burlesque</title>
  14.     <ARTIST>Bob Dylan</ARTIST>
  15.     <COUNTRY>USA</COUNTRY>
  16.     <COMPANY>Columbia</COMPANY>
  17.     <PRICE>10.90</PRICE>
  18.     <YEAR>1985</YEAR>
  19.   </cd>
  20.   <cd>
  21.     <title>Hide your heart</title>
  22.     <ARTIST>Bonnie Tylor</ARTIST>
  23.     <COUNTRY>UK</COUNTRY>
  24.     <COMPANY>CBS Records</COMPANY>
  25.     <PRICE>9.90</PRICE>
  26.     <YEAR>1988</YEAR>
  27.   </cd>
  28. </CATALOG>''')
  29.  
  30. xmlRoot = xmlDoc.childNodes[0]
  31. print xmlRoot.__getattr__
  32. print xmlRoot.cd.title.childNodes[0].nodeValue
Thanks for the help, guys. :)

You are welcome, and thank you for sharing what you have found out.
Reply