r47227 - Merge t-names-authority-py3-8259-3: Port twisted.names.authority to Python 3

hawkowl-TA+aISz0psMTMxyoc4vAAJOcrHinNvQL0E9HWUfgJXw@public.gmane.org Thu, 14 Apr 2016 04:52:22 -0600 (MDT)
Newsgroups gmane.comp.python.twisted.commits
Message-ID <[email protected]>
Author: hawkowl
Date: Thu Apr 14 04:52:07 2016
New Revision: 47227

Added:
   trunk/twisted/names/topfiles/8259.feature
Modified:
   trunk/twisted/names/authority.py
   trunk/twisted/names/dns.py
   trunk/twisted/names/secondary.py
   trunk/twisted/names/test/test_examples.py
   trunk/twisted/names/test/test_names.py
   trunk/twisted/python/dist3.py

Log:
Merge t-names-authority-py3-8259-3: Port twisted.names.authority to Python 3

Author: pawelmhm, hawkowl
Reviewers: adiroiban, hawkowl, glyph
Fixes: #8259

Modified: trunk/twisted/names/authority.py
==============================================================================
--- trunk/twisted/names/authority.py	(original)
+++ trunk/twisted/names/authority.py	Thu Apr 14 04:52:07 2016
@@ -6,15 +6,16 @@
 Authoritative resolvers.
 """
 
+from __future__ import absolute_import, division
+
 import os
 import time
 
-from twisted.names import dns, error
+from twisted.names import dns, error, common
 from twisted.internet import defer
 from twisted.python import failure
 from twisted.python.compat import execfile
 
-import common
 
 def getSerial(filename = '/tmp/twisted-names.serial'):
     """Return a monotonically increasing (across program runs) integer.
@@ -24,25 +25,21 @@
     """
     serial = time.strftime('%Y%m%d')
 
-    o = os.umask(0177)
+    o = os.umask(0o177)
     try:
         if not os.path.exists(filename):
-            f = file(filename, 'w')
-            f.write(serial + ' 0')
-            f.close()
+            with open(filename, 'w') as f:
+                f.write(serial + ' 0')
     finally:
         os.umask(o)
 
-    serialFile = file(filename, 'r')
-    lastSerial, ID = serialFile.readline().split()
+    with open(filename, 'r') as serialFile:
+        lastSerial, ID = serialFile.readline().split()
+
     ID = (lastSerial == serial) and (int(ID) + 1) or 0
-    serialFile.close()
-    serialFile = file(filename, 'w')
-    serialFile.write('%s %d' % (serial, ID))
-    serialFile.close()
-    serial = serial + ('%02d' % (ID,))
-    return serial
 
+    with open(filename, 'w') as serialFile:
+        serialFile.write('%s %d' % (serial, ID))
 
 #class LookupCacherMixin(object):
 #    _cache = None
@@ -58,6 +55,8 @@
 #            r = self._meth(name, cls, type, timeout)
 #            self._cache[(name, cls, type)] = r
 #            return r
+    serial = serial + ('%02d' % (ID,))
+    return serial
 
 
 
@@ -67,8 +66,12 @@
 
     @ivar _ADDITIONAL_PROCESSING_TYPES: Record types for which additional
         processing will be done.
+
     @ivar _ADDRESS_TYPES: Record types which are useful for inclusion in the
         additional section generated during additional processing.
+
+    @ivar soa: A 2-tuple containing the SOA domain name as a L{bytes} and a
+        L{dns.Record_SOA}.
     """
     # See https://twistedmatrix.com/trac/ticket/6650
     _ADDITIONAL_PROCESSING_TYPES = (dns.CNAME, dns.MX, dns.NS)
@@ -244,7 +247,7 @@
         g, l = self.setupConfigNamespace(), {}
         execfile(filename, g, l)
         if not l.has_key('zone'):
-            raise ValueError, "No zone defined in " + filename
+            raise ValueError("No zone defined in " + filename)
 
         self.records = {}
         for rr in l['zone']:
@@ -272,7 +275,9 @@
 
     def loadFile(self, filename):
         self.origin = os.path.basename(filename) + '.' # XXX - this might suck
-        lines = open(filename).readlines()
+
+        with open(filename, 'rb') as f:
+            lines = f.readlines()
         lines = self.stripComments(lines)
         lines = self.collapseContinuations(lines)
         self.parseLines(lines)
@@ -337,7 +342,7 @@
         if f:
             f(ttl, type, domain, rdata)
         else:
-            raise NotImplementedError, "Record class %r not supported" % cls
+            raise NotImplementedError("Record class %r not supported" % cls)
 
 
     def class_IN(self, ttl, type, domain, rdata):
@@ -347,11 +352,11 @@
             r.ttl = ttl
             self.records.setdefault(domain.lower(), []).append(r)
 
-            print 'Adding IN Record', domain, ttl, r
+            print('Adding IN Record', domain, ttl, r)
             if type == 'SOA':
                 self.soa = (domain, r)
         else:
-            raise NotImplementedError, "Record type %r not supported" % type
+            raise NotImplementedError("Record type %r not supported" % type)
 
 
     #

Modified: trunk/twisted/names/dns.py
==============================================================================
--- trunk/twisted/names/dns.py	(original)
+++ trunk/twisted/names/dns.py	Thu Apr 14 04:52:07 2016
@@ -69,8 +69,8 @@
         Construct a bytes object representing a single byte with the given
         ordinal value.
 
-        @type ordinal: C{int}
-        @rtype: C{bytes}
+        @type ordinal: L{int}
+        @rtype: L{bytes}
         """
         return bytes([ordinal])
 
@@ -81,7 +81,7 @@
         to an end user.
 
         @param bytes: The bytes to represent.
-        @rtype: C{str}
+        @rtype: L{str}
         """
         return repr(bytes)[1:]
 
@@ -92,7 +92,7 @@
         presentation to an end user.
 
         @param list: The list of bytes to represent.
-        @rtype: C{str}
+        @rtype: L{str}
         """
         return '[%s]' % (
             ', '.join([_nicebytes(b) for b in list]),)
@@ -212,7 +212,7 @@
     """
     Split a domain name into its constituent labels.
 
-    @type name: C{str}
+    @type name: L{bytes}
     @param name: A fully qualified domain name (with or without a
         trailing dot).
 
@@ -244,10 +244,10 @@
     C{descendantName} is considered a I{subdomain} if its sequence of
     labels ends with the labels of C{ancestorName}.
 
-    @type descendantName: C{bytes}
+    @type descendantName: L{bytes}
     @param descendantName: The DNS subdomain name.
 
-    @type ancestorName: C{bytes}
+    @type ancestorName: L{bytes}
     @param ancestorName: The DNS parent or ancestor domain name.
 
     @return: C{True} if C{descendantName} is equal to or if it is a
@@ -270,15 +270,19 @@
         (years).  For example: C{"3S"} indicates an interval of three seconds;
         C{"5D"} indicates an interval of five days.  Alternatively, C{s} may be
         any non-string and it will be returned unmodified.
-    @type s: text string (C{str}) for parsing; anything else for passthrough.
+    @type s: text string (L{bytes} or L{unicode}) for parsing; anything else
+        for passthrough.
 
-    @return: an C{int} giving the interval represented by the string C{s}, or
+    @return: an L{int} giving the interval represented by the string C{s}, or
         whatever C{s} is if it is not a string.
     """
     suffixes = (
         ('S', 1), ('M', 60), ('H', 60 * 60), ('D', 60 * 60 * 24),
         ('W', 60 * 60 * 24 * 7), ('Y', 60 * 60 * 24 * 365)
     )
+    if _PY3 and isinstance(s, bytes):
+        s = s.decode('ascii')
+
     if isinstance(s, str):
         s = s.upper().strip()
         for (suff, mult) in suffixes:
@@ -326,7 +330,7 @@
         @type strio: File-like object
         @param strio: The stream from which bytes may be read
 
-        @type length: C{int} or C{None}
+        @type length: L{int} or L{None}
         @param length: The number of bytes in this RDATA field.  Most
         implementations can ignore this value.  Only in the case of
         records similar to TXT where the total length is in no way
@@ -405,9 +409,13 @@
     I{twistedmatrix.com}.
 
     @ivar name: A byte string giving the name.
-    @type name: C{bytes}
+    @type name: L{bytes}
     """
     def __init__(self, name=b''):
+        """
+        @param name: A name.
+        @type name: L{unicode} or L{bytes}
+        """
         if isinstance(name, unicode):
             name = name.encode('idna')
         if not isinstance(name, bytes):
@@ -520,8 +528,13 @@
     Represent a single DNS query.
 
     @ivar name: The name about which this query is requesting information.
+    @type name: L{Name}
+
     @ivar type: The query type.
+    @type type: L{int}
+
     @ivar cls: The query class.
+    @type cls: L{int}
     """
     name = None
     type = None
@@ -529,13 +542,13 @@
 
     def __init__(self, name=b'', type=A, cls=IN):
         """
-        @type name: C{bytes}
-        @param name: The name about which to request information.
+        @type name: L{bytes} or L{unicode}
+        @param name: See L{Query.name}
 
-        @type type: C{int}
+        @type type: L{int}
         @param type: The query type.
 
-        @type cls: C{int}
+        @type cls: L{int}
         @param cls: The query class.
         """
         self.name = Name(name)
@@ -823,16 +836,23 @@
     """
     A resource record header.
 
-    @cvar fmt: C{str} specifying the byte format of an RR.
+    @cvar fmt: L{str} specifying the byte format of an RR.
 
     @ivar name: The name about which this reply contains information.
+    @type name: L{Name}
+
     @ivar type: The query type of the original request.
+    @type type: L{int}
+
     @ivar cls: The query class of the original request.
+
     @ivar ttl: The time-to-live for this record.
-    @ivar payload: An object that implements the IEncodable interface
+    @type ttl: L{int}
+
+    @ivar payload: An object that implements the L{IEncodable} interface
 
-    @ivar auth: A C{bool} indicating whether this C{RRHeader} was parsed from an
-        authoritative message.
+    @ivar auth: A L{bool} indicating whether this C{RRHeader} was parsed from
+        an authoritative message.
     """
     compareAttributes = ('name', 'type', 'cls', 'ttl', 'payload', 'auth')
 
@@ -847,18 +867,19 @@
 
     cachedResponse = None
 
-    def __init__(self, name=b'', type=A, cls=IN, ttl=0, payload=None, auth=False):
+    def __init__(self, name=b'', type=A, cls=IN, ttl=0, payload=None,
+                 auth=False):
         """
-        @type name: C{bytes}
-        @param name: The name about which this reply contains information.
+        @type name: L{bytes} or L{unicode}
+        @param name: See L{RRHeader.name}
 
-        @type type: C{int}
+        @type type: L{int}
         @param type: The query type.
 
-        @type cls: C{int}
+        @type cls: L{int}
         @param cls: The query class.
 
-        @type ttl: C{int}
+        @type ttl: L{int}
         @param ttl: Time to live for this record.
 
         @type payload: An object implementing C{IEncodable}
@@ -921,7 +942,7 @@
     @type name: L{Name}
     @ivar name: The name associated with this record.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
     """
@@ -932,6 +953,10 @@
     name = None
 
     def __init__(self, name=b'', ttl=None):
+        """
+        @param name: See L{SimpleRecord.name}
+        @type name: L{bytes} or L{unicode}
+        """
         self.name = Name(name)
         self.ttl = str2time(ttl)
 
@@ -1057,11 +1082,11 @@
     """
     An IPv4 host address.
 
-    @type address: C{str}
+    @type address: L{bytes}
     @ivar address: The packed network-order representation of the IPv4 address
         associated with this record.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
     """
@@ -1071,6 +1096,14 @@
     address = None
 
     def __init__(self, address='0.0.0.0', ttl=None):
+        """
+        @type address: L{bytes} or L{unicode}
+        @param address: The IPv4 address associated with this record, in
+            quad-dotted notation.
+        """
+        if _PY3 and isinstance(address, bytes):
+            address = address.decode('idna')
+
         address = socket.inet_aton(address)
         self.address = address
         self.ttl = str2time(ttl)
@@ -1114,28 +1147,28 @@
     @ivar rname: A domain-name which specifies the mailbox of the person
         responsible for this zone.
 
-    @type serial: C{int}
+    @type serial: L{int}
     @ivar serial: The unsigned 32 bit version number of the original copy of
         the zone.  Zone transfers preserve this value.  This value wraps and
         should be compared using sequence space arithmetic.
 
-    @type refresh: C{int}
+    @type refresh: L{int}
     @ivar refresh: A 32 bit time interval before the zone should be refreshed.
 
-    @type minimum: C{int}
+    @type minimum: L{int}
     @ivar minimum: The unsigned 32 bit minimum TTL field that should be
         exported with any RR from this zone.
 
-    @type expire: C{int}
+    @type expire: L{int}
     @ivar expire: A 32 bit time value that specifies the upper limit on the
         time interval that can elapse before the zone is no longer
         authoritative.
 
-    @type retry: C{int}
+    @type retry: L{int}
     @ivar retry: A 32 bit time interval that should elapse before a failed
         refresh should be retried.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The default TTL to use for records served from this zone.
     """
     fancybasename = 'SOA'
@@ -1146,6 +1179,13 @@
 
     def __init__(self, mname=b'', rname=b'', serial=0, refresh=0, retry=0,
                  expire=0, minimum=0, ttl=None):
+        """
+        @param mname: See L{Record_SOA.mname}
+        @type mname: L{bytes} or L{unicode}
+
+        @param rname: See L{Record_SOA.rname}
+        @type rname: L{bytes} or L{unicode}
+        """
         self.mname, self.rname = Name(mname), Name(rname)
         self.serial, self.refresh = str2time(serial), str2time(refresh)
         self.minimum, self.expire = str2time(minimum), str2time(expire)
@@ -1188,7 +1228,7 @@
 
     This is an experimental record type.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
     """
@@ -1223,19 +1263,19 @@
 
     This record type is obsolete.  See L{Record_SRV}.
 
-    @type address: C{str}
+    @type address: L{bytes}
     @ivar address: The packed network-order representation of the IPv4 address
         associated with this record.
 
-    @type protocol: C{int}
+    @type protocol: L{int}
     @ivar protocol: The 8 bit IP protocol number for which this service map is
         relevant.
 
-    @type map: C{str}
+    @type map: L{bytes}
     @ivar map: A bitvector indicating the services available at the specified
         address.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
     """
@@ -1247,7 +1287,15 @@
 
     _address = property(lambda self: socket.inet_ntoa(self.address))
 
-    def __init__(self, address='0.0.0.0', protocol=0, map='', ttl=None):
+    def __init__(self, address='0.0.0.0', protocol=0, map=b'', ttl=None):
+        """
+        @type address: L{bytes} or L{unicode}
+        @param address: The IPv4 address associated with this record, in
+            quad-dotted notation.
+        """
+        if _PY3 and isinstance(address, bytes):
+            address = address.decode('idna')
+
         self.address = socket.inet_aton(address)
         self.protocol, self.map = protocol, map
         self.ttl = str2time(ttl)
@@ -1275,11 +1323,11 @@
     """
     An IPv6 host address.
 
-    @type address: C{str}
+    @type address: L{bytes}
     @ivar address: The packed network-order representation of the IPv6 address
         associated with this record.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
 
@@ -1294,6 +1342,13 @@
     _address = property(lambda self: socket.inet_ntop(AF_INET6, self.address))
 
     def __init__(self, address='::', ttl=None):
+        """
+        @type address: L{bytes} or L{unicode}
+        @param address: The IPv6 address for this host, in RFC 2373 format.
+        """
+        if _PY3 and isinstance(address, bytes):
+            address = address.decode('idna')
+
         self.address = socket.inet_pton(AF_INET6, address)
         self.ttl = str2time(ttl)
 
@@ -1318,20 +1373,20 @@
 
     This is an experimental record type.
 
-    @type prefixLen: C{int}
+    @type prefixLen: L{int}
     @ivar prefixLen: The length of the suffix.
 
-    @type suffix: C{str}
+    @type suffix: L{bytes}
     @ivar suffix: An IPv6 address suffix in network order.
 
     @type prefix: L{Name}
     @ivar prefix: If specified, a name which will be used as a prefix for other
         A6 records.
 
-    @type bytes: C{int}
+    @type bytes: L{int}
     @ivar bytes: The length of the prefix.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
 
@@ -1348,6 +1403,16 @@
     _suffix = property(lambda self: socket.inet_ntop(AF_INET6, self.suffix))
 
     def __init__(self, prefixLen=0, suffix='::', prefix=b'', ttl=None):
+        """
+        @param suffix: An IPv6 address suffix in in RFC 2373 format.
+        @type suffix: L{bytes} or L{unicode}
+
+        @param prefix: An IPv6 address prefix for other A6 records.
+        @type prefix: L{bytes} or L{unicode}
+        """
+        if _PY3 and isinstance(suffix, bytes):
+            suffix = suffix.decode('idna')
+
         self.prefixLen = prefixLen
         self.suffix = socket.inet_pton(AF_INET6, suffix)
         self.prefix = Name(prefix)
@@ -1402,18 +1467,18 @@
 
     This is an experimental record type.
 
-    @type priority: C{int}
+    @type priority: L{int}
     @ivar priority: The priority of this target host.  A client MUST attempt to
         contact the target host with the lowest-numbered priority it can reach;
         target hosts with the same priority SHOULD be tried in an order defined
         by the weight field.
 
-    @type weight: C{int}
+    @type weight: L{int}
     @ivar weight: Specifies a relative weight for entries with the same
         priority. Larger weights SHOULD be given a proportionately higher
         probability of being selected.
 
-    @type port: C{int}
+    @type port: L{int}
     @ivar port: The port on this target host of this service.
 
     @type target: L{Name}
@@ -1424,7 +1489,7 @@
         section.  Unless and until permitted by future standards action, name
         compression is not to be used for this field.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
 
@@ -1437,6 +1502,10 @@
     showAttributes = ('priority', 'weight', ('target', 'target', '%s'), 'port', 'ttl')
 
     def __init__(self, priority=0, weight=0, port=0, target=b'', ttl=None):
+        """
+        @param target: See L{Record_SRV.target}
+        @type target: L{bytes} or L{unicode}
+        """
         self.priority = int(priority)
         self.weight = int(weight)
         self.port = int(port)
@@ -1467,12 +1536,12 @@
     """
     The location of the server(s) for a specific protocol and domain.
 
-    @type order: C{int}
+    @type order: L{int}
     @ivar order: An integer specifying the order in which the NAPTR records
         MUST be processed to ensure the correct ordering of rules.  Low numbers
         are processed before high numbers.
 
-    @type preference: C{int}
+    @type preference: L{int}
     @ivar preference: An integer that specifies the order in which NAPTR
         records with equal "order" values SHOULD be processed, low numbers
         being processed before high numbers.
@@ -1501,7 +1570,7 @@
         records depending on the value of the flags field.  This MUST be a
         fully qualified domain-name.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
 
@@ -1517,8 +1586,12 @@
                       ('service', 'service', '%s'), ('regexp', 'regexp', '%s'),
                       ('replacement', 'replacement', '%s'), 'ttl')
 
-    def __init__(self, order=0, preference=0, flags=b'', service=b'', regexp=b'',
-                 replacement=b'', ttl=None):
+    def __init__(self, order=0, preference=0, flags=b'', service=b'',
+                 regexp=b'', replacement=b'', ttl=None):
+        """
+        @param replacement: See L{Record_NAPTR.replacement}
+        @type replacement: L{bytes} or L{unicode}
+        """
         self.order = int(order)
         self.preference = int(preference)
         self.flags = Charstr(flags)
@@ -1562,7 +1635,7 @@
     """
     Map from a domain name to the name of an AFS cell database server.
 
-    @type subtype: C{int}
+    @type subtype: L{int}
     @ivar subtype: In the case of subtype 1, the host has an AFS version 3.0
         Volume Location Server for the named AFS cell.  In the case of subtype
         2, the host has an authenticated name server holding the cell-root
@@ -1572,7 +1645,7 @@
     @ivar hostname: The domain name of a host that has a server for the cell
         named by this record.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
 
@@ -1585,6 +1658,10 @@
     showAttributes = ('subtype', ('hostname', 'hostname', '%s'), 'ttl')
 
     def __init__(self, subtype=0, hostname=b'', ttl=None):
+        """
+        @param hostname: See L{Record_AFSDB.hostname}
+        @type hostname: L{bytes} or L{unicode}
+        """
         self.subtype = int(subtype)
         self.hostname = Name(hostname)
         self.ttl = str2time(ttl)
@@ -1619,7 +1696,7 @@
     @ivar txt: A domain name for which TXT RR's exist (indirection through
         which allows information sharing about the contents of this RP record).
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
 
@@ -1632,6 +1709,13 @@
     showAttributes = (('mbox', 'mbox', '%s'), ('txt', 'txt', '%s'), 'ttl')
 
     def __init__(self, mbox=b'', txt=b'', ttl=None):
+        """
+        @param mbox: See L{Record_RP.mbox}.
+        @type mbox: L{bytes} or L{unicode}
+
+        @param txt: See L{Record_RP.txt}
+        @type txt: L{bytes} or L{unicode}
+        """
         self.mbox = Name(mbox)
         self.txt = Name(txt)
         self.ttl = str2time(ttl)
@@ -1659,13 +1743,13 @@
     """
     Host information.
 
-    @type cpu: C{str}
+    @type cpu: L{bytes}
     @ivar cpu: Specifies the CPU type.
 
-    @type os: C{str}
+    @type os: L{bytes}
     @ivar os: Specifies the OS.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
     """
@@ -1675,7 +1759,7 @@
     showAttributes = (('cpu', _nicebytes), ('os', _nicebytes), 'ttl')
     compareAttributes = ('cpu', 'os', 'ttl')
 
-    def __init__(self, cpu='', os='', ttl=None):
+    def __init__(self, cpu=b'', os=b'', ttl=None):
         self.cpu, self.os = cpu, os
         self.ttl = str2time(ttl)
 
@@ -1723,7 +1807,7 @@
         owner of the MINFO record.  If this domain name names the root, errors
         should be returned to the sender of the message.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
     """
@@ -1739,6 +1823,13 @@
                       'ttl')
 
     def __init__(self, rmailbx=b'', emailbx=b'', ttl=None):
+        """
+        @param rmailbx: See L{Record_MINFO.rmailbx}.
+        @type rmailbx: L{bytes} or L{unicode}
+
+        @param emailbx: See L{Record_MINFO.rmailbx}.
+        @type emailbx: L{bytes} or L{unicode}
+        """
         self.rmailbx, self.emailbx = Name(rmailbx), Name(emailbx)
         self.ttl = str2time(ttl)
 
@@ -1764,7 +1855,7 @@
     """
     Mail exchange.
 
-    @type preference: C{int}
+    @type preference: L{int}
     @ivar preference: Specifies the preference given to this RR among others at
         the same owner.  Lower values are preferred.
 
@@ -1772,7 +1863,7 @@
     @ivar name: A domain-name which specifies a host willing to act as a mail
         exchange.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be
         cached.
     """
@@ -1783,7 +1874,12 @@
     showAttributes = ('preference', ('name', 'name', '%s'), 'ttl')
 
     def __init__(self, preference=0, name=b'', ttl=None, **kwargs):
-        self.preference, self.name = int(preference), Name(kwargs.get('exchange', name))
+        """
+        @param name: See L{Record_MX.name}.
+        @type name: L{bytes} or L{unicode}
+        """
+        self.preference = int(preference)
+        self.name = Name(kwargs.get('exchange', name))
         self.ttl = str2time(ttl)
 
     def encode(self, strio, compDict = None):
@@ -1806,10 +1902,10 @@
     """
     Freeform text.
 
-    @type data: C{list} of C{bytes}
+    @type data: L{list} of L{bytes}
     @ivar data: Freeform text which makes up this record.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be cached.
     """
     TYPE = TXT
@@ -1855,10 +1951,10 @@
     Encapsulate the wire data for unknown record types so that they can
     pass through the system unchanged.
 
-    @type data: C{bytes}
+    @type data: L{bytes}
     @ivar data: Wire data which makes up this record.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be cached.
 
     @since: 11.1
@@ -1900,10 +1996,10 @@
     Structurally, freeform text. Semantically, a policy definition, formatted
     as defined in U{rfc 4408<http://www.faqs.org/rfcs/rfc4408.html>}.
 
-    @type data: C{list} of C{str}
+    @type data: L{list} of L{bytes}
     @ivar data: Freeform text which makes up this record.
 
-    @type ttl: C{int}
+    @type ttl: L{int}
     @ivar ttl: The maximum number of seconds which this record should be cached.
     """
     TYPE = SPF
@@ -2158,13 +2254,13 @@
         """
         Add another query to this Message.
 
-        @type name: C{bytes}
+        @type name: L{bytes}
         @param name: The name to query.
 
-        @type type: C{int}
+        @type type: L{int}
         @param type: Query type
 
-        @type cls: C{int}
+        @type cls: L{int}
         @param cls: Query class
         """
         self.queries.append(Query(name, type, cls))
@@ -2272,9 +2368,9 @@
         Retrieve the L{IRecord} implementation for the given record type.
 
         @param type: A record type, such as L{A} or L{NS}.
-        @type type: C{int}
+        @type type: L{int}
 
-        @return: An object which implements L{IRecord} or C{None} if none
+        @return: An object which implements L{IRecord} or L{None} if none
             can be found for the given type.
         @rtype: L{types.ClassType}
         """
@@ -2286,7 +2382,7 @@
         Encode this L{Message} into a byte string in the format described by RFC
         1035.
 
-        @rtype: C{bytes}
+        @rtype: L{bytes}
         """
         strio = BytesIO()
         self.encode(strio)
@@ -2413,7 +2509,7 @@
 
         @param dnssecOK: DNSSEC OK bit as defined by
             U{RFC3225 3<https://tools.ietf.org/html/rfc3225#section-3>}.
-        @type dnssecOK: C{bool}
+        @type dnssecOK: L{bool}
 
         @param authenticData: A flag indicating in a response that all the data
             included in the answer and authority portion of the response has
@@ -2656,13 +2752,13 @@
         """
         Send out a message with the given queries.
 
-        @type queries: C{list} of C{Query} instances
+        @type queries: L{list} of C{Query} instances
         @param queries: The queries to transmit
 
-        @type timeout: C{int} or C{float}
+        @type timeout: L{int} or C{float}
         @param timeout: How long to wait before giving up
 
-        @type id: C{int}
+        @type id: L{int}
         @param id: Unique key for this request
 
         @type writeMessage: C{callable}
@@ -2776,10 +2872,10 @@
         """
         Send out a message with the given queries.
 
-        @type address: C{tuple} of C{str} and C{int}
+        @type address: L{tuple} of L{str} and L{int}
         @param address: The address to which to send the query
 
-        @type queries: C{list} of C{Query} instances
+        @type queries: L{list} of C{Query} instances
         @param queries: The queries to transmit
 
         @rtype: C{Deferred}
@@ -2870,7 +2966,7 @@
         """
         Send out a message with the given queries.
 
-        @type queries: C{list} of C{Query} instances
+        @type queries: L{list} of C{Query} instances
         @param queries: The queries to transmit
 
         @rtype: C{Deferred}

Modified: trunk/twisted/names/secondary.py
==============================================================================
--- trunk/twisted/names/secondary.py	(original)
+++ trunk/twisted/names/secondary.py	Thu Apr 14 04:52:07 2016
@@ -2,6 +2,8 @@
 # Copyright (c) Twisted Matrix Laboratories.
 # See LICENSE for details.
 
+from __future__ import absolute_import, division
+
 __all__ = ['SecondaryAuthority', 'SecondaryAuthorityService']
 
 from twisted.internet import task, defer
@@ -23,9 +25,11 @@
         """
         @param primary: The IP address of the server from which to perform
         zone transfers.
+        @type primary: L{str}
 
         @param domains: A sequence of domain names for which to perform
         zone transfers.
+        @type domains: L{list} of L{bytes}
         """
         self.primary = primary
         self.domains = [SecondaryAuthority(primary, d) for d in domains]
@@ -43,7 +47,7 @@
             C{int} giving a port number.  Together, these define where zone
             transfers will be attempted from.
 
-        @param domain: A C{str} giving the domain to transfer.
+        @param domain: A C{bytes} giving the domain to transfer.
 
         @return: A new instance of L{SecondaryAuthorityService}.
         """
@@ -98,6 +102,11 @@
     _reactor = None
 
     def __init__(self, primaryIP, domain):
+        """
+        @param domain: The domain for which this will be the secondary
+            authority.
+        @type domain: L{bytes}
+        """
         # Yep.  Skip over FileAuthority.__init__.  This is a hack until we have
         # a good composition-based API for the complicated DNS record lookup
         # logic we want to share.
@@ -110,7 +119,7 @@
     def fromServerAddressAndDomain(cls, serverAddress, domain):
         """
         Construct a new L{SecondaryAuthority} from a tuple giving a server
-        address and a C{str} giving the name of a domain for which this is an
+        address and a C{bytes} giving the name of a domain for which this is an
         authority.
 
         @param serverAddress: A two-tuple, the first element of which is a
@@ -118,7 +127,7 @@
             C{int} giving a port number.  Together, these define where zone
             transfers will be attempted from.
 
-        @param domain: A C{str} giving the domain to transfer.
+        @param domain: A C{bytes} giving the domain to transfer.
 
         @return: A new instance of L{SecondaryAuthority}.
         """

Modified: trunk/twisted/names/test/test_examples.py
==============================================================================
--- trunk/twisted/names/test/test_examples.py	(original)
+++ trunk/twisted/names/test/test_examples.py	Thu Apr 14 04:52:07 2016
@@ -5,12 +5,13 @@
 Tests for L{twisted.names} example scripts.
 """
 
+from __future__ import absolute_import, division
+
 import sys
-from StringIO import StringIO
 
 from twisted.python.filepath import FilePath
 from twisted.trial.unittest import SkipTest, TestCase
-
+from twisted.python.compat import NativeStringIO
 
 
 class ExampleTestBase(object):
@@ -37,9 +38,10 @@
         self.originalPath = sys.path[:]
         self.originalModules = sys.modules.copy()
 
-        self.fakeErr = StringIO()
+        # Python usually expects native strs to be written to sys.stdout/stderr
+        self.fakeErr = NativeStringIO()
         self.patch(sys, 'stderr', self.fakeErr)
-        self.fakeOut = StringIO()
+        self.fakeOut = NativeStringIO()
         self.patch(sys, 'stdout', self.fakeOut)
 
         # Get documentation root
@@ -81,7 +83,7 @@
         """
         self.assertEqual(
             self.examplePath.open().readline().rstrip(),
-            '#!/usr/bin/env python')
+            b'#!/usr/bin/env python')
 
 
     def test_usageConsistency(self):

Modified: trunk/twisted/names/test/test_names.py
==============================================================================
--- trunk/twisted/names/test/test_names.py	(original)
+++ trunk/twisted/names/test/test_names.py	Thu Apr 14 04:52:07 2016
@@ -5,8 +5,13 @@
 Test cases for twisted.names.
 """
 
-import socket, operator, copy
-from StringIO import StringIO
+from __future__ import absolute_import, division
+
+import socket
+import operator
+import copy
+
+from io import BytesIO
 from functools import partial, reduce
 from struct import pack
 
@@ -34,8 +39,8 @@
 
 
 soa_record = dns.Record_SOA(
-                    mname = 'test-domain.com',
-                    rname = 'root.test-domain.com',
+                    mname = b'test-domain.com',
+                    rname = u'root.test-domain.com',
                     serial = 100,
                     refresh = 1234,
                     minimum = 7654,
@@ -45,8 +50,8 @@
                 )
 
 reverse_soa = dns.Record_SOA(
-                     mname = '93.84.28.in-addr.arpa',
-                     rname = '93.84.28.in-addr.arpa',
+                     mname = b'93.84.28.in-addr.arpa',
+                     rname = b'93.84.28.in-addr.arpa',
                      serial = 120,
                      refresh = 54321,
                      minimum = 382,
@@ -56,8 +61,8 @@
                 )
 
 my_soa = dns.Record_SOA(
-    mname = 'my-domain.com',
-    rname = 'postmaster.test-domain.com',
+    mname = u'my-domain.com',
+    rname = b'postmaster.test-domain.com',
     serial = 130,
     refresh = 12345,
     minimum = 1,
@@ -66,61 +71,63 @@
     )
 
 test_domain_com = NoFileAuthority(
-    soa = ('test-domain.com', soa_record),
+    soa = (b'test-domain.com', soa_record),
     records = {
-        'test-domain.com': [
+        b'test-domain.com': [
             soa_record,
-            dns.Record_A('127.0.0.1'),
-            dns.Record_NS('39.28.189.39'),
-            dns.Record_SPF('v=spf1 mx/30 mx:example.org/30 -all'),
-            dns.Record_SPF('v=spf1 +mx a:\0colo', '.example.com/28 -all not valid'),
-            dns.Record_MX(10, 'host.test-domain.com'),
-            dns.Record_HINFO(os='Linux', cpu='A Fast One, Dontcha know'),
-            dns.Record_CNAME('canonical.name.com'),
-            dns.Record_MB('mailbox.test-domain.com'),
-            dns.Record_MG('mail.group.someplace'),
-            dns.Record_TXT('A First piece of Text', 'a SecoNd piece'),
-            dns.Record_A6(0, 'ABCD::4321', ''),
-            dns.Record_A6(12, '0:0069::0', 'some.network.tld'),
-            dns.Record_A6(8, '0:5634:1294:AFCB:56AC:48EF:34C3:01FF', 'tra.la.la.net'),
-            dns.Record_TXT('Some more text, haha!  Yes.  \0  Still here?'),
-            dns.Record_MR('mail.redirect.or.whatever'),
-            dns.Record_MINFO(rmailbx='r mail box', emailbx='e mail box'),
-            dns.Record_AFSDB(subtype=1, hostname='afsdb.test-domain.com'),
-            dns.Record_RP(mbox='whatever.i.dunno', txt='some.more.text'),
-            dns.Record_WKS('12.54.78.12', socket.IPPROTO_TCP,
-                           '\x12\x01\x16\xfe\xc1\x00\x01'),
-            dns.Record_NAPTR(100, 10, "u", "sip+E2U",
-                             "!^.*$!sip:[email protected]!"),
-            dns.Record_AAAA('AF43:5634:1294:AFCB:56AC:48EF:34C3:01FF')],
-        'http.tcp.test-domain.com': [
-            dns.Record_SRV(257, 16383, 43690, 'some.other.place.fool')
+            dns.Record_A(b'127.0.0.1'),
+            dns.Record_NS(b'39.28.189.39'),
+            dns.Record_SPF(b'v=spf1 mx/30 mx:example.org/30 -all'),
+            dns.Record_SPF(b'v=spf1 +mx a:\0colo',
+                           b'.example.com/28 -all not valid'),
+            dns.Record_MX(10, u'host.test-domain.com'),
+            dns.Record_HINFO(os=b'Linux', cpu=b'A Fast One, Dontcha know'),
+            dns.Record_CNAME(b'canonical.name.com'),
+            dns.Record_MB(b'mailbox.test-domain.com'),
+            dns.Record_MG(b'mail.group.someplace'),
+            dns.Record_TXT(b'A First piece of Text', b'a SecoNd piece'),
+            dns.Record_A6(0, b'ABCD::4321', b''),
+            dns.Record_A6(12, b'0:0069::0', b'some.network.tld'),
+            dns.Record_A6(8, b'0:5634:1294:AFCB:56AC:48EF:34C3:01FF',
+                          b'tra.la.la.net'),
+            dns.Record_TXT(b'Some more text, haha!  Yes.  \0  Still here?'),
+            dns.Record_MR(b'mail.redirect.or.whatever'),
+            dns.Record_MINFO(rmailbx=b'r mail box', emailbx=b'e mail box'),
+            dns.Record_AFSDB(subtype=1, hostname=b'afsdb.test-domain.com'),
+            dns.Record_RP(mbox=b'whatever.i.dunno', txt=b'some.more.text'),
+            dns.Record_WKS(b'12.54.78.12', socket.IPPROTO_TCP,
+                           b'\x12\x01\x16\xfe\xc1\x00\x01'),
+            dns.Record_NAPTR(100, 10, b"u", b"sip+E2U",
+                             b"!^.*$!sip:[email protected]!"),
+            dns.Record_AAAA(b'AF43:5634:1294:AFCB:56AC:48EF:34C3:01FF')],
+        b'http.tcp.test-domain.com': [
+            dns.Record_SRV(257, 16383, 43690, b'some.other.place.fool')
         ],
-        'host.test-domain.com': [
-            dns.Record_A('123.242.1.5'),
-            dns.Record_A('0.255.0.255'),
+        b'host.test-domain.com': [
+            dns.Record_A(b'123.242.1.5'),
+            dns.Record_A(b'0.255.0.255'),
         ],
-        'host-two.test-domain.com': [
+        b'host-two.test-domain.com': [
 #
 #  Python bug
 #           dns.Record_A('255.255.255.255'),
 #
-            dns.Record_A('255.255.255.254'),
-            dns.Record_A('0.0.0.0')
+            dns.Record_A(b'255.255.255.254'),
+            dns.Record_A(b'0.0.0.0')
         ],
-        'cname.test-domain.com': [
-            dns.Record_CNAME('test-domain.com')
+        b'cname.test-domain.com': [
+            dns.Record_CNAME(b'test-domain.com')
         ],
-        'anothertest-domain.com': [
-            dns.Record_A('1.2.3.4')],
+        b'anothertest-domain.com': [
+            dns.Record_A(b'1.2.3.4')],
     }
 )
 
 reverse_domain = NoFileAuthority(
-    soa = ('93.84.28.in-addr.arpa', reverse_soa),
+    soa = (b'93.84.28.in-addr.arpa', reverse_soa),
     records = {
-        '123.93.84.28.in-addr.arpa': [
-             dns.Record_PTR('test.host-reverse.lookup.com'),
+        b'123.93.84.28.in-addr.arpa': [
+             dns.Record_PTR(b'test.host-reverse.lookup.com'),
              reverse_soa
         ]
     }
@@ -128,14 +135,15 @@
 
 
 my_domain_com = NoFileAuthority(
-    soa = ('my-domain.com', my_soa),
+    soa = (b'my-domain.com', my_soa),
     records = {
-        'my-domain.com': [
+        b'my-domain.com': [
             my_soa,
-            dns.Record_A('1.2.3.4', ttl='1S'),
-            dns.Record_NS('ns1.domain', ttl='2M'),
-            dns.Record_NS('ns2.domain', ttl='3H'),
-            dns.Record_SRV(257, 16383, 43690, 'some.other.place.fool', ttl='4D')
+            dns.Record_A(b'1.2.3.4', ttl='1S'),
+            dns.Record_NS(b'ns1.domain', ttl=b'2M'),
+            dns.Record_NS(b'ns2.domain', ttl='3H'),
+            dns.Record_SRV(257, 16383, 43690, b'some.other.place.fool',
+                           ttl='4D')
             ]
         }
     )
@@ -224,7 +232,8 @@
         """Test DNS 'A' record queries with multiple answers"""
         return self.namesTest(
             self.resolver.lookupAddress('host.test-domain.com'),
-            [dns.Record_A('123.242.1.5', ttl=19283784), dns.Record_A('0.255.0.255', ttl=19283784)]
+            [dns.Record_A('123.242.1.5', ttl=19283784),
+             dns.Record_A('0.255.0.255', ttl=19283784)]
         )
 
 
@@ -268,7 +277,8 @@
         """Test DNS 'HINFO' record queries"""
         return self.namesTest(
             self.resolver.lookupHostInfo('test-domain.com'),
-            [dns.Record_HINFO(os='Linux', cpu='A Fast One, Dontcha know', ttl=19283784)]
+            [dns.Record_HINFO(os=b'Linux', cpu=b'A Fast One, Dontcha know',
+                              ttl=19283784)]
         )
 
     def test_PTR(self):
@@ -345,8 +355,10 @@
         """Test DNS 'TXT' record queries"""
         return self.namesTest(
             self.resolver.lookupText('test-domain.com'),
-            [dns.Record_TXT('A First piece of Text', 'a SecoNd piece', ttl=19283784),
-             dns.Record_TXT('Some more text, haha!  Yes.  \0  Still here?', ttl=19283784)]
+            [dns.Record_TXT(b'A First piece of Text', b'a SecoNd piece',
+                            ttl=19283784),
+             dns.Record_TXT(b'Some more text, haha!  Yes.  \0  Still here?',
+                            ttl=19283784)]
         )
 
 
@@ -356,8 +368,10 @@
         """
         return self.namesTest(
             self.resolver.lookupSenderPolicy('test-domain.com'),
-            [dns.Record_SPF('v=spf1 mx/30 mx:example.org/30 -all', ttl=19283784),
-            dns.Record_SPF('v=spf1 +mx a:\0colo', '.example.com/28 -all not valid', ttl=19283784)]
+            [dns.Record_SPF(b'v=spf1 mx/30 mx:example.org/30 -all',
+                            ttl=19283784),
+            dns.Record_SPF(b'v=spf1 +mx a:\0colo',
+                           b'.example.com/28 -all not valid', ttl=19283784)]
         )
 
 
@@ -365,7 +379,8 @@
         """Test DNS 'WKS' record queries"""
         return self.namesTest(
             self.resolver.lookupWellKnownServices('test-domain.com'),
-            [dns.Record_WKS('12.54.78.12', socket.IPPROTO_TCP, '\x12\x01\x16\xfe\xc1\x00\x01', ttl=19283784)]
+            [dns.Record_WKS('12.54.78.12', socket.IPPROTO_TCP,
+                            b'\x12\x01\x16\xfe\xc1\x00\x01', ttl=19283784)]
         )
 
 
@@ -428,8 +443,8 @@
         """
         return self.namesTest(
             self.resolver.lookupNamingAuthorityPointer('test-domain.com'),
-            [dns.Record_NAPTR(100, 10, "u", "sip+E2U",
-                              "!^.*$!sip:[email protected]!",
+            [dns.Record_NAPTR(100, 10, b"u", b"sip+E2U",
+                              b"!^.*$!sip:[email protected]!",
                               ttl=19283784)])
 
 
@@ -510,7 +525,7 @@
 
     def test_empty(self):
         resolvConf = self.mktemp()
-        fObj = file(resolvConf, 'w')
+        fObj = open(resolvConf, 'w')
         fObj.close()
         r = client.Resolver(resolv=resolvConf)
         self.assertEqual(r.dynServers, [('127.0.0.1', 53)])
@@ -533,7 +548,7 @@
         nothing to do with the zone example.com.
         """
         testDomain = test_domain_com
-        testDomainName = 'nonexistent.prefix-' + testDomain.soa[0]
+        testDomainName = b'nonexistent.prefix-' + testDomain.soa[0]
         f = self.failureResultOf(testDomain.lookupAddress(testDomainName))
         self.assertIsInstance(f.value, DomainError)
 
@@ -593,7 +608,7 @@
     def test_referral(self):
         """
         When an I{NS} record is found for a child zone, it is included in the
-        authority section of the response.  It is marked as non-authoritative if
+        authority section of the response. It is marked as non-authoritative if
         the authority is not also authoritative for the child zone (RFC 2181,
         section 6.1).
         """
@@ -667,7 +682,8 @@
         @param computed: A L{list} of L{RRHeader} instances giving the records
             computed by the scenario under test.
 
-        @raise self.failureException: If the two collections of records disagree.
+        @raise self.failureException: If the two collections of records
+            disagree.
         """
         # RRHeader instances aren't inherently ordered.  Impose an ordering
         # that's good enough for the purposes of these tests - in which we
@@ -969,7 +985,7 @@
         msg = Message()
         # DNSProtocol.writeMessage length encodes the message by prepending a
         # 2 byte message length to the buffered value.
-        msg.decode(StringIO(transport.value()[2:]))
+        msg.decode(BytesIO(transport.value()[2:]))
 
         self.assertEqual(
             [dns.Query('example.com', dns.AXFR, dns.IN)], msg.queries)
@@ -981,7 +997,7 @@
         with the I{A} records the authority has cached from the primary.
         """
         secondary = SecondaryAuthority.fromServerAddressAndDomain(
-            (b'192.168.1.2', 1234), b'example.com')
+            ('192.168.1.2', 1234), b'example.com')
         secondary._reactor = reactor = MemoryReactorClock()
 
         secondary.transfer()
@@ -993,7 +1009,7 @@
         proto.makeConnection(transport)
 
         query = Message(answer=1, auth=1)
-        query.decode(StringIO(transport.value()[2:]))
+        query.decode(BytesIO(transport.value()[2:]))
 
         # Generate a response with some data we can check.
         soa = Record_SOA(

Modified: trunk/twisted/python/dist3.py
==============================================================================
--- trunk/twisted/python/dist3.py	(original)
+++ trunk/twisted/python/dist3.py	Thu Apr 14 04:52:07 2016
@@ -124,6 +124,8 @@
     "twisted.logger.test.__init__",
     "twisted.names.__init__",
     "twisted.names._rfc1982",
+    "twisted.names.authority",
+    "twisted.names.secondary",
     "twisted.names.cache",
     "twisted.names.client",
     "twisted.names.common",
@@ -331,6 +333,8 @@
     "twisted.names.test.test_rfc1982",
     "twisted.names.test.test_server",
     "twisted.names.test.test_util",
+    "twisted.names.test.test_names",
+    "twisted.names.test.test_examples",
     "twisted.persisted.test.test_styles",
     "twisted.positioning.test.test_base",
     "twisted.positioning.test.test_nmea",