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__",