Remove compatibility hacks for dnspython<2.7.0

The minimum required dnspython version is now 2.7.0 and those
compatibility hacks can be dropped.

(cherry picked from commit ce385d8100)
This commit is contained in:
Nicki Křížek 2025-12-30 14:29:51 +01:00
parent a78d19c597
commit 5c931d8045
60 changed files with 37 additions and 231 deletions

View file

@ -17,9 +17,6 @@ import dns.message
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
import isctest
from isctest.instance import NamedInstance

View file

@ -28,13 +28,6 @@ from isctest.asyncserver import (
ResponseAction,
)
try:
dns_namerelation_equal = dns.name.NameRelation.EQUAL
dns_namerelation_subdomain = dns.name.NameRelation.SUBDOMAIN
except AttributeError: # dnspython < 2.3.0 compat
dns_namerelation_equal = dns.name.NAMERELN_EQUAL # type: ignore
dns_namerelation_subdomain = dns.name.NAMERELN_SUBDOMAIN # type: ignore
def get_dname_rrset_at_name(
zone: dns.zone.Zone, name: dns.name.Name
@ -94,11 +87,11 @@ class Cve202125215(DomainHandler):
relation, _, _ = qctx.qname.fullcompare(self_example_dname)
if relation in (dns_namerelation_equal, dns_namerelation_subdomain):
if relation in (dns.name.NameRelation.EQUAL, dns.name.NameRelation.SUBDOMAIN):
del qctx.response.authority[:]
qctx.response.set_rcode(dns.rcode.NOERROR)
if relation == dns_namerelation_subdomain:
if relation == dns.name.NameRelation.SUBDOMAIN:
qctx.response.answer.append(dname_rrset)
cname_rrset = dns.rrset.from_text(
qctx.qname,

View file

@ -11,9 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[
"dig.out.*",

View file

@ -21,7 +21,6 @@ import time
import isctest
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import dns.exception
import dns.message
import dns.name

View file

@ -13,8 +13,6 @@ from re import compile as Re
import pytest
pytest.importorskip("dns", minversion="2.5.0")
import dns.message
import isctest

View file

@ -11,9 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[
"dig.out.*",

View file

@ -11,9 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[
"delv.out.*",

View file

@ -14,9 +14,6 @@
import pytest
import isctest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
import dns.message
pytestmark = pytest.mark.extra_artifacts(

View file

@ -16,9 +16,6 @@ import os
import pytest
pytest.importorskip("cryptography")
pytest.importorskip(
"dns", minversion="2.7.0"
) # dns.dnssec.sign_zone(deterministic=...) needed
from cryptography.hazmat.primitives.asymmetric import ec

View file

@ -11,9 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[
".hypothesis/examples/*",

View file

@ -18,7 +18,6 @@ import isctest
import isctest.mark
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import dns.rrset
pytestmark = [

View file

@ -18,7 +18,7 @@ import time
import pytest
pytest.importorskip("dns")
import dns
import dns.exception
import dns.name
import dns.rdataclass

View file

@ -11,8 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[

View file

@ -11,8 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[

View file

@ -16,7 +16,6 @@ import pytest
import isctest
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[

View file

@ -10,10 +10,6 @@
# information regarding copyright ownership.
import isctest
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
def test_async_hook():

View file

@ -114,7 +114,6 @@ class AsyncServer:
tcp_handler: Optional[_TcpHandler],
pidfile: Optional[str] = None,
) -> None:
self._abort_if_on_dnspython_version_less_than_2_0_0()
logging.basicConfig(
format="%(asctime)s %(levelname)8s %(message)s",
level=os.environ.get("ANS_LOG_LEVEL", "INFO").upper(),
@ -142,14 +141,6 @@ class AsyncServer:
self._pidfile: Optional[str] = pidfile
self._work_done: Optional[asyncio.Future] = None
@classmethod
def _abort_if_on_dnspython_version_less_than_2_0_0(cls) -> None:
if dns.version.MAJOR < 2:
error = f"Using {cls.__name__} requires dnspython >= 2.0.0; "
error += 'add `pytest.importorskip("dns", minversion="2.0.0")` '
error += "to the test module to skip this test."
raise RuntimeError(error)
def _get_ipv4_address_from_directory_name(self) -> str:
containing_directory = pathlib.Path().absolute().stem
match_result = re.match(r"ans(?P<index>\d+)", containing_directory)

View file

@ -14,13 +14,13 @@ import shutil
from typing import cast, List, Optional
import dns.edns
from dns.edns import EDECode, EDEOption
import dns.flags
import dns.message
import dns.rcode
import dns.zone
import isctest.log
from isctest.compat import dns_rcode, EDECode, EDEOption
def rcode(message: dns.message.Message, expected_rcode) -> None:
@ -28,19 +28,19 @@ def rcode(message: dns.message.Message, expected_rcode) -> None:
def noerror(message: dns.message.Message) -> None:
rcode(message, dns_rcode.NOERROR)
rcode(message, dns.rcode.NOERROR)
def notimp(message: dns.message.Message) -> None:
rcode(message, dns_rcode.NOTIMP)
rcode(message, dns.rcode.NOTIMP)
def refused(message: dns.message.Message) -> None:
rcode(message, dns_rcode.REFUSED)
rcode(message, dns.rcode.REFUSED)
def servfail(message: dns.message.Message) -> None:
rcode(message, dns_rcode.SERVFAIL)
rcode(message, dns.rcode.SERVFAIL)
def adflag(message: dns.message.Message) -> None:
@ -83,10 +83,6 @@ def _extract_ede_options(
def noede(message: dns.message.Message) -> None:
"""Check that message contains no EDE option."""
if not hasattr(dns.edns, "EDECode"):
# dnspython<2.2.0 doesn't support EDE, skip check
return
ede_options = _extract_ede_options(message)
assert not ede_options, f"unexpected EDE options {ede_options} in {message}"
@ -95,10 +91,6 @@ def ede(
message: dns.message.Message, code: EDECode, text: Optional[str] = None
) -> None:
"""Check if message contains expected EDE code (and its text)."""
if not hasattr(dns.edns, "EDECode"):
# dnspython<2.2.0 doesn't support EDE, skip check
return
msg_opts = _extract_ede_options(message)
matching_opts = [opt for opt in msg_opts if opt.code == code]
@ -205,7 +197,7 @@ def is_executable(cmd: str, errmsg: str) -> None:
def named_alive(named_proc, resolver_ip):
assert named_proc.poll() is None, "named isn't running"
msg = isctest.query.create("version.bind", "TXT", "CH")
isctest.query.tcp(msg, resolver_ip, expected_rcode=dns_rcode.NOERROR)
isctest.query.tcp(msg, resolver_ip, expected_rcode=dns.rcode.NOERROR)
def notauth(message: dns.message.Message) -> None:

View file

@ -1,70 +0,0 @@
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
#
# SPDX-License-Identifier: MPL-2.0
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
from typing import Any, TYPE_CHECKING
import dns.edns
import dns.rcode
# compatiblity with dnspython<2.0.0
try:
# In dnspython>=2.0.0, dns.rcode.Rcode class is available
# pylint: disable=invalid-name
dns_rcode = dns.rcode.Rcode # type: Any
except AttributeError:
# In dnspython<2.0.0, selected rcodes are available as integers directly
# from dns.rcode
dns_rcode = dns.rcode
if TYPE_CHECKING:
EDECode = dns.edns.EDECode
EDEOption = dns.edns.EDEOption
else:
try: # compatiblity with dnspython<2.2.0
EDECode = dns.edns.EDECode
except AttributeError:
# In dnspython<2.2.0, the dns.edns.EDECode doesn't exist.
#
# The primary use-case is for us to use existing EDECode objects from the
# class, e.g. EDECode.FILTERED. To mimick this behavior, use a string
# factory that just turns the attribute name into a string.
#
# The used compatibility hack doesn't really matter (as long as EDECode.xxx
# doesn't raise exception), as with dnspython versions prior to 2.2.0, any
# EDE checking will be skipped anyway.
class _CompatEDECode:
def __getattr__(self, name: str) -> str:
return name
EDECode = _CompatEDECode()
try:
EDEOption = dns.edns.EDEOption
except AttributeError:
# In dnspython<2.2.0, the dns.edns.EDEOption doesn't exist, so we stub it to be
# able to use it in type annotations.
class EDEOption:
def __new__(cls, *args, **kwargs):
raise RuntimeError("Using EDEOption requires dnspython>=2.2.0")
# pylint: disable=unused-import
try:
from dns.dnssec import DSDigest
except ImportError: # dnspython<2.0.0
import enum
class DSDigest(enum.IntEnum): # type: ignore
"""DNSSEC Delgation Signer Digest Algorithm"""
SHA1 = 1
SHA256 = 2
SHA384 = 4

View file

@ -143,13 +143,8 @@ def dns_names(
RDATACLASS_MAX = RDATATYPE_MAX = 65535
try:
dns_rdataclasses = builds(dns.rdataclass.RdataClass, integers(0, RDATACLASS_MAX))
dns_rdatatypes = builds(dns.rdatatype.RdataType, integers(0, RDATATYPE_MAX))
except AttributeError:
# In old dnspython versions, RDataTypes and RDataClasses are int and not enums.
dns_rdataclasses = integers(0, RDATACLASS_MAX) # type: ignore
dns_rdatatypes = integers(0, RDATATYPE_MAX) # type: ignore
dns_rdataclasses = builds(dns.rdataclass.RdataClass, integers(0, RDATACLASS_MAX))
dns_rdatatypes = builds(dns.rdatatype.RdataType, integers(0, RDATATYPE_MAX))
dns_rdataclasses_without_meta = dns_rdataclasses.filter(dns.rdataclass.is_metaclass)
# NOTE: This should really be `dns_rdatatypes_without_meta = dns_rdatatypes_without_meta.filter(dns.rdatatype.is_metatype()`,

View file

@ -17,7 +17,7 @@ import os
from pathlib import Path
import re
import dns.message
import dns.update
import dns.rcode
from .log import debug, WatchLogFromStart, WatchLogFromHere
@ -116,14 +116,11 @@ class NamedInstance:
return self._rndc(command, timeout=timeout, **kwargs)
def nsupdate(
self, update_msg: dns.message.Message, expected_rcode=dns.rcode.NOERROR
self, update_msg: dns.update.UpdateMessage, expected_rcode=dns.rcode.NOERROR
):
"""
Issue a dynamic update to a server's zone.
"""
# FUTURE update_msg is actually dns.update.UpdateMessage, but it not
# typed properly here in order to support use of this module with
# dnspython<2.0.0
zone = str(update_msg.zone[0].name) # type: ignore[attr-defined]
try:
response = udp(

View file

@ -25,12 +25,9 @@ import dns.rdatatype
import dns.rrset
import dns.tsig
import pytest
import isctest.log
import isctest.query
import isctest.util
from isctest.compat import DSDigest
from isctest.instance import NamedInstance
from isctest.template import TrustAnchor
from isctest.vars.algorithms import Algorithm, ALL_ALGORITHMS_BY_NUM
@ -444,7 +441,6 @@ class Key:
@property
def dnskey(self) -> dns.rrset.RRset:
pytest.importorskip("dns", minversion="2.2.0") # dns.zonefile.read_rrsets
with open(self.keyfile, "r", encoding="utf-8") as file:
rrsets = dns.zonefile.read_rrsets(
file.read(),
@ -459,7 +455,7 @@ class Key:
), f"DNSKEY not found in {self.keyfile}"
return dnskey_rr
def into_ta(self, ta_type: str, dsdigest=DSDigest.SHA256) -> TrustAnchor:
def into_ta(self, ta_type: str, dsdigest=dns.dnssec.DSDigest.SHA256) -> TrustAnchor:
dnskey = self.dnskey
if ta_type in ["static-ds", "initial-ds"]:
ds = dns.dnssec.make_ds(dnskey.name, dnskey[0], dsdigest)

View file

@ -59,7 +59,6 @@ class ZoneAnalyzer:
return cls(zonedb)
def __init__(self, zone: dns.zone.Zone):
self._abort_on_old_dnspython()
self.zone = zone
assert self.zone.origin # mypy hack
# based on individual nodes but not relationship between nodes
@ -85,14 +84,6 @@ class ZoneAnalyzer:
.union(self.reachable_dnames)
)
def _abort_on_old_dnspython(self):
if not hasattr(dns.name, "NameRelation"):
raise RuntimeError(
"ZoneAnalyzer requires dnspython>=2.3.0 for dns.name.NameRelation support. "
"Use pytest.importorskip('dns', minversion='2.3.0') to the test module to "
"skip this test."
)
def get_names_with_type(self, rdtype) -> FrozenSet[Name]:
return frozenset(
name for name in self.zone if self.zone.get_rdataset(name, rdtype)

View file

@ -17,7 +17,6 @@ import dns.query
import dns.message
import isctest.log
from isctest.compat import dns_rcode
QUERY_TIMEOUT = 10
@ -30,7 +29,7 @@ def generic_query(
source: Optional[str] = None,
timeout: int = QUERY_TIMEOUT,
attempts: int = 10,
expected_rcode: dns_rcode = None,
expected_rcode: Optional[dns.rcode.Rcode] = None,
verify: bool = False,
log_query: bool = True,
log_response: bool = True,
@ -77,9 +76,9 @@ def generic_query(
time.sleep(1)
if expected_rcode is not None:
last_rcode = dns_rcode.to_text(res.rcode()) if res else None
last_rcode = dns.rcode.to_text(res.rcode()) if res else None
isctest.log.debug(
f"isc.query.{query_func.__name__}(): expected rcode={dns_rcode.to_text(expected_rcode)}, last rcode={last_rcode}"
f"isc.query.{query_func.__name__}(): expected rcode={dns.rcode.to_text(expected_rcode)}, last rcode={last_rcode}"
)
raise dns.exception.Timeout

View file

@ -11,9 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[
"dig.out*",

View file

@ -20,7 +20,6 @@ import dns
import dns.update
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import isctest
import isctest.mark
from isctest.kasp import (

View file

@ -14,9 +14,6 @@ import itertools
import isctest
import pytest
# Everything from getting a big answer to creating an RR set with thousands
# of records takes minutes of CPU and real time with dnspython < 2.0.0.
pytest.importorskip("dns", minversion="2.0.0")
import dns.rrset

View file

@ -10,12 +10,10 @@
# information regarding copyright ownership.
import os
from datetime import timedelta
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import isctest
import isctest.mark

View file

@ -15,7 +15,6 @@ from re import compile as Re
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import dns
import dns.update

View file

@ -9,10 +9,6 @@
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
import pytest
pytest.importorskip("dns", minversion="2.7.0")
import isctest

View file

@ -18,7 +18,6 @@ from typing import Container, Iterable, Optional, Set, Tuple
import pytest
pytest.importorskip("dns", minversion="2.5.0")
import dns.dnssec
import dns.message
import dns.name

View file

@ -15,10 +15,10 @@ import os
import shutil
import time
import dns
import dns.update
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import isctest
import isctest.mark
from isctest.vars.algorithms import RSASHA1

View file

@ -13,10 +13,10 @@
import os
import dns
import dns.update
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import isctest
import isctest.mark
from isctest.vars.algorithms import RSASHA1

View file

@ -15,10 +15,10 @@ import os
import shutil
import time
import dns
import dns.update
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import isctest
import isctest.mark
from isctest.vars.algorithms import RSASHA1

View file

@ -15,9 +15,9 @@ import os
import shutil
import time
import dns
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import isctest
from nsec3.common import (
ALGORITHM,

View file

@ -13,10 +13,10 @@
import os
import dns
import dns.update
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import isctest
import isctest.mark
from nsec3.common import (

View file

@ -11,15 +11,14 @@
# pylint: disable=redefined-outer-name,unused-import
from datetime import timedelta
import os
import shutil
from datetime import timedelta
import dns
import dns.update
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import isctest
import isctest.mark
from isctest.vars.algorithms import RSASHA256

View file

@ -13,9 +13,6 @@ import platform
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[
"Kxxx*",

View file

@ -19,7 +19,7 @@ import sys
import isctest
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import dns
import dns.exception
import dns.message
import dns.name

View file

@ -11,8 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[

View file

@ -16,7 +16,7 @@ import os
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import dns
import dns.update
import isctest

View file

@ -15,12 +15,11 @@ import os
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import dns
import dns.rcode
import dns.rrset
import isctest
from isctest.compat import dns_rcode
pytestmark = pytest.mark.extra_artifacts(
@ -78,13 +77,13 @@ def test_rpz_multiple_views(qname, source, rcode):
msg,
ip="10.53.0.3",
source="10.53.0.2",
expected_rcode=dns_rcode.NOERROR,
expected_rcode=dns.rcode.NOERROR,
)
isctest.query.tcp(
msg,
ip="10.53.0.3",
source="10.53.0.5",
expected_rcode=dns_rcode.NOERROR,
expected_rcode=dns.rcode.NOERROR,
)
msg = isctest.query.create(qname, "A")

View file

@ -11,8 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[

View file

@ -11,9 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
import isctest.mark
pytestmark = [

View file

@ -19,6 +19,7 @@ import collections
import itertools
from pathlib import Path
import dns
import dns.name
from dns.name import Name
import pytest
@ -26,7 +27,6 @@ import pytest
import isctest
import isctest.name
pytest.importorskip("dns", minversion="2.3.0")
# set of properies present in the tested zone - read by tests_zone_analyzer.py
CATEGORIES = frozenset(

View file

@ -21,7 +21,7 @@ import time
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import dns
import dns.exception
import isctest

View file

@ -11,8 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[

View file

@ -14,13 +14,13 @@
from datetime import datetime
import pytest
import requests
import isctest.mark
pytest.register_assert_rewrite("generic")
import generic
requests = pytest.importorskip("requests")
pytestmark = [
isctest.mark.with_json_c,

View file

@ -15,13 +15,13 @@ from datetime import datetime
import xml.etree.ElementTree as ET
import pytest
import requests
import isctest.mark
pytest.register_assert_rewrite("generic")
import generic
requests = pytest.importorskip("requests")
pytestmark = [
isctest.mark.with_libxml2,

View file

@ -19,7 +19,7 @@ import time
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import dns
import dns.message
import dns.query

View file

@ -18,7 +18,7 @@ import time
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import dns
import dns.edns
import dns.message
import dns.name

View file

@ -19,7 +19,6 @@ from isctest.hypothesis.strategies import dns_names
from hypothesis import strategies, given, settings
pytest.importorskip("dns.dnssectypes")
from dns.dnssectypes import NSEC3Hash
import dns.dnssec

View file

@ -18,9 +18,6 @@ import time
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
import dns.message
import dns.query
import dns.tsigkeyring

View file

@ -21,9 +21,6 @@ pytestmark = pytest.mark.extra_artifacts(
]
)
# TSIG queries not supported by isctest.asyncserver with old dnspython
pytest.importorskip("dns", minversion="2.0.0")
def test_tsig(run_tests_sh):
run_tests_sh()

View file

@ -15,8 +15,6 @@ import time
import pytest
pytest.importorskip("dns", minversion="2.7.0") # TSIG parsing without validation
import dns.exception
import dns.message
import dns.name

View file

@ -24,7 +24,7 @@ import pytest
import isctest
pytest.importorskip("dns")
import dns
import dns.message
import dns.name
import dns.rdata

View file

@ -11,8 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[

View file

@ -29,7 +29,7 @@ Limitations - untested properties:
import pytest
pytest.importorskip("dns", minversion="2.0.0")
import dns
import dns.message
import dns.name
import dns.query

View file

@ -11,8 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[

View file

@ -11,8 +11,6 @@
import pytest
# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")
pytestmark = pytest.mark.extra_artifacts(
[