]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
convert to restructured text
authorBob Halley <halley@dnspython.org>
Sat, 31 Dec 2016 18:29:07 +0000 (10:29 -0800)
committerBob Halley <halley@dnspython.org>
Sat, 31 Dec 2016 18:29:07 +0000 (10:29 -0800)
dns/name.py

index 97e216c8fed9ac9f03be372bfa6c12a6a36b14a5..ab3dd1a24f2d7b35bbddb6299aedad4d0955b6e6 100644 (file)
 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 """DNS Names.
-
-@var root: The DNS root name.
-@type root: dns.name.Name object
-@var empty: The empty DNS name.
-@type empty: dns.name.Name object
 """
 
 from io import BytesIO
@@ -42,75 +37,72 @@ try:
 except AttributeError:
     maxint = (1 << (8 * struct.calcsize("P"))) // 2 - 1
 
+
+# fullcompare() result values
+
+#: The compared names have no relationship to each other.
 NAMERELN_NONE = 0
+#: the first name is a superdomain of the second.
 NAMERELN_SUPERDOMAIN = 1
+#: The first name is a subdomain of the second.
 NAMERELN_SUBDOMAIN = 2
+#: The compared names are equal.
 NAMERELN_EQUAL = 3
+#: The compared names have a common ancestor.
 NAMERELN_COMMONANCESTOR = 4
 
 
 class EmptyLabel(dns.exception.SyntaxError):
-
     """A DNS label is empty."""
 
 
 class BadEscape(dns.exception.SyntaxError):
-
     """An escaped code in a text format of DNS name is invalid."""
 
 
 class BadPointer(dns.exception.FormError):
-
     """A DNS compression pointer points forward instead of backward."""
 
 
 class BadLabelType(dns.exception.FormError):
-
     """The label type in DNS name wire format is unknown."""
 
 
 class NeedAbsoluteNameOrOrigin(dns.exception.DNSException):
-
     """An attempt was made to convert a non-absolute name to
     wire when there was also a non-absolute (or missing) origin."""
 
 
 class NameTooLong(dns.exception.FormError):
-
     """A DNS name is > 255 octets long."""
 
 
 class LabelTooLong(dns.exception.SyntaxError):
-
     """A DNS label is > 63 octets long."""
 
 
 class AbsoluteConcatenation(dns.exception.DNSException):
-
     """An attempt was made to append anything other than the
     empty name to an absolute DNS name."""
 
 
 class NoParent(dns.exception.DNSException):
-
     """An attempt was made to get the parent of the root name
     or the empty name."""
 
 class NoIDNA2008(dns.exception.DNSException):
-
     """IDNA 2008 processing was requested but the idna module is not
     available."""
 
 
 class IDNAException(dns.exception.DNSException):
-
     """IDNA processing raised an exception."""
 
     supp_kwargs = set(['idna_exception'])
     fmt = "IDNA processing exception: {idna_exception}"
 
-class IDNACodec(object):
 
+class IDNACodec(object):
     """Abstract base class for IDNA encoder/decoders."""
 
     def __init__(self):
@@ -131,21 +123,24 @@ class IDNACodec(object):
             label = maybe_decode(label)
         return _escapify(label, True)
 
-class IDNA2003Codec(IDNACodec):
 
+class IDNA2003Codec(IDNACodec):
     """IDNA 2003 encoder/decoder."""
 
     def __init__(self, strict_decode=False):
         """Initialize the IDNA 2003 encoder/decoder.
-        @param strict_decode: If True, then IDNA2003 checking is done when
-        decoding.  This can cause failures if the name was encoded with
-        IDNA2008.  The default is False.
-        @type strict_decode: bool
+
+        *strict_decode* is a ``bool``. If `True`, then IDNA2003 checking
+        is done when decoding.  This can cause failures if the name
+        was encoded with IDNA2008.  The default is `False`.
         """
+
         super(IDNA2003Codec, self).__init__()
         self.strict_decode = strict_decode
 
     def encode(self, label):
+        """Encode *label*."""
+
         if label == '':
             return b''
         try:
@@ -154,6 +149,7 @@ class IDNA2003Codec(IDNACodec):
             raise LabelTooLong
 
     def decode(self, label):
+        """Decode *label*."""
         if not self.strict_decode:
             return super(IDNA2003Codec, self).decode(label)
         if label == b'':
@@ -163,34 +159,34 @@ class IDNA2003Codec(IDNACodec):
         except Exception as e:
             raise IDNAException(idna_exception=e)
 
-class IDNA2008Codec(IDNACodec):
 
-    """IDNA 2008 encoder/decoder."""
+class IDNA2008Codec(IDNACodec):
+    """IDNA 2008 encoder/decoder.
+
+        *uts_46* is a ``bool``.  If True, apply Unicode IDNA
+        compatibility processing as described in Unicode Technical
+        Standard #46 (http://unicode.org/reports/tr46/).
+        If False, do not apply the mapping.  The default is False.
+
+        *transitional* is a ``bool``: If True, use the
+        "transitional" mode described in Unicode Technical Standard
+        #46.  The default is False.
+
+        *allow_pure_ascii* is a ``bool``.  If True, then a label which
+        consists of only ASCII characters is allowed.  This is less
+        strict than regular IDNA 2008, but is also necessary for mixed
+        names, e.g. a name with starting with "_sip._tcp." and ending
+        in an IDN suffix which would otherwise be disallowed.  The
+        default is False.
+
+        *strict_decode* is a ``bool``: If True, then IDNA2008 checking
+        is done when decoding.  This can cause failures if the name
+        was encoded with IDNA2003.  The default is False.
+        """
 
     def __init__(self, uts_46=False, transitional=False,
                  allow_pure_ascii=False, strict_decode=False):
-        """Initialize the IDNA 2008 encoder/decoder.
-        @param uts_46: If True, apply Unicode IDNA compatibility processing
-        as described in Unicode Technical Standard #46
-        (U{http://unicode.org/reports/tr46/}).  This parameter is only
-        meaningful if IDNA 2008 is in use.  If False, do not apply
-        the mapping.  The default is False
-        @type uts_46: bool
-        @param transitional: If True, use the "transitional" mode described
-        in Unicode Technical Standard #46.  This parameter is only
-        meaningful if IDNA 2008 is in use.  The default is False.
-        @type transitional: bool
-        @param allow_pure_ascii: If True, then a label which
-        consists of only ASCII characters is allowed.  This is less strict
-        than regular IDNA 2008, but is also necessary for mixed names,
-        e.g. a name with starting with "_sip._tcp." and ending in an IDN
-        suffixm which would otherwise be disallowed.  The default is False
-        @type allow_pure_ascii: bool
-        @param strict_decode: If True, then IDNA2008 checking is done when
-        decoding.  This can cause failures if the name was encoded with
-        IDNA2003.  The default is False.
-        @type strict_decode: bool
-        """
+        """Initialize the IDNA 2008 encoder/decoder."""
         super(IDNA2008Codec, self).__init__()
         self.uts_46 = uts_46
         self.transitional = transitional
@@ -277,9 +273,14 @@ def _escapify(label, unicode_mode=False):
 def _validate_labels(labels):
     """Check for empty labels in the middle of a label sequence,
     labels that are too long, and for too many labels.
-    @raises NameTooLong: the name as a whole is too long
-    @raises EmptyLabel: a label is empty (i.e. the root label) and appears
-    in a position other than the end of the label sequence"""
+
+    Raises ``dns.name.NameTooLong`` if the name as a whole is too long.
+
+    Raises ``dns.name.EmptyLabel`` if a label is empty (i.e. the root
+    label) and appears in a position other than the end of the label
+    sequence
+
+    """
 
     l = len(labels)
     total = 0
@@ -299,7 +300,12 @@ def _validate_labels(labels):
         raise EmptyLabel
 
 
-def _ensure_bytes(label):
+def _maybe_convert_to_binary(label):
+    """If label is ``text``, convert it to ``binary``.  If it is already
+    ``binary`` just return it.
+
+    """
+
     if isinstance(label, binary_type):
         return label
     if isinstance(label, text_type):
@@ -311,24 +317,24 @@ class Name(object):
 
     """A DNS name.
 
-    The dns.name.Name class represents a DNS name as a tuple of labels.
-    Instances of the class are immutable.
-
-    @ivar labels: The tuple of labels in the name. Each label is a string of
-    up to 63 octets."""
+    The dns.name.Name class represents a DNS name as a tuple of
+    labels.  Each label is a `binary` in DNS wire format.  Instances
+    of the class are immutable.
+    """
 
     __slots__ = ['labels']
 
     def __init__(self, labels):
         """Initialize a domain name from a list of labels.
-        @param labels: the labels
-        @type labels: any iterable whose values are strings
+        *labels* is any iterable whose values are ``text`` or ``binary``.
         """
-        labels = [_ensure_bytes(x) for x in labels]
+
+        labels = [_maybe_convert_to_binary(x) for x in labels]
         super(Name, self).__setattr__('labels', tuple(labels))
         _validate_labels(self.labels)
 
     def __setattr__(self, name, value):
+        # Names are immutable
         raise TypeError("object doesn't support attribute assignment")
 
     def __copy__(self):
@@ -338,6 +344,7 @@ class Name(object):
         return Name(copy.deepcopy(self.labels, memo))
 
     def __getstate__(self):
+        # Names can be pickled
         return {'labels': self.labels}
 
     def __setstate__(self, state):
@@ -346,21 +353,24 @@ class Name(object):
 
     def is_absolute(self):
         """Is the most significant label of this name the root label?
-        @rtype: bool
+
+        Returns a ``bool``.
         """
 
         return len(self.labels) > 0 and self.labels[-1] == b''
 
     def is_wild(self):
         """Is this name wild?  (I.e. Is the least significant label '*'?)
-        @rtype: bool
+
+        Returns a ``bool``.
         """
 
         return len(self.labels) > 0 and self.labels[0] == b'*'
 
     def __hash__(self):
         """Return a case-insensitive hash of the name.
-        @rtype: int
+
+        Returns an ``int``.
         """
 
         h = long(0)
@@ -370,20 +380,35 @@ class Name(object):
         return int(h % maxint)
 
     def fullcompare(self, other):
-        """Compare two names, returning a 3-tuple (relation, order, nlabels).
+        """Compare two names, returning a 3-tuple
+        ``(relation, order, nlabels)``.
 
-        I{relation} describes the relation ship between the names,
-        and is one of: dns.name.NAMERELN_NONE,
-        dns.name.NAMERELN_SUPERDOMAIN, dns.name.NAMERELN_SUBDOMAIN,
-        dns.name.NAMERELN_EQUAL, or dns.name.NAMERELN_COMMONANCESTOR
+        *relation* describes the relation ship between the names,
+        and is one of: ``dns.name.NAMERELN_NONE``,
+        ``dns.name.NAMERELN_SUPERDOMAIN``, ``dns.name.NAMERELN_SUBDOMAIN``,
+        ``dns.name.NAMERELN_EQUAL``, or ``dns.name.NAMERELN_COMMONANCESTOR``.
 
-        I{order} is < 0 if self < other, > 0 if self > other, and ==
-        0 if self == other.  A relative name is always less than an
+        *order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and ==
+        0 if *self* == *other*.  A relative name is always less than an
         absolute name.  If both names have the same relativity, then
         the DNSSEC order relation is used to order them.
 
-        I{nlabels} is the number of significant labels that the two names
+        *nlabels* is the number of significant labels that the two names
         have in common.
+
+        Here are some examples.  Names ending in "." are absolute names,
+        those not ending in "." are relative names.
+
+        =============  =============  ===========  =====  =======
+        self           other          relation     order  nlabels
+        =============  =============  ===========  =====  =======
+        www.example.   www.example.   equal        0      3
+        www.example.   example.       subdomain    > 0    2
+        example.       www.example.   superdomain  < 0    2
+        example1.com.  example2.com.  common anc.  < 0    2
+        example1       example2.      none         < 0    0
+        example1.      example2       none         > 0    0
+        =============  =============  ===========  =====  =======
         """
 
         sabs = self.is_absolute()
@@ -433,8 +458,10 @@ class Name(object):
     def is_subdomain(self, other):
         """Is self a subdomain of other?
 
-        The notion of subdomain includes equality.
-        @rtype: bool
+        Note that the notion of subdomain includes equality, e.g.
+        "dnpython.org" is a subdomain of itself.
+
+        Returns a ``bool``.
         """
 
         (nr, o, nl) = self.fullcompare(other)
@@ -445,8 +472,10 @@ class Name(object):
     def is_superdomain(self, other):
         """Is self a superdomain of other?
 
-        The notion of subdomain includes equality.
-        @rtype: bool
+        Note that the notion of superdomain includes equality, e.g.
+        "dnpython.org" is a superdomain of itself.
+
+        Returns a ``bool``.
         """
 
         (nr, o, nl) = self.fullcompare(other)
@@ -457,7 +486,6 @@ class Name(object):
     def canonicalize(self):
         """Return a name which is equal to the current name, but is in
         DNSSEC canonical form.
-        @rtype: dns.name.Name object
         """
 
         return Name([x.lower() for x in self.labels])
@@ -505,10 +533,13 @@ class Name(object):
         return self.to_text(False)
 
     def to_text(self, omit_final_dot=False):
-        """Convert name to text format.
-        @param omit_final_dot: If True, don't emit the final dot (denoting the
-        root label) for absolute names.  The default is False.
-        @rtype: string
+        """Convert name to DNS text format.
+
+        *omit_final_dot* is a ``bool``.  If True, don't emit the final
+        dot (denoting the root label) for absolute names.  The default
+        is False.
+
+        Returns a ``text``.
         """
 
         if len(self.labels) == 0:
@@ -527,16 +558,17 @@ class Name(object):
 
         IDN ACE labels are converted to Unicode.
 
-        @param omit_final_dot: If True, don't emit the final dot (denoting the
-        root label) for absolute names.  The default is False.
-        @type omit_final_dot: bool
-        @param idna_codec: IDNA encoder/decoder.  If None, the
-        IDNA_2003_Practical encoder/decoder is used.  The IDNA_2003_Practical
-        decoder does not impose any policy, it just decodes punycode, so if
-        you don't want checking for compliance, you can use this decoder for
-        IDNA2008 as well.
-        @type idna_codec: dns.name.IDNA
-        @rtype: string
+        *omit_final_dot* is a ``bool``.  If True, don't emit the final
+        dot (denoting the root label) for absolute names.  The default
+        is False.
+        *idna_codec* specifies the IDNA encoder/decoder.  If None, the
+        dns.name.IDNA_2003_Practical encoder/decoder is used.
+        The IDNA_2003_Practical decoder does
+        not impose any policy, it just decodes punycode, so if you
+        don't want checking for compliance, you can use this decoder
+        for IDNA2008 as well.
+
+        Returns a ``text``.
         """
 
         if len(self.labels) == 0:
@@ -554,15 +586,18 @@ class Name(object):
     def to_digestable(self, origin=None):
         """Convert name to a format suitable for digesting in hashes.
 
-        The name is canonicalized and converted to uncompressed wire format.
+        The name is canonicalized and converted to uncompressed wire
+        format.  All names in wire format are absolute.  If the name
+        is a relative name, then an origin must be supplied.
+
+        *origin* is a ``dns.name.Name`` or ``None``.  If the name is
+        relative and origin is not ``None``, then origin will be appended
+        to the name.
 
-        @param origin: If the name is relative and origin is not None, then
-        origin will be appended to it.
-        @type origin: dns.name.Name object
-        @raises NeedAbsoluteNameOrOrigin: All names in wire format are
-        absolute.  If self is a relative name, then an origin must be supplied;
-        if it is missing, then this exception is raised
-        @rtype: string
+        Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is
+        relative and no origin was provided.
+
+        Returns a ``binary``.
         """
 
         if not self.is_absolute():
@@ -579,19 +614,22 @@ class Name(object):
     def to_wire(self, file=None, compress=None, origin=None):
         """Convert name to wire format, possibly compressing it.
 
-        @param file: the file where the name is emitted (typically
-        a BytesIO file).  If None, a string containing the wire name
-        will be returned.
-        @type file: file or None
-        @param compress: The compression table.  If None (the default) names
-        will not be compressed.
-        @type compress: dict
-        @param origin: If the name is relative and origin is not None, then
-        origin will be appended to it.
-        @type origin: dns.name.Name object
-        @raises NeedAbsoluteNameOrOrigin: All names in wire format are
-        absolute.  If self is a relative name, then an origin must be supplied;
-        if it is missing, then this exception is raised
+        *file* is the file where the name is emitted (typically a
+        BytesIO file).  If ``None`` (the default), a ``binary``
+        containing the wire name will be returned.
+
+        *compress*, a ``dict``, is the compression table to use.  If
+        ``None`` (the default), names will not be compressed.
+
+        *origin* is a ``dns.name.Name`` or ``None``.  If the name is
+        relative and origin is not ``None``, then *origin* will be appended
+        to it.
+
+        Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is
+        relative and no origin was provided.
+
+        Returns a ``binary`` or ``None``.
+
         """
 
         if file is None:
@@ -634,7 +672,8 @@ class Name(object):
 
     def __len__(self):
         """The length of the name (in labels).
-        @rtype: int
+
+        Returns an ``int``.
         """
 
         return len(self.labels)
@@ -649,14 +688,14 @@ class Name(object):
         return self.relativize(other)
 
     def split(self, depth):
-        """Split a name into a prefix and suffix at depth.
+        """Split a name into a prefix and suffix names at the specified depth.
+
+        *depth* is an ``int`` specifying the number of labels in the suffix
 
-        @param depth: the number of labels in the suffix
-        @type depth: int
-        @raises ValueError: the depth was not >= 0 and <= the length of the
+        Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the
         name.
-        @returns: the tuple (prefix, suffix)
-        @rtype: tuple
+
+        Returns the tuple ``(prefix, suffix)``.
         """
 
         l = len(self.labels)
@@ -671,9 +710,11 @@ class Name(object):
 
     def concatenate(self, other):
         """Return a new name which is the concatenation of self and other.
-        @rtype: dns.name.Name object
-        @raises AbsoluteConcatenation: self is absolute and other is
-        not the empty name
+
+        Raises ``dns.name.AbsoluteConcatenation`` if the name is
+        absolute and *other* is not the empty name.
+
+        Returns a ``dns.name.Name``.
         """
 
         if self.is_absolute() and len(other) > 0:
@@ -683,9 +724,14 @@ class Name(object):
         return Name(labels)
 
     def relativize(self, origin):
-        """If self is a subdomain of origin, return a new name which is self
-        relative to origin.  Otherwise return self.
-        @rtype: dns.name.Name object
+        """If the name is a subdomain of *origin*, return a new name which is
+        the name relative to origin.  Otherwise return the name.
+
+        For example, relativizing ``www.dnspython.org.`` to origin
+        ``dnspython.org.`` returns the name ``www``.  Relativizing ``example.``
+        to origin ``dnspython.org.`` returns ``example.``.
+
+        Returns a ``dns.name.Name``.
         """
 
         if origin is not None and self.is_subdomain(origin):
@@ -694,9 +740,14 @@ class Name(object):
             return self
 
     def derelativize(self, origin):
-        """If self is a relative name, return a new name which is the
-        concatenation of self and origin.  Otherwise return self.
-        @rtype: dns.name.Name object
+        """If the name is a relative name, return a new name which is the
+        concatenation of the name and origin.  Otherwise return the name.
+
+        For example, derelativizing ``www`` to origin ``dnspython.org.``
+        returns the name ``www.dnspython.org.``.  Derelativizing ``example.``
+        to origin ``dnspython.org.`` returns ``example.``.
+
+        Returns a ``dns.name.Name``.
         """
 
         if not self.is_absolute():
@@ -705,11 +756,14 @@ class Name(object):
             return self
 
     def choose_relativity(self, origin=None, relativize=True):
-        """Return a name with the relativity desired by the caller.  If
-        origin is None, then self is returned.  Otherwise, if
-        relativize is true the name is relativized, and if relativize is
-        false the name is derelativized.
-        @rtype: dns.name.Name object
+        """Return a name with the relativity desired by the caller.
+
+        If *origin* is ``None``, then the name is returned.
+        Otherwise, if *relativize* is ``True`` the name is
+        relativized, and if *relativize* is ``False`` the name is
+        derelativized.
+
+        Returns a ``dns.name.Name``.
         """
 
         if origin:
@@ -722,31 +776,41 @@ class Name(object):
 
     def parent(self):
         """Return the parent of the name.
-        @rtype: dns.name.Name object
-        @raises NoParent: the name is either the root name or the empty name,
-        and thus has no parent.
+
+        For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``.
+
+        Raises ``dns.name.NoParent`` if the name is either the root name or the
+        empty name, and thus has no parent.
+
+        Returns a ``dns.name.Name``.
         """
+
         if self == root or self == empty:
             raise NoParent
         return Name(self.labels[1:])
 
+#: The root name, '.'
 root = Name([b''])
-empty = Name([])
 
+#: The empty name.
+empty = Name([])
 
 def from_unicode(text, origin=root, idna_codec=None):
     """Convert unicode text into a Name object.
 
-    Labels are encoded in IDN ACE form.
+    Labels are encoded in IDN ACE form according to rules specified by
+    the IDNA codec.
+
+    *text*, a ``text``, is the text to convert into a name.
+
+    *origin*, a ``dns.name.Name``, specifies the origin to
+    append to non-absolute names.  The default is the root name.
 
-    @param text: The text to convert into a name.
-    @type text: Unicode string
-    @param origin: The origin to append to non-absolute names.
-    @type origin: dns.name.Name
-    @param idna_codec: IDNA encoder/decoder.  If None, the default IDNA 2003
-    encoder/decoder is used.
-    @type idna_codec: dns.name.IDNA
-    @rtype: dns.name.Name object
+    *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
+    encoder/decoder.  If ``None``, the default IDNA 2003 encoder/decoder
+    is used.
+
+    Returns a ``dns.name.Name``.
     """
 
     if not isinstance(text, text_type):
@@ -809,14 +873,16 @@ def from_unicode(text, origin=root, idna_codec=None):
 def from_text(text, origin=root, idna_codec=None):
     """Convert text into a Name object.
 
-    @param text: The text to convert into a name.
-    @type text: string
-    @param origin: The origin to append to non-absolute names.
-    @type origin: dns.name.Name
-    @param idna_codec: IDNA encoder/decoder.  If None, the default IDNA 2003
-    encoder/decoder is used.
-    @type idna_codec: dns.name.IDNA
-    @rtype: dns.name.Name object
+    *text*, a ``text``, is the text to convert into a name.
+
+    *origin*, a ``dns.name.Name``, specifies the origin to
+    append to non-absolute names.  The default is the root name.
+
+    *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
+    encoder/decoder.  If ``None``, the default IDNA 2003 encoder/decoder
+    is used.
+
+    Returns a ``dns.name.Name``.
     """
 
     if isinstance(text, text_type):
@@ -878,17 +944,21 @@ def from_text(text, origin=root, idna_codec=None):
 
 def from_wire(message, current):
     """Convert possibly compressed wire format into a Name.
-    @param message: the entire DNS message
-    @type message: string
-    @param current: the offset of the beginning of the name from the start
-    of the message
-    @type current: int
-    @raises dns.name.BadPointer: a compression pointer did not point backwards
-    in the message
-    @raises dns.name.BadLabelType: an invalid label type was encountered.
-    @returns: a tuple consisting of the name that was read and the number
-    of bytes of the wire format message which were consumed reading it
-    @rtype: (dns.name.Name object, int) tuple
+
+    *message* is a ``binary`` containing an entire DNS message in DNS
+    wire form.
+
+    *current*, an ``int``, is the offset of the beginning of the name
+    from the start of the message
+
+    Raises ``dns.name.BadPointer`` if a compression pointer did not
+    point backwards in the message.
+
+    Raises ``dns.name.BadLabelType`` if an invalid label type was encountered.
+
+    Returns a ``(dns.name.Name, int)`` tuple consisting of the name
+    that was read and the number of bytes of the wire format message
+    which were consumed reading it.
     """
 
     if not isinstance(message, binary_type):