Commit 2e0b167c2f for bind
commit 2e0b167c2f637c2791ea313b885efe6b9ca669e3
Author: Nicki Křížek <nicki@isc.org>
Date: Wed Oct 21 15:05:24 2026 +0000
Make VarLookup a read-only Mapping
Subclassing ChainMap left VarLookup open to del, pop, clear and |=,
all of which modify the first of the coalesced module dicts, and makes
pylint 4.1 flag ALL as a dangerous default value in TemplateEngine.
Assisted-by: Claude:claude-opus-5-5
diff --git a/bin/tests/system/isctest/vars/all.py b/bin/tests/system/isctest/vars/all.py
index 0726326359..465dbf747b 100644
--- a/bin/tests/system/isctest/vars/all.py
+++ b/bin/tests/system/isctest/vars/all.py
@@ -9,7 +9,8 @@
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
-from collections import ChainMap
+from collections.abc import Iterator, Mapping
+from typing import Any
from .algorithms import ALG_VARS, CRYPTO_SUPPORTED_VARS
from .basic import BASIC_VARS
@@ -20,37 +21,37 @@ from .openssl import OPENSSL_VARS
from .ports import PORT_VARS
-class VarLookup(ChainMap):
+class VarLookup(Mapping[str, Any]):
"""
- A dictionary-like structure to coalesce the variables from different
- modules without making a copy (which would prevent updating these values
- from inside the modules). Values which are None are treated as unset when
+ A read-only mapping to coalesce the variables from different modules
+ without making a copy (which would prevent updating these values from
+ inside the modules). Values which are None are treated as unset when
iterating.
"""
- def __init__(self, *maps):
- keys = set()
+ def __init__(self, *maps: Mapping[str, str | None]):
+ keys: set[str] = set()
for m in maps:
overlap = keys.intersection(m.keys())
if overlap:
raise RuntimeError(f"key(s) are defined multiple times: {overlap}")
keys = keys.union(m.keys())
- super().__init__(*maps)
+ self._maps = maps
- def __setitem__(self, *args, **kwargs):
- raise RuntimeError("read-only structure")
+ def __getitem__(self, key: str) -> Any:
+ for m in self._maps:
+ if key in m:
+ return m[key]
+ raise KeyError(key)
- def keys(self):
- result = set()
- for m in self.maps:
+ def __iter__(self) -> Iterator[str]:
+ for m in self._maps:
for key, val in m.items():
- if val is None: # treat None as unset
- continue
- result.add(key)
- return list(result)
+ if val is not None:
+ yield key
- def __iter__(self):
- return iter(self.keys())
+ def __len__(self) -> int:
+ return sum(1 for _ in self)
ALL = VarLookup(