summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.flake82
-rw-r--r--Makefile3
-rw-r--r--dns/_asyncio_backend.py4
-rw-r--r--dns/_curio_backend.py2
-rw-r--r--dns/_trio_backend.py2
-rw-r--r--dns/asyncbackend.py5
-rw-r--r--dns/asyncresolver.py8
-rw-r--r--dns/dnssec.py27
-rw-r--r--dns/edns.py16
-rw-r--r--dns/flags.py23
-rw-r--r--dns/ipv6.py2
-rw-r--r--dns/message.py20
-rw-r--r--dns/name.py4
-rw-r--r--dns/opcode.py12
-rw-r--r--dns/query.py34
-rw-r--r--dns/rcode.py27
-rw-r--r--dns/rdataclass.py15
-rw-r--r--dns/rdataset.py2
-rw-r--r--dns/rdatatype.py80
-rw-r--r--dns/rdtypes/CH/A.py3
-rw-r--r--dns/rdtypes/dnskeybase.py10
-rw-r--r--dns/resolver.py35
-rw-r--r--dns/rrset.py20
-rw-r--r--dns/update.py11
-rw-r--r--dns/zone.py6
-rw-r--r--pylintrc23
-rw-r--r--tests/test_constants.py38
-rw-r--r--tests/util.py19
-rwxr-xr-xutil/constants-tool85
29 files changed, 453 insertions, 85 deletions
diff --git a/.flake8 b/.flake8
index a294638..3b1f77d 100644
--- a/.flake8
+++ b/.flake8
@@ -6,6 +6,8 @@ ignore =
E129,
# Whitespace round parameter '=' can be excessive
E252,
+ # Multiple # in a comment is OK
+ E266,
# Not excited by the "two blank lines" rule
E302,
E305,
diff --git a/Makefile b/Makefile
index d961e76..ff51150 100644
--- a/Makefile
+++ b/Makefile
@@ -58,6 +58,9 @@ potestlf:
potype:
poetry run python -m mypy examples tests dns/*.py
+polint:
+ poetry run pylint dns/*.py
+
poflake:
poetry run flake8 dns
diff --git a/dns/_asyncio_backend.py b/dns/_asyncio_backend.py
index 3af34ff..1cf1519 100644
--- a/dns/_asyncio_backend.py
+++ b/dns/_asyncio_backend.py
@@ -86,14 +86,12 @@ class StreamSocket(dns._asyncbackend.DatagramSocket):
self.writer = writer
async def sendall(self, what, timeout):
- self.writer.write(what),
+ self.writer.write(what)
return await _maybe_wait_for(self.writer.drain(), timeout)
- raise dns.exception.Timeout(timeout=timeout)
async def recv(self, count, timeout):
return await _maybe_wait_for(self.reader.read(count),
timeout)
- raise dns.exception.Timeout(timeout=timeout)
async def close(self):
self.writer.close()
diff --git a/dns/_curio_backend.py b/dns/_curio_backend.py
index 300e1b8..894e87c 100644
--- a/dns/_curio_backend.py
+++ b/dns/_curio_backend.py
@@ -21,6 +21,8 @@ def _maybe_timeout(timeout):
# for brevity
_lltuple = dns.inet.low_level_address_tuple
+# pylint: disable=redefined-outer-name
+
class DatagramSocket(dns._asyncbackend.DatagramSocket):
def __init__(self, socket):
diff --git a/dns/_trio_backend.py b/dns/_trio_backend.py
index 92ea879..82512cd 100644
--- a/dns/_trio_backend.py
+++ b/dns/_trio_backend.py
@@ -21,6 +21,8 @@ def _maybe_timeout(timeout):
# for brevity
_lltuple = dns.inet.low_level_address_tuple
+# pylint: disable=redefined-outer-name
+
class DatagramSocket(dns._asyncbackend.DatagramSocket):
def __init__(self, socket):
diff --git a/dns/asyncbackend.py b/dns/asyncbackend.py
index 9582a6f..e6a42ce 100644
--- a/dns/asyncbackend.py
+++ b/dns/asyncbackend.py
@@ -2,9 +2,12 @@
import dns.exception
+# pylint: disable=unused-import
+
from dns._asyncbackend import Socket, DatagramSocket, \
StreamSocket, Backend # noqa:
+# pylint: enable=unused-import
_default_backend = None
@@ -25,6 +28,7 @@ def get_backend(name):
Raises NotImplementError if an unknown backend name is specified.
"""
+ # pylint: disable=import-outside-toplevel,redefined-outer-name
backend = _backends.get(name)
if backend:
return backend
@@ -50,6 +54,7 @@ def sniff():
Returns the name of the library, or raises AsyncLibraryNotFoundError
if the library cannot be determined.
"""
+ # pylint: disable=import-outside-toplevel
try:
if _no_sniffio:
raise ImportError
diff --git a/dns/asyncresolver.py b/dns/asyncresolver.py
index b0af934..a6582b2 100644
--- a/dns/asyncresolver.py
+++ b/dns/asyncresolver.py
@@ -36,6 +36,8 @@ _tcp = dns.asyncquery.tcp
class Resolver(dns.resolver.Resolver):
+ # pylint: disable=invalid-overridden-method, arguments-differ
+
async def resolve(self, qname, rdtype=dns.rdatatype.A,
rdclass=dns.rdataclass.IN,
tcp=False, source=None, raise_on_no_answer=True,
@@ -139,11 +141,15 @@ class Resolver(dns.resolver.Resolver):
if answer is not None:
return answer
+ # pylint: disable=signature-differs
+
async def query(self, *args, **kwargs):
# We have to define something here as we don't want to inherit the
# parent's query().
raise NotImplementedError
+ # pylint: enable=signature-differs
+
async def resolve_address(self, ipaddr, *args, **kwargs):
"""Use an asynchronous resolver to run a reverse query for PTR
records.
@@ -165,6 +171,8 @@ class Resolver(dns.resolver.Resolver):
rdclass=dns.rdataclass.IN,
*args, **kwargs)
+ # pylint: disable=redefined-outer-name
+
async def canonical_name(self, name):
"""Determine the canonical name of *name*.
diff --git a/dns/dnssec.py b/dns/dnssec.py
index e36e729..095fabd 100644
--- a/dns/dnssec.py
+++ b/dns/dnssec.py
@@ -64,9 +64,6 @@ class Algorithm(dns.enum.IntEnum):
return 255
-globals().update(Algorithm.__members__)
-
-
def algorithm_from_text(text):
"""Convert text into a DNSSEC algorithm value.
@@ -551,7 +548,7 @@ def nsec3_hash(domain, salt, iterations, algorithm):
domain_encoded = domain.canonicalize().to_wire()
digest = hashlib.sha1(domain_encoded + salt_encoded).digest()
- for i in range(iterations):
+ for _ in range(iterations):
digest = hashlib.sha1(digest + salt_encoded).digest()
output = base64.b32encode(digest).decode("utf-8")
@@ -584,3 +581,25 @@ else:
validate = _validate # type: ignore
validate_rrsig = _validate_rrsig # type: ignore
_have_pyca = True
+
+### BEGIN generated Algorithm constants
+
+RSAMD5 = Algorithm.RSAMD5
+DH = Algorithm.DH
+DSA = Algorithm.DSA
+ECC = Algorithm.ECC
+RSASHA1 = Algorithm.RSASHA1
+DSANSEC3SHA1 = Algorithm.DSANSEC3SHA1
+RSASHA1NSEC3SHA1 = Algorithm.RSASHA1NSEC3SHA1
+RSASHA256 = Algorithm.RSASHA256
+RSASHA512 = Algorithm.RSASHA512
+ECCGOST = Algorithm.ECCGOST
+ECDSAP256SHA256 = Algorithm.ECDSAP256SHA256
+ECDSAP384SHA384 = Algorithm.ECDSAP384SHA384
+ED25519 = Algorithm.ED25519
+ED448 = Algorithm.ED448
+INDIRECT = Algorithm.INDIRECT
+PRIVATEDNS = Algorithm.PRIVATEDNS
+PRIVATEOID = Algorithm.PRIVATEOID
+
+### END generated Algorithm constants
diff --git a/dns/edns.py b/dns/edns.py
index 087592b..199e8c4 100644
--- a/dns/edns.py
+++ b/dns/edns.py
@@ -50,7 +50,6 @@ class OptionType(dns.enum.IntEnum):
def _maximum(cls):
return 65535
-globals().update(OptionType.__members__)
class Option:
@@ -352,3 +351,18 @@ def register_type(implementation, otype):
"""
_type_to_class[otype] = implementation
+
+### BEGIN generated OptionType constants
+
+NSID = OptionType.NSID
+DAU = OptionType.DAU
+DHU = OptionType.DHU
+N3U = OptionType.N3U
+ECS = OptionType.ECS
+EXPIRE = OptionType.EXPIRE
+COOKIE = OptionType.COOKIE
+KEEPALIVE = OptionType.KEEPALIVE
+PADDING = OptionType.PADDING
+CHAIN = OptionType.CHAIN
+
+### END generated OptionType constants
diff --git a/dns/flags.py b/dns/flags.py
index 4eb6d90..9652287 100644
--- a/dns/flags.py
+++ b/dns/flags.py
@@ -37,8 +37,6 @@ class Flag(enum.IntFlag):
#: Checking Disabled
CD = 0x0010
-globals().update(Flag.__members__)
-
# EDNS flags
@@ -47,9 +45,6 @@ class EDNSFlag(enum.IntFlag):
DO = 0x8000
-globals().update(EDNSFlag.__members__)
-
-
def _from_text(text, enum_class):
flags = 0
tokens = text.split()
@@ -104,3 +99,21 @@ def edns_to_text(flags):
"""
return _to_text(flags, EDNSFlag)
+
+### BEGIN generated Flag constants
+
+QR = Flag.QR
+AA = Flag.AA
+TC = Flag.TC
+RD = Flag.RD
+RA = Flag.RA
+AD = Flag.AD
+CD = Flag.CD
+
+### END generated Flag constants
+
+### BEGIN generated EDNSFlag constants
+
+DO = EDNSFlag.DO
+
+### END generated EDNSFlag constants
diff --git a/dns/ipv6.py b/dns/ipv6.py
index 5424fce..0905aaf 100644
--- a/dns/ipv6.py
+++ b/dns/ipv6.py
@@ -157,7 +157,7 @@ def inet_aton(text, ignore_scope=False):
if seen_empty:
raise dns.exception.SyntaxError
seen_empty = True
- for i in range(0, 8 - l + 1):
+ for _ in range(0, 8 - l + 1):
canonical.append(b'0000')
else:
lc = len(c)
diff --git a/dns/message.py b/dns/message.py
index 52e3819..ceebdf9 100644
--- a/dns/message.py
+++ b/dns/message.py
@@ -107,7 +107,6 @@ class MessageSection(dns.enum.IntEnum):
def _maximum(cls):
return 3
-globals().update(MessageSection.__members__)
DEFAULT_EDNS_PAYLOAD = 1232
MAX_CHAIN = 16
@@ -699,9 +698,13 @@ class Message:
# What the caller picked is fine.
return value
+ # pylint: disable=unused-argument
+
def _parse_rr_header(self, section, name, rdclass, rdtype):
return (rdclass, rdtype, None, False)
+ # pylint: enable=unused-argument
+
def _parse_special_rr_header(self, section, count, position,
name, rdclass, rdtype):
if rdtype == dns.rdatatype.OPT:
@@ -811,6 +814,8 @@ def _maybe_import_update():
# We avoid circular imports by doing this here. We do it in another
# function as doing it in _message_factory_from_opcode() makes "dns"
# a local symbol, and the first line fails :)
+
+ # pylint: disable=redefined-outer-name,import-outside-toplevel,unused-import
import dns.update # noqa: F401
@@ -857,7 +862,7 @@ class _WireReader:
"""
section = self.message.sections[section_number]
- for i in range(qcount):
+ for _ in range(qcount):
qname = self.parser.get_name(self.message.origin)
(rdtype, rdclass) = self.parser.get_struct('!HH')
(rdclass, rdtype, _, _) = \
@@ -1080,7 +1085,7 @@ class _TextReader:
self.opcode = dns.opcode.QUERY
self.flags = 0
- def _header_line(self, section):
+ def _header_line(self, _):
"""Process one line from the text format header section."""
token = self.tok.get()
@@ -1459,3 +1464,12 @@ def make_response(query, recursion_available=False, our_payload=8192,
tsig_error, b'', query.keyalgorithm)
response.request_mac = query.mac
return response
+
+### BEGIN generated MessageSection constants
+
+QUESTION = MessageSection.QUESTION
+ANSWER = MessageSection.ANSWER
+AUTHORITY = MessageSection.AUTHORITY
+ADDITIONAL = MessageSection.ADDITIONAL
+
+### END generated MessageSection constants
diff --git a/dns/name.py b/dns/name.py
index 94d7b93..8775e0b 100644
--- a/dns/name.py
+++ b/dns/name.py
@@ -459,7 +459,7 @@ class Name:
Returns a ``bool``.
"""
- (nr, o, nl) = self.fullcompare(other)
+ (nr, _, _) = self.fullcompare(other)
if nr == NAMERELN_SUBDOMAIN or nr == NAMERELN_EQUAL:
return True
return False
@@ -473,7 +473,7 @@ class Name:
Returns a ``bool``.
"""
- (nr, o, nl) = self.fullcompare(other)
+ (nr, _, _) = self.fullcompare(other)
if nr == NAMERELN_SUPERDOMAIN or nr == NAMERELN_EQUAL:
return True
return False
diff --git a/dns/opcode.py b/dns/opcode.py
index 5a76326..5cf6143 100644
--- a/dns/opcode.py
+++ b/dns/opcode.py
@@ -40,8 +40,6 @@ class Opcode(dns.enum.IntEnum):
def _unknown_exception_class(cls):
return UnknownOpcode
-globals().update(Opcode.__members__)
-
class UnknownOpcode(dns.exception.DNSException):
"""An DNS opcode is unknown."""
@@ -105,3 +103,13 @@ def is_update(flags):
"""
return from_flags(flags) == Opcode.UPDATE
+
+### BEGIN generated Opcode constants
+
+QUERY = Opcode.QUERY
+IQUERY = Opcode.IQUERY
+STATUS = Opcode.STATUS
+NOTIFY = Opcode.NOTIFY
+UPDATE = Opcode.UPDATE
+
+### END generated Opcode constants
diff --git a/dns/query.py b/dns/query.py
index c2f619c..d4a3afa 100644
--- a/dns/query.py
+++ b/dns/query.py
@@ -95,10 +95,13 @@ def _compute_times(timeout):
return (now, now + timeout)
-def _wait_for(fd, readable, writable, error, expiration):
+def _wait_for(fd, readable, writable, _, expiration):
# Use the selected selector class to wait for any of the specified
# events. An "expiration" absolute time is converted into a relative
# timeout.
+ #
+ # The unused parameter is 'error', which is always set when
+ # selecting for read or write, and we have no error-only selects.
if readable and isinstance(fd, ssl.SSLSocket) and fd.pending() > 0:
return True
@@ -278,27 +281,24 @@ def https(q, where, timeout=None, port=443, source=None, source_port=0,
raise NoDOH # pragma: no cover
wire = q.to_wire()
- (af, destination, source) = _destination_and_source(where, port,
- source, source_port,
- False)
+ (af, _, source) = _destination_and_source(where, port, source, source_port,
+ False)
transport_adapter = None
headers = {
"accept": "application/dns-message"
}
- try:
- where_af = dns.inet.af_for_address(where)
- if where_af == socket.AF_INET:
+ if af is not None:
+ if af == socket.AF_INET:
url = 'https://{}:{}{}'.format(where, port, path)
- elif where_af == socket.AF_INET6:
+ elif af == socket.AF_INET6:
url = 'https://[{}]:{}{}'.format(where, port, path)
- except ValueError:
- if bootstrap_address is not None:
- split_url = urllib.parse.urlsplit(where)
- headers['Host'] = split_url.hostname
- url = where.replace(split_url.hostname, bootstrap_address)
- transport_adapter = HostHeaderSSLAdapter()
- else:
- url = where
+ elif bootstrap_address is not None:
+ split_url = urllib.parse.urlsplit(where)
+ headers['Host'] = split_url.hostname
+ url = where.replace(split_url.hostname, bootstrap_address)
+ transport_adapter = HostHeaderSSLAdapter()
+ else:
+ url = where
if source is not None:
# set source port and source address
transport_adapter = SourceAddressAdapter(source)
@@ -917,7 +917,7 @@ def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN,
(expiration is not None and mexpiration > expiration):
mexpiration = expiration
if use_udp:
- (wire, from_address) = _udp_recv(s, 65535, expiration)
+ (wire, _) = _udp_recv(s, 65535, expiration)
else:
ldata = _net_read(s, 2, mexpiration)
(l,) = struct.unpack("!H", ldata)
diff --git a/dns/rcode.py b/dns/rcode.py
index 846bf6d..49fee69 100644
--- a/dns/rcode.py
+++ b/dns/rcode.py
@@ -72,7 +72,6 @@ class Rcode(dns.enum.IntEnum):
def _unknown_exception_class(cls):
return UnknownRcode
-globals().update(Rcode.__members__)
class UnknownRcode(dns.exception.DNSException):
"""A DNS rcode is unknown."""
@@ -137,3 +136,29 @@ def to_text(value, tsig=False):
if tsig and value == Rcode.BADVERS:
return 'BADSIG'
return Rcode.to_text(value)
+
+### BEGIN generated Rcode constants
+
+NOERROR = Rcode.NOERROR
+FORMERR = Rcode.FORMERR
+SERVFAIL = Rcode.SERVFAIL
+NXDOMAIN = Rcode.NXDOMAIN
+NOTIMP = Rcode.NOTIMP
+REFUSED = Rcode.REFUSED
+YXDOMAIN = Rcode.YXDOMAIN
+YXRRSET = Rcode.YXRRSET
+NXRRSET = Rcode.NXRRSET
+NOTAUTH = Rcode.NOTAUTH
+NOTZONE = Rcode.NOTZONE
+DSOTYPENI = Rcode.DSOTYPENI
+BADVERS = Rcode.BADVERS
+BADSIG = Rcode.BADSIG
+BADKEY = Rcode.BADKEY
+BADTIME = Rcode.BADTIME
+BADMODE = Rcode.BADMODE
+BADNAME = Rcode.BADNAME
+BADALG = Rcode.BADALG
+BADTRUNC = Rcode.BADTRUNC
+BADCOOKIE = Rcode.BADCOOKIE
+
+### END generated Rcode constants
diff --git a/dns/rdataclass.py b/dns/rdataclass.py
index 7943a95..41bba69 100644
--- a/dns/rdataclass.py
+++ b/dns/rdataclass.py
@@ -48,7 +48,6 @@ class RdataClass(dns.enum.IntEnum):
def _unknown_exception_class(cls):
return UnknownRdataclass
-globals().update(RdataClass.__members__)
_metaclasses = {RdataClass.NONE, RdataClass.ANY}
@@ -100,3 +99,17 @@ def is_metaclass(rdclass):
if rdclass in _metaclasses:
return True
return False
+
+### BEGIN generated RdataClass constants
+
+RESERVED0 = RdataClass.RESERVED0
+IN = RdataClass.IN
+INTERNET = RdataClass.INTERNET
+CH = RdataClass.CH
+CHAOS = RdataClass.CHAOS
+HS = RdataClass.HS
+HESIOD = RdataClass.HESIOD
+NONE = RdataClass.NONE
+ANY = RdataClass.ANY
+
+### END generated RdataClass constants
diff --git a/dns/rdataset.py b/dns/rdataset.py
index 8e70a08..0e47139 100644
--- a/dns/rdataset.py
+++ b/dns/rdataset.py
@@ -87,7 +87,7 @@ class Rdataset(dns.set.Set):
elif ttl < self.ttl:
self.ttl = ttl
- def add(self, rd, ttl=None):
+ def add(self, rd, ttl=None): # pylint: disable=arguments-differ
"""Add the specified rdata to the rdataset.
If the optional *ttl* parameter is supplied, then
diff --git a/dns/rdatatype.py b/dns/rdatatype.py
index c793d5a..740752e 100644
--- a/dns/rdatatype.py
+++ b/dns/rdatatype.py
@@ -115,8 +115,6 @@ class RdataType(dns.enum.IntEnum):
_registered_by_text = {}
_registered_by_value = {}
-globals().update(RdataType.__members__)
-
_metatypes = {RdataType.OPT}
_singletons = {RdataType.SOA, RdataType.NXT, RdataType.DNAME,
@@ -219,3 +217,81 @@ def register_type(rdtype, rdtype_text, is_singleton=False):
_registered_by_value[rdtype] = rdtype_text
if is_singleton:
_singletons.add(rdtype)
+
+### BEGIN generated RdataType constants
+
+TYPE0 = RdataType.TYPE0
+NONE = RdataType.NONE
+A = RdataType.A
+NS = RdataType.NS
+MD = RdataType.MD
+MF = RdataType.MF
+CNAME = RdataType.CNAME
+SOA = RdataType.SOA
+MB = RdataType.MB
+MG = RdataType.MG
+MR = RdataType.MR
+NULL = RdataType.NULL
+WKS = RdataType.WKS
+PTR = RdataType.PTR
+HINFO = RdataType.HINFO
+MINFO = RdataType.MINFO
+MX = RdataType.MX
+TXT = RdataType.TXT
+RP = RdataType.RP
+AFSDB = RdataType.AFSDB
+X25 = RdataType.X25
+ISDN = RdataType.ISDN
+RT = RdataType.RT
+NSAP = RdataType.NSAP
+NSAP_PTR = RdataType.NSAP_PTR
+SIG = RdataType.SIG
+KEY = RdataType.KEY
+PX = RdataType.PX
+GPOS = RdataType.GPOS
+AAAA = RdataType.AAAA
+LOC = RdataType.LOC
+NXT = RdataType.NXT
+SRV = RdataType.SRV
+NAPTR = RdataType.NAPTR
+KX = RdataType.KX
+CERT = RdataType.CERT
+A6 = RdataType.A6
+DNAME = RdataType.DNAME
+OPT = RdataType.OPT
+APL = RdataType.APL
+DS = RdataType.DS
+SSHFP = RdataType.SSHFP
+IPSECKEY = RdataType.IPSECKEY
+RRSIG = RdataType.RRSIG
+NSEC = RdataType.NSEC
+DNSKEY = RdataType.DNSKEY
+DHCID = RdataType.DHCID
+NSEC3 = RdataType.NSEC3
+NSEC3PARAM = RdataType.NSEC3PARAM
+TLSA = RdataType.TLSA
+HIP = RdataType.HIP
+NINFO = RdataType.NINFO
+CDS = RdataType.CDS
+CDNSKEY = RdataType.CDNSKEY
+OPENPGPKEY = RdataType.OPENPGPKEY
+CSYNC = RdataType.CSYNC
+SPF = RdataType.SPF
+UNSPEC = RdataType.UNSPEC
+EUI48 = RdataType.EUI48
+EUI64 = RdataType.EUI64
+TKEY = RdataType.TKEY
+TSIG = RdataType.TSIG
+IXFR = RdataType.IXFR
+AXFR = RdataType.AXFR
+MAILB = RdataType.MAILB
+MAILA = RdataType.MAILA
+ANY = RdataType.ANY
+URI = RdataType.URI
+CAA = RdataType.CAA
+AVC = RdataType.AVC
+AMTRELAY = RdataType.AMTRELAY
+TA = RdataType.TA
+DLV = RdataType.DLV
+
+### END generated RdataType constants
diff --git a/dns/rdtypes/CH/A.py b/dns/rdtypes/CH/A.py
index 330fcae..0cb89e2 100644
--- a/dns/rdtypes/CH/A.py
+++ b/dns/rdtypes/CH/A.py
@@ -15,9 +15,10 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-import dns.rdtypes.mxbase
import struct
+import dns.rdtypes.mxbase
+
class A(dns.rdata.Rdata):
"""A record for Chaosnet"""
diff --git a/dns/rdtypes/dnskeybase.py b/dns/rdtypes/dnskeybase.py
index 0243d6f..935ebeb 100644
--- a/dns/rdtypes/dnskeybase.py
+++ b/dns/rdtypes/dnskeybase.py
@@ -31,8 +31,6 @@ class Flag(enum.IntFlag):
REVOKE = 0x0080
ZONE = 0x0100
-globals().update(Flag.__members__)
-
class DNSKEYBase(dns.rdata.Rdata):
@@ -72,3 +70,11 @@ class DNSKEYBase(dns.rdata.Rdata):
key = parser.get_remaining()
return cls(rdclass, rdtype, header[0], header[1], header[2],
key)
+
+### BEGIN generated Flag constants
+
+SEP = Flag.SEP
+REVOKE = Flag.REVOKE
+ZONE = Flag.ZONE
+
+### END generated Flag constants
diff --git a/dns/resolver.py b/dns/resolver.py
index 9600c8e..eba0b44 100644
--- a/dns/resolver.py
+++ b/dns/resolver.py
@@ -43,6 +43,7 @@ import dns.reversename
import dns.tsig
if sys.platform == 'win32':
+ # pylint: disable=import-error
import winreg
class NXDOMAIN(dns.exception.DNSException):
@@ -50,7 +51,10 @@ class NXDOMAIN(dns.exception.DNSException):
supp_kwargs = {'qnames', 'responses'}
fmt = None # we have our own __str__ implementation
- def _check_kwargs(self, qnames, responses=None):
+ # pylint: disable=arguments-differ
+
+ def _check_kwargs(self, qnames,
+ responses=None):
if not isinstance(qnames, (list, tuple, set)):
raise AttributeError("qnames must be a list, tuple or set")
if len(qnames) == 0:
@@ -161,6 +165,7 @@ class NoNameservers(dns.exception.DNSException):
def _fmt_kwargs(self, **kwargs):
srv_msgs = []
for err in kwargs['errors']:
+ # pylint: disable=bad-continuation
srv_msgs.append('Server {} {} port {} answered {}'.format(err[0],
'TCP' if err[1] else 'UDP', err[2], err[3]))
return super()._fmt_kwargs(query=kwargs['request'].question,
@@ -854,34 +859,36 @@ class Resolver:
self.search.append(dns.name.from_text(s))
def _config_win32_fromkey(self, key, always_try_domain):
+ # pylint: disable=undefined-variable
+ # (disabled for WindowsError)
try:
- servers, rtype = winreg.QueryValueEx(key, 'NameServer')
- except WindowsError: # pylint: disable=undefined-variable
+ servers, _ = winreg.QueryValueEx(key, 'NameServer')
+ except WindowsError: # pragma: no cover
servers = None
if servers:
self._config_win32_nameservers(servers)
if servers or always_try_domain:
try:
- dom, rtype = winreg.QueryValueEx(key, 'Domain')
+ dom, _ = winreg.QueryValueEx(key, 'Domain')
if dom:
self._config_win32_domain(dom) # pragma: no cover
except WindowsError: # pragma: no cover
pass
else:
try:
- servers, rtype = winreg.QueryValueEx(key, 'DhcpNameServer')
+ servers, _ = winreg.QueryValueEx(key, 'DhcpNameServer')
except WindowsError: # pragma: no cover
servers = None
if servers:
self._config_win32_nameservers(servers)
try:
- dom, rtype = winreg.QueryValueEx(key, 'DhcpDomain')
+ dom, _ = winreg.QueryValueEx(key, 'DhcpDomain')
if dom:
self._config_win32_domain(dom)
except WindowsError: # pragma: no cover
pass
try:
- search, rtype = winreg.QueryValueEx(key, 'SearchList')
+ search, _ = winreg.QueryValueEx(key, 'SearchList')
except WindowsError: # pragma: no cover
search = None
if search: # pragma: no cover
@@ -909,6 +916,7 @@ class Resolver:
try:
guid = winreg.EnumKey(interfaces, i)
i += 1
+ # XXXRTH why do we get this key and then not use it?
key = winreg.OpenKey(interfaces, guid)
if not self._win32_is_nic_enabled(lm, guid, key):
continue
@@ -923,8 +931,7 @@ class Resolver:
finally:
lm.Close()
- def _win32_is_nic_enabled(self, lm, guid,
- interface_key):
+ def _win32_is_nic_enabled(self, lm, guid, _):
# Look in the Windows Registry to determine whether the network
# interface corresponding to the given guid is enabled.
#
@@ -1027,7 +1034,7 @@ class Resolver:
def resolve(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
tcp=False, source=None, raise_on_no_answer=True, source_port=0,
- lifetime=None, search=None):
+ lifetime=None, search=None): # pylint: disable=arguments-differ
"""Query nameservers to find the answer to the question.
The *qname*, *rdtype*, and *rdclass* parameters may be objects
@@ -1165,6 +1172,8 @@ class Resolver:
rdclass=dns.rdataclass.IN,
*args, **kwargs)
+ # pylint: disable=redefined-outer-name
+
def canonical_name(self, name):
"""Determine the canonical name of *name*.
@@ -1186,6 +1195,8 @@ class Resolver:
canonical_name = e.canonical_name
return canonical_name
+ # pylint: enable=redefined-outer-name
+
def use_tsig(self, keyring, keyname=None,
algorithm=dns.tsig.default_algorithm):
"""Add a TSIG signature to each query.
@@ -1405,7 +1416,7 @@ def _getaddrinfo(host=None, service=None, family=socket.AF_UNSPEC, socktype=0,
raise socket.gaierror(socket.EAI_NONAME, 'Name or service not known')
v6addrs = []
v4addrs = []
- canonical_name = None
+ canonical_name = None # pylint: disable=redefined-outer-name
# Is host None or an address literal? If so, use the system's
# getaddrinfo().
if host is None:
@@ -1571,7 +1582,7 @@ def _gethostbyaddr(ip):
'Name or service not known')
sockaddr = (ip, 80)
family = socket.AF_INET
- (name, port) = _getnameinfo(sockaddr, socket.NI_NAMEREQD)
+ (name, _) = _getnameinfo(sockaddr, socket.NI_NAMEREQD)
aliases = []
addresses = []
tuples = _getaddrinfo(name, 0, family, socket.SOCK_STREAM, socket.SOL_TCP,
diff --git a/dns/rrset.py b/dns/rrset.py
index 68136f4..13737d3 100644
--- a/dns/rrset.py
+++ b/dns/rrset.py
@@ -69,13 +69,17 @@ class RRset(dns.rdataset.Rdataset):
return self.to_text()
def __eq__(self, other):
- if not isinstance(other, RRset):
- return False
- if self.name != other.name:
+ if isinstance(other, RRset):
+ if self.name != other.name:
+ return False
+ elif not isinstance(other, dns.rdataset.Rdataset):
return False
return super().__eq__(other)
- def match(self, name, rdclass, rdtype, covers, deleting=None):
+ # pylint: disable=arguments-differ
+
+ def match(self, name, rdclass, rdtype, covers,
+ deleting=None):
"""Returns ``True`` if this rrset matches the specified class, type,
covers, and deletion state.
"""
@@ -86,7 +90,8 @@ class RRset(dns.rdataset.Rdataset):
return False
return True
- def to_text(self, origin=None, relativize=True, **kw):
+ def to_text(self, origin=None, relativize=True,
+ **kw):
"""Convert the RRset into DNS master file format.
See ``dns.name.Name.choose_relativity`` for more information
@@ -106,7 +111,8 @@ class RRset(dns.rdataset.Rdataset):
return super().to_text(self.name, origin, relativize,
self.deleting, **kw)
- def to_wire(self, file, compress=None, origin=None, **kw):
+ def to_wire(self, file, compress=None, origin=None,
+ **kw):
"""Convert the RRset to wire format.
All keyword arguments are passed to ``dns.rdataset.to_wire()``; see
@@ -118,6 +124,8 @@ class RRset(dns.rdataset.Rdataset):
return super().to_wire(self.name, file, compress, origin,
self.deleting, **kw)
+ # pylint: enable=arguments-differ
+
def to_rdataset(self):
"""Convert an RRset into an Rdataset.
diff --git a/dns/update.py b/dns/update.py
index 8e79650..a541af2 100644
--- a/dns/update.py
+++ b/dns/update.py
@@ -38,8 +38,6 @@ class UpdateSection(dns.enum.IntEnum):
def _maximum(cls):
return 3
-globals().update(UpdateSection.__members__)
-
class UpdateMessage(dns.message.Message):
@@ -310,3 +308,12 @@ class UpdateMessage(dns.message.Message):
# backwards compatibility
Update = UpdateMessage
+
+### BEGIN generated UpdateSection constants
+
+ZONE = UpdateSection.ZONE
+PREREQ = UpdateSection.PREREQ
+UPDATE = UpdateSection.UPDATE
+ADDITIONAL = UpdateSection.ADDITIONAL
+
+### END generated UpdateSection constants
diff --git a/dns/zone.py b/dns/zone.py
index ad597e9..d5bb305 100644
--- a/dns/zone.py
+++ b/dns/zone.py
@@ -904,8 +904,10 @@ class _MasterReader:
# rhs (required)
rhs = token.value
- lmod, lsign, loffset, lwidth, lbase = self._parse_modify(lhs)
- rmod, rsign, roffset, rwidth, rbase = self._parse_modify(rhs)
+ # The code currently only supports base 'd', so the last value
+ # in the tuple _parse_modify returns is ignored
+ lmod, lsign, loffset, lwidth, _ = self._parse_modify(lhs)
+ rmod, rsign, roffset, rwidth, _ = self._parse_modify(rhs)
for i in range(start, stop + 1, step):
# +1 because bind is inclusive and python is exclusive
diff --git a/pylintrc b/pylintrc
index 2a7f718..809d654 100644
--- a/pylintrc
+++ b/pylintrc
@@ -9,7 +9,6 @@ jobs=0
enable=
all,
- python3
# It's worth looking at len-as-condition for optimization, but it's disabled
# here as it is not a correctness thing. Similarly eq-without-hash is
@@ -18,36 +17,26 @@ enable=
disable=
R,
I,
- anomalous-backslash-in-string,
- arguments-differ,
- assigning-non-slot,
- bad-builtin,
- bad-continuation,
broad-except,
- deprecated-method,
fixme,
global-statement,
invalid-name,
- missing-docstring,
+ missing-module-docstring,
+ missing-class-docstring,
+ missing-function-docstring,
no-absolute-import,
- no-member,
+ no-member, # We'd like to use this, but our immutability is too hard for it
protected-access,
redefined-builtin,
too-many-lines,
- unused-argument,
- unused-variable,
- wrong-import-order,
- wrong-import-position,
- len-as-condition,
- eq-without-hash,
- next-method-defined,
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html. You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
-output-format=colorized
+#output-format=colorized
+output-format=parseable
# Tells whether to display a full report or only the messages
reports=no
diff --git a/tests/test_constants.py b/tests/test_constants.py
new file mode 100644
index 0000000..e818bb9
--- /dev/null
+++ b/tests/test_constants.py
@@ -0,0 +1,38 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import unittest
+
+import dns.dnssec
+import dns.rdtypes.dnskeybase
+import dns.flags
+import dns.rcode
+import dns.opcode
+import dns.message
+import dns.update
+import dns.edns
+
+import tests.util
+
+
+class ConstantsTestCase(unittest.TestCase):
+
+ def test_dnssec_constants(self):
+ tests.util.check_enum_exports(dns.dnssec, self.assertEqual,
+ only={dns.dnssec.Algorithm})
+ tests.util.check_enum_exports(dns.rdtypes.dnskeybase, self.assertEqual)
+
+ def test_flags_constants(self):
+ tests.util.check_enum_exports(dns.flags, self.assertEqual)
+ tests.util.check_enum_exports(dns.rcode, self.assertEqual)
+ tests.util.check_enum_exports(dns.opcode, self.assertEqual)
+
+ def test_message_constants(self):
+ tests.util.check_enum_exports(dns.message, self.assertEqual)
+ tests.util.check_enum_exports(dns.update, self.assertEqual)
+
+ def test_rdata_constants(self):
+ tests.util.check_enum_exports(dns.rdataclass, self.assertEqual)
+ tests.util.check_enum_exports(dns.rdatatype, self.assertEqual)
+
+ def test_edns_constants(self):
+ tests.util.check_enum_exports(dns.edns, self.assertEqual)
diff --git a/tests/util.py b/tests/util.py
index df736df..644a88a 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -15,7 +15,26 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+import enum
+import inspect
import os.path
def here(filename):
return os.path.join(os.path.dirname(__file__), filename)
+
+def enumerate_module(module, super_class):
+ """Yield module attributes which are subclasses of given class"""
+ for attr_name in dir(module):
+ attr = getattr(module, attr_name)
+ if inspect.isclass(attr) and issubclass(attr, super_class):
+ yield attr
+
+def check_enum_exports(module, eq_callback, only=None):
+ """Make sure module exports all mnemonics from enums"""
+ for attr in enumerate_module(module, enum.Enum):
+ if only is not None and attr not in only:
+ print('SKIP', attr)
+ continue
+ for flag, value in attr.__members__.items():
+ print(module, flag, value)
+ eq_callback(getattr(module, flag), value)
diff --git a/util/constants-tool b/util/constants-tool
new file mode 100755
index 0000000..0fc7cd4
--- /dev/null
+++ b/util/constants-tool
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+from importlib import import_module
+import os
+import sys
+
+enum_names = [
+ 'dns.dnssec.Algorithm',
+ 'dns.edns.OptionType',
+ 'dns.flags.Flag',
+ 'dns.flags.EDNSFlag',
+ 'dns.message.MessageSection',
+ 'dns.opcode.Opcode',
+ 'dns.rcode.Rcode',
+ 'dns.rdataclass.RdataClass',
+ 'dns.rdatatype.RdataType',
+ 'dns.rdtypes.dnskeybase.Flag',
+ 'dns.update.UpdateSection',
+]
+
+def generate():
+ for enum_name in enum_names:
+ dot = enum_name.rindex('.')
+ module_name = enum_name[:dot]
+ type_name = enum_name[dot + 1:]
+ lname = type_name.lower()
+ mod = import_module(module_name)
+ enum = getattr(mod, type_name)
+ filename = module_name.replace('.', '/') + '.py'
+ new_filename = filename + '.new'
+ with open(filename) as f:
+ with open(new_filename, 'w') as nf:
+ lines = f.readlines()
+ found = False
+ i = 0
+ length = len(lines)
+ while i < length:
+ l = lines[i].rstrip()
+ i += 1
+ if l.startswith(f'### BEGIN generated {type_name} ' +
+ 'constants') or \
+ l.startswith(f'### BEGIN generated {lname} constants'):
+ found = True
+ break
+ else:
+ print(l, file=nf)
+ if found:
+ found = False
+ while i < length:
+ l = lines[i].rstrip()
+ i += 1
+ if l.startswith(f'### END generated {type_name} ' +
+ 'constants') or \
+ l.startswith(f'### END generated {lname} constants'):
+ found = True
+ break
+ if not found:
+ print(f'Found begin marker for {type_name} but did ' +
+ 'not find end marker!', file=sys.stderr)
+ sys.exit(1)
+ if not found:
+ print('', file=nf)
+ print(f'### BEGIN generated {type_name} constants', file=nf)
+ print('', file=nf)
+ # We have to use __members__.items() and not "in enum" because
+ # otherwise we miss values that have multiple names (e.g. NONE
+ # and TYPE0 for rdatatypes).
+ for name, value in enum.__members__.items():
+ print(f'{name} = {type_name}.{name}', file=nf)
+ print('', file=nf)
+ print(f'### END generated {type_name} constants', file=nf)
+ # Copy remaining lines (if any)
+ while i < length:
+ l = lines[i].rstrip()
+ i += 1
+ print(l, file=nf)
+ os.rename(new_filename, filename)
+
+def main():
+ generate()
+
+if __name__ == '__main__':
+ main()