Commit 8976c6867c for bind

commit 8976c6867c4d63d8c437b32ccf884c671f95a846
Author: Štěpán Balážik <stepan@isc.org>
Date:   Tue Sep 8 14:22:21 2026 +0200

    Let response handlers declare a matcher

    A handler declares the queries it handles in its `matcher`, which the
    default match() defers to.  A handler overriding match() silently
    takes precedence for the time being, so that the existing handlers
    keep working while they are converted one by one, until match()
    itself goes.

    Assisted-by: Claude:claude-fable-5

diff --git a/bin/tests/system/isctest/asyncserver/__init__.py b/bin/tests/system/isctest/asyncserver/__init__.py
index e2a81779c8..3f5673f41a 100644
--- a/bin/tests/system/isctest/asyncserver/__init__.py
+++ b/bin/tests/system/isctest/asyncserver/__init__.py
@@ -46,6 +46,7 @@ import isctest.zone

 from .context import DnsProtocol, Peer, QueryContext
 from .dnssec import SigningKey
+from .matchers import Always, Matcher

 __all__ = [
     "AsyncDnsServer",
@@ -264,20 +265,23 @@ class ResponseHandler(abc.ABC):
     """
     Base class for generic response handlers.

-    If a query passes the `match()` function logic, then it is handled by this
-    response handler and response(s) may be generated by the `get_responses()`
-    method.
+    The queries a handler handles are declared in its `matcher`; the first
+    handler whose matcher matches a query handles it, and response(s) may be
+    generated by its `get_responses()` method.  The default matcher handles
+    every query.
     """

-    # pylint: disable=unused-argument
+    matcher: Matcher = Always()
+
     def match(self, qctx: QueryContext) -> bool:
         """
-        Matching logic - the first handler whose `match()` method returns True
-        is used for handling the query.
+        Whether this handler handles the query in `qctx`.

-        The default for each handler is to handle all queries.
+        The default implementation defers to `matcher`, which is how handlers
+        are expected to declare what they handle.  A handler overriding this
+        method still takes precedence, silently, for the time being.
         """
-        return True
+        return self.matcher.match(qctx)

     @abc.abstractmethod
     async def get_responses(
@@ -293,7 +297,9 @@ class ResponseHandler(abc.ABC):
         yield  # pylint: disable=unreachable

     def __str__(self) -> str:
-        return self.__class__.__name__
+        if isinstance(self.matcher, Always):
+            return self.__class__.__name__
+        return f"{self.__class__.__name__} matching {self.matcher}"


 @dataclass