commit: 25c03d669e65ed13ba38a657ffb5357432b5131d
Author: Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Mon Oct 27 07:01:58 2025 +0000
Commit: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Mon Oct 27 21:38:15 2025 +0000
URL:
https://gitweb.gentoo.org/proj/pkgcore/snakeoil.git/commit/?id=25c03d66
chore: add explicit tests for get_slots_of
Also tweak the mechanism here slightly to remove some
errant duplicate slots that were coming through.
Signed-off-by: Brian Harring <ferringb <AT> gmail.com>
src/snakeoil/klass/util.py | 5 +++--
tests/klass/test_util.py | 37 ++++++++++++++++++++++++++++++-------
2 files changed, 33 insertions(+), 9 deletions(-)
diff --git a/src/snakeoil/klass/util.py b/src/snakeoil/klass/util.py
index 5fbe410..888c4a7 100644
--- a/src/snakeoil/klass/util.py
+++ b/src/snakeoil/klass/util.py
@@ -15,11 +15,12 @@ def get_slots_of(kls: type) -> Iterable[tuple[type, None |
tuple[str, ...]]]:
or literal python C extensions, not unless they expose __slots__.
"""
- yield (kls, getattr(kls, "__slots__", () if kls in _known_builtins else
None))
for base in kls.mro():
yield (
base,
- getattr(base, "__slots__", () if base in _known_builtins else
None),
+ # class objects provide a proxy map so abuse that to look at the
class
+ # directly.
+ base.__dict__.get("__slots__", () if base in _known_builtins else
None),
)
diff --git a/tests/klass/test_util.py b/tests/klass/test_util.py
index 6039055..cdd099d 100644
--- a/tests/klass/test_util.py
+++ b/tests/klass/test_util.py
@@ -3,7 +3,7 @@ from typing import Any
import pytest
-from snakeoil.klass.util import combine_classes, get_attrs_of
+from snakeoil.klass.util import combine_classes, get_attrs_of, get_slots_of
def test_get_attrs_of():
@@ -52,9 +52,9 @@ def test_get_attrs_of():
# the fun one. Mixed slotting.
obj = mk_obj(mk_obj(), slots="x", create=True)
obj.x = 1
- assert (
- "x" not in obj.__dict__
- ), "a slotted variable was tucked into __dict__; this is not how python is
understood to work for this code. While real code can do this- it's dumb but
possible- this test doesn't do that, thus something is off."
+ assert "x" not in obj.__dict__, (
+ "a slotted variable was tucked into __dict__; this is not how python
is understood to work for this code. While real code can do this- it's dumb
but possible- this test doesn't do that, thus something is off."
+ )
assert_attrs(obj, {"x": 1})
obj.y = 2
assert_attrs(obj, {"x": 1, "y": 2})
@@ -69,6 +69,29 @@ def test_get_attrs_of():
assert_attrs(obj, {}, suppressions=["blah"])
+def test_slots_of():
+ # the bulk of this logic is already flxed by get_attrs_of. Just assert
the api.
+ class kls1:
+ __slots__ = ("x",)
+
+ class kls2(kls1):
+ pass
+
+ class kls3(kls2):
+ __slots__ = ()
+
+ class kls4(kls3):
+ __slots__ = ("y",)
+
+ assert [
+ (kls4, ("y",)),
+ (kls3, ()),
+ (kls2, None),
+ (kls1, ("x",)),
+ (object, ()),
+ ] == list(get_slots_of(kls4))
+
+
def test_combine_classes():
class kls1(type):
pass
@@ -89,9 +112,9 @@ def test_combine_classes():
# there is caching, thus also do identity check whilst checking the MRO
chain
kls = combine_classes(kls1, kls2, kls3)
- assert (
- kls is combine_classes(kls1, kls2, kls3)
- ), "combine_metaclass uses lru_cache to avoid generating duplicate
classes, however this didn't cache"
+ assert kls is combine_classes(kls1, kls2, kls3), (
+ "combine_metaclass uses lru_cache to avoid generating duplicate
classes, however this didn't cache"
+ )
combined = combine_classes(kls1, kls2)
assert [combined, kls1, kls2, type, object] == list(combined.__mro__)