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__)

Reply via email to