commit:     f7a5a96097afdf0ccc4774447b62de328bcf254a
Author:     Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Sun Dec  7 14:55:06 2025 +0000
Commit:     Brian Harring <ferringb <AT> gmail <DOT> com>
CommitDate: Sun Dec  7 14:55:06 2025 +0000
URL:        
https://gitweb.gentoo.org/proj/pkgcore/snakeoil.git/commit/?id=f7a5a960

chore(tests): convert code_quality noise to subtests, thus less noise

Signed-off-by: Brian Harring <ferringb <AT> gmail.com>

 src/snakeoil/test/__init__.py     |   4 +-
 src/snakeoil/test/code_quality.py | 160 +++++++++++++++-----------------------
 2 files changed, 63 insertions(+), 101 deletions(-)

diff --git a/src/snakeoil/test/__init__.py b/src/snakeoil/test/__init__.py
index cbfead8..89bab4c 100644
--- a/src/snakeoil/test/__init__.py
+++ b/src/snakeoil/test/__init__.py
@@ -4,7 +4,7 @@ __all__ = (
     "coverage",
     "hide_imports",
     "Modules",
-    "ParameterizeBase",
+    "NamespaceCollector",
     "protect_process",
     "random_str",
     "Slots",
@@ -18,7 +18,7 @@ import subprocess
 import sys
 from unittest.mock import patch
 
-from .code_quality import Modules, ParameterizeBase, Slots
+from .code_quality import Modules, NamespaceCollector, Slots
 
 
 def random_str(length):

diff --git a/src/snakeoil/test/code_quality.py 
b/src/snakeoil/test/code_quality.py
index d969ac4..a309afe 100644
--- a/src/snakeoil/test/code_quality.py
+++ b/src/snakeoil/test/code_quality.py
@@ -1,6 +1,5 @@
-__all__ = ("ParameterizeBase", "Slots", "Modules")
+__all__ = ("NamespaceCollector", "Slots", "Modules")
 import abc
-import functools
 import inspect
 import typing
 from types import ModuleType
@@ -18,56 +17,28 @@ from snakeoil.python_namespaces import get_submodules_of
 T = typing.TypeVar("T")
 
 
-class ParameterizeBase(typing.Generic[T], abc.ABC):
-    namespaces: typing.ClassVar[tuple[str]] = abstractclassvar(tuple[str])
-    namespace_ignores: tuple[str, ...] = ()
-    strict: tuple[str] | bool = False
-    tests_to_parameterize: typing.ClassVar[tuple[str, ...]] = abstractclassvar(
-        tuple[str]
-    )
-
-    @classmethod
-    @abc.abstractmethod
-    def make_id(cls, /, param: T) -> str: ...
-
-    @classmethod
-    @abc.abstractmethod
-    def collect_parameters(cls, modules: set[ModuleType]) -> 
typing.Iterable[T]: ...
+class maybe_strict_tests(list):
+    def register(self, thing):
+        self.append(thing)
+        return thing
 
-    def __init_subclass__(cls) -> None:
-        super().__init_subclass__()
 
-        if inspect.isabstract(cls):
-            return
-
-        # Inject the parameterization
-        targets = list(
-            sorted(cls.collect_parameters(set(cls.collect_modules())), 
key=cls.make_id)
-        )
-
-        for test in cls.tests_to_parameterize:
-            original = getattr(cls, test)
-
-            @pytest.mark.parametrize(
-                "param",
-                targets,
-                ids=cls.make_id,
-            )
-            @functools.wraps(original)
-            def do_it(self, param: T, original=original):
-                return original(self, param)
+class NamespaceCollector(typing.Generic[T], abc.ABC):
+    namespaces: tuple[str] = abstractclassvar(tuple[str])
+    namespace_ignores: tuple[str, ...] = ()
 
-            if not cls.is_strict_test(test):
-                do_it = pytest.mark.xfail(strict=False)(do_it)
-            setattr(cls, test, do_it)
+    strict_configurable_tests: typing.ClassVar[tuple[str, ...]] = ()
+    strict: typing.ClassVar[typing.Literal[True] | tuple[str, ...]] = ()
 
-        super().__init_subclass__()
+    def __init_subclass__(cls, **kwargs) -> None:
+        if not inspect.isabstract(cls):
+            targets = cls.strict_configurable_tests
+            strict = targets if cls.strict is True else cls.strict
+            for test_name in cls.strict_configurable_tests:
+                if test_name not in strict:
+                    setattr(cls, test_name, pytest.mark.xfail(getattr(cls, 
test_name)))
 
-    @classmethod
-    def is_strict_test(cls, test_name: str) -> bool:
-        if isinstance(cls.strict, bool):
-            return cls.strict
-        return test_name in cls.strict
+        return super().__init_subclass__(**kwargs)
 
     @classmethod
     def collect_modules(cls) -> typing.Iterable[ModuleType]:
@@ -79,79 +50,70 @@ class ParameterizeBase(typing.Generic[T], abc.ABC):
             )
 
 
-class Slots(ParameterizeBase[type]):
+class Slots(NamespaceCollector[type]):
     disable_str: typing.Final = "__slotting_intentionally_disabled__"
     ignored_subclasses: tuple[type, ...] = (
         Exception,
         typing.Protocol,  # pyright: ignore[reportAssignmentType]
     )
 
-    tests_to_parameterize = (
+    strict_configurable_tests = (
         "test_shadowing",
         "test_slots_mandatory",
     )
 
     @classmethod
-    def make_id(cls, param: type) -> str:
-        return f"{param.__module__}.{param.__qualname__}"
-
-    @classmethod
-    def collect_parameters(cls, modules) -> typing.Iterable[type]:
-        modules = set(x.__name__ for x in modules)
+    def collect_classes(cls) -> typing.Iterable[type]:
+        modules = set(x.__name__ for x in cls.collect_modules())
         for target in get_subclasses_of(object):
-            if not cls.ignore_module(target, modules) and not 
cls.ignore_class(target):
+            if cls.__module__ in modules and not cls.ignore_class(target):
                 yield target
 
-    @classmethod
-    def ignore_module(cls, target: type, collected_modules: 
typing.Container[str]):
-        """Override if you need custom logic for the module filter of 
classes"""
-        return target.__module__ not in collected_modules
-
     @classmethod
     def ignore_class(cls, target: type) -> bool:
         """Override this if you need dynamic suppression of which classes to 
ignore"""
         return issubclass(target, cls.ignored_subclasses)
 
-    def test_slots_mandatory(self, param: type):
-        assert get_slot_of(param).slots is not None or getattr(
-            param, self.disable_str, False
-        ), f"class has no slots nor is {self.disable_str} set to True"
-
-    def test_shadowing(self, param: type):
-        if (slots := get_slot_of(param).slots) is None:
-            return
-        assert isinstance(slots, tuple), "__slots__ must be a tuple"
-        slots = set(slots)
-        for slotting in get_slots_of(param):
-            if slotting.cls is param:
-                continue
-            if slotting.slots is not None:
-                assert set() == slots.intersection(slotting.slots), (
-                    f"has slots that shadow {param}"
-                )
-
-
-class Modules(ParameterizeBase[ModuleType]):
-    tests_to_parameterize = (
+    def test_slots_mandatory(self, subtests):
+        for target in self.collect_classes():
+            with subtests.test(cls=target):
+                assert get_slot_of(target).slots is not None or getattr(
+                    target, self.disable_str, False
+                ), f"class has no slots nor is {self.disable_str} set to True"
+
+    def test_shadowing(self, subtests):
+        for target in self.collect_classes():
+            if (slots := get_slot_of(target).slots) is None:
+                return
+            with subtests.test(cls=target):
+                assert isinstance(slots, tuple), "__slots__ must be a tuple"
+                slots = set(slots)
+                for slotting in get_slots_of(target):
+                    if slotting.cls is target:
+                        continue
+                    if slotting.slots is not None:
+                        assert set() == slots.intersection(slotting.slots), (
+                            f"has slots that shadow {target}"
+                        )
+
+
+class Modules(NamespaceCollector[ModuleType]):
+    strict_configurable_tests = (
         "test_has__all__",
         "test_valid__all__",
     )
     strict = ("test_valid__all__",)
 
-    @classmethod
-    def make_id(cls, /, param: ModuleType) -> str:
-        return param.__name__
-
-    @classmethod
-    def collect_parameters(cls, modules) -> typing.Iterable[ModuleType]:
-        return modules
-
-    def test_has__all__(self, param: ModuleType):
-        assert hasattr(param, "__all__"), "__all__ is missing but should exist"
-
-    def test_valid__all__(self, param: ModuleType):
-        if attrs := getattr(param, "__all__", ()):
-            missing = {attr for attr in attrs if not hasattr(param, attr)}
-            assert not missing, (
-                f"__all__ refers to exports that don't exist: {missing!r}"
-            )
+    def test_has__all__(self, subtests):
+        for module in self.collect_modules():
+            with subtests.test(module=module.__name__):
+                assert hasattr(module, "__all__"), "__all__ is missing but 
should exist"
+
+    def test_valid__all__(self, subtests):
+        for module in self.collect_modules():
+            with subtests.test(module=module.__name__):
+                if attrs := getattr(module, "__all__", ()):
+                    missing = {attr for attr in attrs if not hasattr(module, 
attr)}
+                    assert not missing, (
+                        f"__all__ refers to exports that don't exist: 
{missing!r}"
+                    )

Reply via email to