views:

861

answers:

4

I'm trying to use the dnspython library, and am a little confused by their example for querying MX records on this page: www.dnspython.org/examples.html:

import dns.resolver

answers = dns.resolver.query('dnspython.org', 'MX')
for rdata in answers:
    print 'Host', rdata.exchange, 'has preference', rdata.preference

In the python CLI, a dir(answers) gives me:

['__class__', '__delattr__', '__delitem__', '__delslice__', '__dict__', '__doc__', '__getattr__', '__getattribute__', '__getitem__', '__getslice__', '__hash__', '__init__', '__iter__', '__len__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'expiration', 'qname', 'rdclass', 'rdtype', 'response', 'rrset']

Two things are confusing to me (which are related):

  • Iteration over the answers object. What is rdata in the example?
  • None of the attributes or methods of answers matches exchange or preference. Clearly rdata is not just a simple alias of answers, but I don't understand where those attributes are coming from.
  • +1  A: 

    In the example code, answers is an iterable object containing zero or more items, which are each assigned to rdata in turn. To see the properties of the individual responses, try:

    dir(answers[0])
    
    Ben Blank
    +1  A: 

    answers is an iterable as indicated by its "__iter__" method. Think of answers as a list of rdatas.

    You can try doing this to get 1 rdata from answers:

    answers.__iter__().next()
    
    Unknown
    A: 

    If you're on Python 2.6, the "proper" way to get the first item of any iterable (such as answers here) is next(iter(answers)); if you want to avoid an exception when answers is an empty iterable, then next(iter(answers), somevalue) will return somevalue instead of raising StopIteration. If you're on 2.5, then iter(answers).next(), but you'll have to use it inside a try/except StopIteration: statement if you need to deal with a possible empty iterable.

    Alex Martelli
    A: 

    I haven't looked at dns.resolver as of yet - I just added it to the ever-growing list of things to check out. I would guess that rdata refers to the resource record type specific data as described in Section 4.1.3 of RFC1035. The response of a DNS request contains three data sections in addition to the query and headers:

    1. Answers
    2. Authoritative Name Server records
    3. Additional Resource records

    From the looks of it dns.resolver.query() is returning the first section. In this case, each resource record in the answer section is going to have different attributes based on the record type. In this case, you asked for MX records so the records should have exactly the attributes that you have - exchange and preference. These are described in Section 3.3.9 of RFC1035.

    I suspect that dns.resolver is overriding __getattr__ or something similar to perform the magic that you are seeing so you won't see the fields directly in a dir(). Chances are that you are safe using the attributes as defined in RFC1035. I will definitely have to check this out tomorrow since I have need of a decent DNS subsystem for Python.

    Thanks for mentioning this module and have fun with DNS. It is really pretty interesting stuff if you really dig into how it works. I still think that it is one of the earlier expressions of that ReSTful thing that is all the rage these days ;)

    D.Shawley
    Ah, you're right. Looking at the code, I found this comment in the Answer class: For convenience, the answer object implements much of the sequence protocol, forwarding to its rrset. E.g. "for a in answer" is equivalent to "for a in answer.rrset", "answer[i]" is equivalent to "answer.rrset[i]", and "answer[i:j]" is equivalent to "answer.rrset[i:j]".
    victor