commit:     2d1a4f41d548738be5214fccc0f450608f74b220
Author:     Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Sat Nov 29 10:16:31 2025 +0000
Commit:     Brian Harring <ferringb <AT> gmail <DOT> com>
CommitDate: Sat Nov 29 10:20:57 2025 +0000
URL:        
https://gitweb.gentoo.org/proj/pkgcore/snakeoil.git/commit/?id=2d1a4f41

chore: create abstractvar for usage with abc.ABC

This is not yet promoted to something snakeoil exported-
the manner of using it I don't like.

Consider:
class foo(abc.ABC):
  must_be_set: ClassVar[tuple[str]]

foo() # doesn't fail.

class foo2(abc.ABC):
  must_be_set: ClassVar[tuple[str]] = abstractvar(tuple[str]])

foo2() # does fail.

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

 src/snakeoil/test/code_quality.py | 34 ++++++++++++++++++----------------
 1 file changed, 18 insertions(+), 16 deletions(-)

diff --git a/src/snakeoil/test/code_quality.py 
b/src/snakeoil/test/code_quality.py
index d8d135d..8fb7f4c 100644
--- a/src/snakeoil/test/code_quality.py
+++ b/src/snakeoil/test/code_quality.py
@@ -17,14 +17,27 @@ from snakeoil.python_namespaces import get_submodules_of
 T = typing.TypeVar("T")
 
 
+class _abstractvar:
+    __slots__ = ()
+    __isabstractmethod__ = True
+
+
+def abstractvar(_: type[T]) -> T:
+    """
+    mechanism to use with ClassVars to force abc.ABC to block creation if the 
subclass hasn't set it.
+
+    The mechanism currently is janky; you must pass in the type definition 
since it's the
+    only way to attach this information to the returned object, lieing to the 
type system
+    that the value is type compatible while carrying the marker abc.ABC needs.
+    """
+    return typing.cast(T, _abstractvar())
+
+
 class ParameterizeBase(typing.Generic[T], abc.ABC):
-    namespaces: tuple[str]
+    namespaces: typing.ClassVar[tuple[str]] = abstractvar(tuple[str])
     namespace_ignores: tuple[str, ...] = ()
     strict: tuple[str] | bool = False
-    tests_to_parameterize: typing.ClassVar[tuple[str, ...]]
-
-    # ABC doesn't apply to actual attributes, thus this.
-    is_abstract_still: typing.ClassVar[bool] = False
+    tests_to_parameterize: typing.ClassVar[tuple[str, ...]] = 
abstractvar(tuple[str])
 
     @classmethod
     @abc.abstractmethod
@@ -39,14 +52,6 @@ class ParameterizeBase(typing.Generic[T], abc.ABC):
 
         if inspect.isabstract(cls):
             return
-        if cls.is_abstract_still:
-            del cls.is_abstract_still
-            return
-
-        if not hasattr(cls, "namespaces"):
-            raise TypeError("namespaces wasn't defined on the class")
-        if not hasattr(cls, "tests_to_parameterize"):
-            raise TypeError("tests_to_parameterize wasn't set")
 
         # Inject the parameterization
         targets = list(
@@ -89,8 +94,6 @@ class Slots(ParameterizeBase[type]):
     disable_str: typing.Final = "__slotting_intentionally_disabled__"
     ignored_subclasses: tuple[type, ...] = (Exception,)
 
-    is_abstract_still = True
-
     tests_to_parameterize = (
         "test_shadowing",
         "test_slots_mandatory",
@@ -137,7 +140,6 @@ class Slots(ParameterizeBase[type]):
 
 
 class Modules(ParameterizeBase[ModuleType]):
-    is_abstract_still = True
     tests_to_parameterize = (
         "test_has__all__",
         "test_valid__all__",

Reply via email to