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