commit: 9abc045f8436c779343d9b1bf38e7fc1ca5bfa9f
Author: Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Fri Nov 28 19:40:23 2025 +0000
Commit: Brian Harring <ferringb <AT> gmail <DOT> com>
CommitDate: Fri Nov 28 21:46:48 2025 +0000
URL:
https://gitweb.gentoo.org/proj/pkgcore/snakeoil.git/commit/?id=9abc045f
feat: add klass.is_metaclass
It's simple, but it's bit me a few times doing checks against
the wrong base (specifically isinstance), so do this.
Signed-off-by: Brian Harring <ferringb <AT> gmail.com>
src/snakeoil/klass/util.py | 13 ++++++++++++-
tests/klass/test_util.py | 10 ++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/src/snakeoil/klass/util.py b/src/snakeoil/klass/util.py
index aee1b1c..689aca9 100644
--- a/src/snakeoil/klass/util.py
+++ b/src/snakeoil/klass/util.py
@@ -97,14 +97,21 @@ def get_subclasses_of(
This walks the in memory tree of a class hierarchy, yield the subclasses
of the given
cls after optional filtering.
+ Note: this cannot work on metaclasses. Python doesn't carry the necessary
bookkeeping.
+ The first level of a metaclass will be returned, but none of it's
derivative, and it'll
+ be treated as a leaf node- even if it isn't.
+
:param only_leaf_nodes: if True, only yield classes which have no
subclasses
:param ABC: if True, only yield abstract classes. If False, only yield
classes no longer
abstract. If None- the default- do no filtering for ABC.
"""
+ if is_metaclass(cls):
+ return
stack = cls.__subclasses__()
while stack:
current = stack.pop()
- subclasses = current.__subclasses__()
+
+ subclasses = () if is_metaclass(current) else current.__subclasses__()
stack.extend(subclasses)
if ABC is not None:
@@ -117,6 +124,10 @@ def get_subclasses_of(
yield current
+def is_metaclass(cls: type) -> bool:
+ return issubclass(cls, type)
+
+
@functools.lru_cache
def combine_classes(kls: type, *extra: type) -> type:
"""Given a set of classes, combine this as if one had wrote the class by
hand
diff --git a/tests/klass/test_util.py b/tests/klass/test_util.py
index e397410..3b0799c 100644
--- a/tests/klass/test_util.py
+++ b/tests/klass/test_util.py
@@ -11,6 +11,7 @@ from snakeoil.klass.util import (
get_attrs_of,
get_slots_of,
get_subclasses_of,
+ is_metaclass,
)
@@ -170,3 +171,12 @@ def test_get_subclasses_of():
def f2(self): ...
assert_it(layer3, [ABClayer6], ABC=True, only_leaf_nodes=True)
+
+
+def test_is_metaclass():
+ assert not is_metaclass(object)
+ assert is_metaclass(type)
+
+ class foon(type): ...
+
+ assert is_metaclass(foon)