commit:     2a146ef51347ef75b7784c492eb62fcccda8d6ec
Author:     Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Tue Nov 25 11:28:21 2025 +0000
Commit:     Brian Harring <ferringb <AT> gmail <DOT> com>
CommitDate: Thu Nov 27 16:14:04 2025 +0000
URL:        
https://gitweb.gentoo.org/proj/pkgcore/snakeoil.git/commit/?id=2a146ef5

feat: klass.get_slot_of, derived from klass.get_slots_of

This allows accessing the slotting information of a class,
accounting for the way it must be done.

get_slots_of in turn is rebased to use this.

They're very simple functions, the complexity is purely in
knowing how the python internals behave.

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

 src/snakeoil/klass/__init__.py |  3 ++-
 src/snakeoil/klass/util.py     | 34 ++++++++++++++++++++++++----------
 tests/klass/test_util.py       | 17 +++++++++++------
 3 files changed, 37 insertions(+), 17 deletions(-)

diff --git a/src/snakeoil/klass/__init__.py b/src/snakeoil/klass/__init__.py
index 7fa587b..36e38aa 100644
--- a/src/snakeoil/klass/__init__.py
+++ b/src/snakeoil/klass/__init__.py
@@ -33,6 +33,7 @@ __all__ = (
     "DirProxy",
     "GetAttrProxy",
     "get_slots_of",
+    "get_slot_of",
     "get_attrs_of",
 )
 
@@ -63,7 +64,7 @@ from .properties import (
     jit_attr_named,
     jit_attr_none,
 )
-from .util import combine_classes, copy_docs, get_attrs_of, get_slots_of
+from .util import combine_classes, copy_docs, get_attrs_of, get_slot_of, 
get_slots_of
 
 sentinel = object()
 

diff --git a/src/snakeoil/klass/util.py b/src/snakeoil/klass/util.py
index 73d9bce..981e6c8 100644
--- a/src/snakeoil/klass/util.py
+++ b/src/snakeoil/klass/util.py
@@ -1,13 +1,17 @@
 __all__ = (
     "get_attrs_of",
+    "get_slot_of",
     "get_slots_of",
     "combine_classes",
     "copy_class_docs",
     "copy_docs",
+    "ClassSlotting",
 )
+
 import builtins
 import functools
 import types
+import typing
 from typing import Any, Iterable
 
 _known_builtins = frozenset(
@@ -15,7 +19,12 @@ _known_builtins = frozenset(
 )
 
 
-def get_slots_of(kls: type) -> Iterable[tuple[type, None | tuple[str, ...]]]:
+class ClassSlotting(typing.NamedTuple):
+    cls: type
+    slots: typing.Sequence[str] | None
+
+
+def get_slots_of(kls: type) -> Iterable[ClassSlotting]:
     """Visit a class MRO collecting all slotting
 
     This cannot collect slotting of C objects- python builtins like object,
@@ -23,12 +32,17 @@ def get_slots_of(kls: type) -> Iterable[tuple[type, None | 
tuple[str, ...]]]:
 
     """
     for base in kls.mro():
-        yield (
-            base,
-            # 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),
-        )
+        yield get_slot_of(base)
+
+
+def get_slot_of(cls: type) -> ClassSlotting:
+    """Return the non-inherited slotting from a class, IE specifically what 
that definition set."""
+    return ClassSlotting(
+        cls,
+        # class objects provide a proxy map so abuse that to look at the class
+        # directly.
+        cls.__dict__.get("__slots__", () if cls in _known_builtins else None),
+    )
 
 
 def get_attrs_of(
@@ -61,10 +75,10 @@ def get_attrs_of(
         if k not in seen:
             yield (k, v)
             seen.add(k)
-    for _, slots in get_slots_of(type(obj)):
-        if slots is None:
+    for data in get_slots_of(type(obj)):
+        if data.slots is None:
             continue
-        for slot in slots:
+        for slot in data.slots:
             if slot not in seen:
                 if (o := getattr(obj, slot, _sentinel)) is not _sentinel:
                     yield slot, o

diff --git a/tests/klass/test_util.py b/tests/klass/test_util.py
index cdd099d..5736223 100644
--- a/tests/klass/test_util.py
+++ b/tests/klass/test_util.py
@@ -3,7 +3,12 @@ from typing import Any
 
 import pytest
 
-from snakeoil.klass.util import combine_classes, get_attrs_of, get_slots_of
+from snakeoil.klass.util import (
+    ClassSlotting,
+    combine_classes,
+    get_attrs_of,
+    get_slots_of,
+)
 
 
 def test_get_attrs_of():
@@ -84,11 +89,11 @@ def test_slots_of():
         __slots__ = ("y",)
 
     assert [
-        (kls4, ("y",)),
-        (kls3, ()),
-        (kls2, None),
-        (kls1, ("x",)),
-        (object, ()),
+        ClassSlotting(kls4, ("y",)),
+        ClassSlotting(kls3, ()),
+        ClassSlotting(kls2, None),
+        ClassSlotting(kls1, ("x",)),
+        ClassSlotting(object, ()),
     ] == list(get_slots_of(kls4))
 
 

Reply via email to