mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-28 04:34:54 -04:00
Clean up imports of dnspython modules
Add a pylint plugin that enforces:
- There is no bare `import dns` statement.
- All `dns.<module>` used are explicitly imported.
- There are no unused `dns.<module>` imports.
Fix all the imports to conform with this check.
(cherry picked from commit d3186c7038)
This commit is contained in:
parent
963ef9cb8e
commit
c04b9251aa
50 changed files with 248 additions and 53 deletions
|
|
@ -13,6 +13,7 @@
|
|||
import time
|
||||
|
||||
import dns.message
|
||||
import dns.rrset
|
||||
import pytest
|
||||
|
||||
from isctest.instance import NamedInstance
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ import os
|
|||
import sys
|
||||
import time
|
||||
|
||||
import dns.exception
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
from re import compile as Re
|
||||
|
||||
import dns.message
|
||||
import dns.rcode
|
||||
import pytest
|
||||
|
||||
import isctest
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
import dns
|
||||
|
||||
import dns.rrset
|
||||
|
||||
import isctest
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@
|
|||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
import dns.flags
|
||||
import dns.message
|
||||
import dns.rcode
|
||||
import pytest
|
||||
|
||||
import isctest
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
from typing import AsyncGenerator
|
||||
|
||||
import dns
|
||||
import dns.rcode
|
||||
|
||||
from isctest.asyncserver import (
|
||||
|
|
|
|||
177
bin/tests/system/dns_import_checker.py
Normal file
177
bin/tests/system/dns_import_checker.py
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
# 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 pylint.checkers import BaseChecker
|
||||
|
||||
import astroid
|
||||
|
||||
|
||||
class DnsExplicitImportsChecker(BaseChecker):
|
||||
name = "dns-explicit-imports"
|
||||
|
||||
msgs = {
|
||||
"W9001": (
|
||||
"Bare 'import dns' is discouraged; import required submodules explicitly",
|
||||
"dns-bare-import",
|
||||
"Emitted when the package root 'dns' is imported directly.",
|
||||
),
|
||||
"W9002": (
|
||||
"Missing explicit import for '%s' (add `import %s`)",
|
||||
"dns-missing-submodule-import",
|
||||
"Emitted when code references dns.<...> but the corresponding module prefix "
|
||||
"was not imported with `import dns.<...>`.",
|
||||
),
|
||||
"W9003": (
|
||||
"Unused explicit import for '%s' (remove `import %s`)",
|
||||
"dns-unused-submodule-import",
|
||||
"Emitted when a dns.<...> module is imported explicitly but not used.",
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self, linter=None):
|
||||
super().__init__(linter)
|
||||
self._imported = {}
|
||||
self._imported_aliases = set()
|
||||
self._required = {}
|
||||
|
||||
def visit_module(self, node): # pylint: disable=unused-argument
|
||||
self._imported = {}
|
||||
self._imported_aliases = set()
|
||||
self._required = {}
|
||||
|
||||
def leave_module(self, node): # pylint: disable=unused-argument
|
||||
for mod, use_node in sorted(self._required.items()):
|
||||
if mod in self._imported:
|
||||
continue
|
||||
prefix = mod + "."
|
||||
if any(name.startswith(prefix) for name in self._imported):
|
||||
continue
|
||||
self.add_message(
|
||||
"dns-missing-submodule-import",
|
||||
node=use_node,
|
||||
args=(mod, mod),
|
||||
)
|
||||
for mod, import_node in sorted(self._imported.items()):
|
||||
if mod in self._imported_aliases:
|
||||
continue
|
||||
if any(
|
||||
name == mod or name.startswith(mod + ".") for name in self._required
|
||||
):
|
||||
continue
|
||||
self.add_message(
|
||||
"dns-unused-submodule-import",
|
||||
node=import_node,
|
||||
args=(mod, mod),
|
||||
)
|
||||
|
||||
def visit_import(self, node):
|
||||
for name, _asname in node.names:
|
||||
if name == "dns":
|
||||
self.add_message("dns-bare-import", node=node)
|
||||
continue
|
||||
if name.startswith("dns."):
|
||||
self._imported.setdefault(name, node)
|
||||
if _asname:
|
||||
self._imported_aliases.add(name)
|
||||
|
||||
def visit_importfrom(self, node): # pylint: disable=unused-argument
|
||||
return
|
||||
|
||||
def visit_attribute(self, node):
|
||||
parent = node.parent
|
||||
# For `dns.a.b.c`, astroid visits intermediate attributes too.
|
||||
# Process only the rightmost node to avoid duplicate bookkeeping.
|
||||
if isinstance(parent, astroid.nodes.Attribute) and parent.expr is node:
|
||||
return
|
||||
|
||||
mod = self._dns_module_for_attribute(node)
|
||||
if mod is None:
|
||||
return
|
||||
|
||||
self._required.setdefault(mod, node)
|
||||
|
||||
@staticmethod
|
||||
def _dns_attribute_nodes(node):
|
||||
"""
|
||||
Return the chain of Attribute nodes as a list.
|
||||
|
||||
For `dns.a.b.c`, return the list of Attribute nodes for `dns.a`, `dns.a.b`, and `dns.a.b.c`.
|
||||
|
||||
Return None if the chain is not rooted in `dns`.
|
||||
"""
|
||||
|
||||
if not isinstance(node, astroid.nodes.Attribute):
|
||||
return None
|
||||
|
||||
nodes = []
|
||||
expr = node
|
||||
while isinstance(expr, astroid.nodes.Attribute):
|
||||
nodes.append(expr)
|
||||
expr = expr.expr
|
||||
|
||||
if not isinstance(expr, astroid.nodes.Name) or expr.name != "dns":
|
||||
return None
|
||||
|
||||
return list(reversed(nodes))
|
||||
|
||||
@classmethod
|
||||
def _dns_module_for_attribute(cls, node):
|
||||
"""
|
||||
For dns.a.b.c, return the longest dns.a.b... prefix that is likely to be a module,
|
||||
or None if the chain is not rooted in dns.
|
||||
"""
|
||||
last_module = None
|
||||
chain_nodes = cls._dns_attribute_nodes(node)
|
||||
if chain_nodes is None:
|
||||
return None
|
||||
|
||||
full = "dns." + ".".join(part.attrname for part in chain_nodes)
|
||||
# Prefer inferred module names to avoid treating classes/constants as
|
||||
# modules (e.g. `dns.name.NameRelation` should resolve to `dns.name`).
|
||||
for chain_node in chain_nodes:
|
||||
inferred = cls._infer_module_name(chain_node)
|
||||
if inferred is not None and full.startswith(inferred):
|
||||
last_module = inferred
|
||||
if last_module is not None:
|
||||
return last_module
|
||||
|
||||
# Fallback when inference is unavailable: assume the terminal segment
|
||||
# is not a module symbol and require the parent path.
|
||||
parts = full.split(".")
|
||||
if len(parts) <= 2:
|
||||
return full
|
||||
return ".".join(parts[:-1])
|
||||
|
||||
@staticmethod
|
||||
def _infer_module_name(node):
|
||||
"""Infer `dns.<module>` for a node; return None if inference is unsure."""
|
||||
try:
|
||||
for inferred in node.infer():
|
||||
if inferred is astroid.util.Uninferable:
|
||||
continue
|
||||
# Inference can return either a Module node directly or another
|
||||
# symbol rooted in a module; normalize both to module name.
|
||||
module = (
|
||||
inferred
|
||||
if isinstance(inferred, astroid.nodes.Module)
|
||||
else inferred.root()
|
||||
)
|
||||
name = module.name
|
||||
if name.startswith("dns."):
|
||||
return name
|
||||
# Inference can fail for dynamic/partial code; fall back gracefully.
|
||||
except astroid.AstroidError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def register(linter):
|
||||
linter.register_checker(DnsExplicitImportsChecker(linter))
|
||||
|
|
@ -17,8 +17,11 @@ import os
|
|||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from dns.rdtypes.dnskeybase import Flag
|
||||
|
||||
import dns
|
||||
import dns.dnssec
|
||||
import dns.name
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.rdtypes.ANY.RRSIG
|
||||
import dns.zone
|
||||
import pytest
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
import os
|
||||
import re
|
||||
|
||||
import dns.rcode
|
||||
import dns.rrset
|
||||
import pytest
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ import struct
|
|||
import subprocess
|
||||
import time
|
||||
|
||||
import dns
|
||||
import dns.exception
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
# information regarding copyright ownership.
|
||||
|
||||
import dns.flags
|
||||
import dns.message
|
||||
import pytest
|
||||
|
||||
import isctest
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
from dns import rdataclass, rdatatype
|
||||
|
||||
import dns
|
||||
import dns.name
|
||||
|
||||
import isctest
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ from filters.common import (
|
|||
prime_cache,
|
||||
)
|
||||
|
||||
import isctest
|
||||
import isctest.mark
|
||||
|
||||
pytestmark = pytest.mark.extra_artifacts(ARTIFACTS)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
# information regarding copyright ownership.
|
||||
|
||||
|
||||
import dns.edns
|
||||
import dns.flags
|
||||
import dns.message
|
||||
import pytest
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ import dns.rdataset
|
|||
import dns.rdatatype
|
||||
import dns.rrset
|
||||
import dns.tsig
|
||||
import dns.version
|
||||
import dns.zone
|
||||
|
||||
_UdpHandler = Callable[
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import dns.edns
|
|||
import dns.flags
|
||||
import dns.message
|
||||
import dns.rcode
|
||||
import dns.rrset
|
||||
import dns.zone
|
||||
|
||||
import isctest.log
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ from hypothesis.strategies import (
|
|||
sampled_from,
|
||||
)
|
||||
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from typing import NamedTuple
|
|||
import os
|
||||
import re
|
||||
|
||||
import dns.exception
|
||||
import dns.rcode
|
||||
import dns.update
|
||||
|
||||
|
|
|
|||
|
|
@ -19,11 +19,17 @@ import os
|
|||
import re
|
||||
import time
|
||||
|
||||
import dns
|
||||
import dns.dnssec
|
||||
import dns.exception
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.rrset
|
||||
import dns.tsig
|
||||
import dns.zone
|
||||
import dns.zonefile
|
||||
|
||||
from isctest.instance import NamedInstance
|
||||
from isctest.template import TrustAnchor
|
||||
|
|
|
|||
|
|
@ -14,8 +14,12 @@ from typing import Any, Callable
|
|||
import os
|
||||
import time
|
||||
|
||||
import dns.exception
|
||||
import dns.flags
|
||||
import dns.message
|
||||
import dns.query
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
|
||||
import isctest.log
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
import dns.rrset
|
||||
import dns.zone
|
||||
import pytest
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,12 @@ import shutil
|
|||
import subprocess
|
||||
import time
|
||||
|
||||
import dns
|
||||
import dns.exception
|
||||
import dns.name
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.tsig
|
||||
import dns.update
|
||||
import pytest
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
import itertools
|
||||
|
||||
import dns.flags
|
||||
import dns.rrset
|
||||
import pytest
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import pytest
|
|||
from isctest.vars.algorithms import Algorithm
|
||||
|
||||
import isctest
|
||||
import isctest.mark
|
||||
|
||||
pytestmark = pytest.mark.extra_artifacts(
|
||||
[
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ from re import compile as Re
|
|||
|
||||
import os
|
||||
|
||||
import dns
|
||||
import dns.name
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.update
|
||||
import pytest
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ from hypothesis import assume, given
|
|||
import dns.dnssec
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.query
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@
|
|||
|
||||
from datetime import timedelta
|
||||
|
||||
import dns
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import pytest
|
||||
|
||||
import isctest
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@
|
|||
import shutil
|
||||
import time
|
||||
|
||||
import dns
|
||||
import dns.update
|
||||
import dns.name
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import pytest
|
||||
|
||||
from isctest.vars.algorithms import Algorithm
|
||||
from nsec3.common import NSEC3_MARK, check_nsec3_case
|
||||
|
||||
import isctest
|
||||
import isctest.mark
|
||||
|
||||
pytestmark = NSEC3_MARK
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
import os
|
||||
|
||||
import dns
|
||||
import dns.rcode
|
||||
import dns.update
|
||||
import pytest
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@
|
|||
import os
|
||||
import time
|
||||
|
||||
import dns
|
||||
import dns.update
|
||||
import dns.name
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import pytest
|
||||
|
||||
from isctest.vars.algorithms import RSASHA1, Algorithm
|
||||
|
|
|
|||
|
|
@ -11,15 +11,13 @@
|
|||
|
||||
import os
|
||||
|
||||
import dns
|
||||
import dns.update
|
||||
import dns.rdatatype
|
||||
import pytest
|
||||
|
||||
from isctest.vars.algorithms import Algorithm
|
||||
from nsec3.common import NSEC3_MARK, check_nsec3_case, check_nsec3param
|
||||
|
||||
import isctest
|
||||
import isctest.mark
|
||||
|
||||
pytestmark = NSEC3_MARK
|
||||
|
||||
|
|
|
|||
|
|
@ -13,14 +13,13 @@ from datetime import timedelta
|
|||
|
||||
import os
|
||||
|
||||
import dns
|
||||
import dns.update
|
||||
import dns.rcode
|
||||
import dns.rdatatype
|
||||
|
||||
from isctest.vars.algorithms import RSASHA256
|
||||
from nsec3.common import NSEC3_MARK, check_auth_nsec3, check_nsec3param
|
||||
|
||||
import isctest
|
||||
import isctest.mark
|
||||
|
||||
pytestmark = NSEC3_MARK
|
||||
|
||||
|
|
|
|||
|
|
@ -16,14 +16,9 @@ import os
|
|||
import re
|
||||
import sys
|
||||
|
||||
import dns
|
||||
import dns.exception
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.query
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.zone
|
||||
import pytest
|
||||
|
||||
import isctest
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ information regarding copyright ownership.
|
|||
|
||||
from typing import AsyncGenerator
|
||||
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.rcode
|
||||
import dns.rdatatype
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ from datetime import timedelta
|
|||
|
||||
import os
|
||||
|
||||
import dns
|
||||
import dns.update
|
||||
|
||||
from isctest.kasp import Iret
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
import os
|
||||
|
||||
import dns
|
||||
import dns.rcode
|
||||
import dns.rrset
|
||||
import pytest
|
||||
|
|
|
|||
|
|
@ -21,11 +21,9 @@ import itertools
|
|||
|
||||
from dns.name import Name
|
||||
|
||||
import dns
|
||||
import dns.name
|
||||
import pytest
|
||||
|
||||
import isctest
|
||||
import isctest.name
|
||||
|
||||
# set of properies present in the tested zone - read by tests_zone_analyzer.py
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import signal
|
|||
import subprocess
|
||||
import time
|
||||
|
||||
import dns
|
||||
import dns.exception
|
||||
import pytest
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
# information regarding copyright ownership.
|
||||
|
||||
import dns.message
|
||||
import dns.rrset
|
||||
import pytest
|
||||
|
||||
import isctest
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ from time import sleep
|
|||
import os
|
||||
|
||||
import dns.message
|
||||
import dns.query
|
||||
import dns.rcode
|
||||
|
||||
import isctest
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
import concurrent.futures
|
||||
import time
|
||||
|
||||
import dns.exception
|
||||
import dns.rcode
|
||||
import dns.update
|
||||
import pytest
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
import os
|
||||
|
||||
import dns.message
|
||||
import dns.rrset
|
||||
import pytest
|
||||
|
||||
import isctest
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@ import socket
|
|||
import struct
|
||||
import time
|
||||
|
||||
import dns
|
||||
import dns.flags
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.query
|
||||
import dns.rrset
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.extra_artifacts(
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
import socket
|
||||
import time
|
||||
|
||||
import dns
|
||||
import dns.edns
|
||||
import dns.message
|
||||
import dns.name
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import time
|
|||
|
||||
import dns.message
|
||||
import dns.query
|
||||
import dns.tsig
|
||||
import dns.tsigkeyring
|
||||
import pytest
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ import argparse
|
|||
import struct
|
||||
import time
|
||||
|
||||
import dns
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.rdata
|
||||
import dns.rdataclass
|
||||
|
|
|
|||
|
|
@ -33,11 +33,7 @@ Limitations - untested properties:
|
|||
|
||||
from hypothesis import assume, example, given, settings
|
||||
|
||||
import dns
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.query
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.rrset
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ import socket
|
|||
import time
|
||||
|
||||
import dns.message
|
||||
import dns.query
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.tsig
|
||||
import dns.zone
|
||||
import pytest
|
||||
|
||||
from isctest.util import param
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import shutil
|
|||
import signal
|
||||
import time
|
||||
|
||||
import dns.message
|
||||
import dns.zone
|
||||
import pytest
|
||||
|
||||
import isctest
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ source-roots = [
|
|||
]
|
||||
load-plugins = [
|
||||
"re_compile_checker",
|
||||
"dns_import_checker",
|
||||
]
|
||||
|
||||
[tool.vulture]
|
||||
|
|
@ -72,6 +73,7 @@ exclude = [
|
|||
"doc/man/conf.py",
|
||||
"isctest",
|
||||
"re_compile_checker.py",
|
||||
"dns_import_checker.py",
|
||||
]
|
||||
ignore_decorators = [
|
||||
"@pytest.fixture",
|
||||
|
|
|
|||
Loading…
Reference in a new issue