commit: d8ccaafb718d547f8a0c6f55ad20eac1907db7d5
Author: Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Sun Dec 14 17:17:17 2025 +0000
Commit: Brian Harring <ferringb <AT> gmail <DOT> com>
CommitDate: Mon Dec 15 12:42:10 2025 +0000
URL:
https://gitweb.gentoo.org/proj/pkgcore/snakeoil.git/commit/?id=d8ccaafb
Add tests for get_attrs_of to detect when it's no longer needed.
vars() doesn't handle slotted classes in any way usable, so this
function addresses that. This test just validates *why* this
function exists so we can know when to remove it, if ever.
Signed-off-by: Brian Harring <ferringb <AT> gmail.com>
tests/klass/test_util.py | 140 +++++++++++++++++++++++++++--------------------
1 file changed, 80 insertions(+), 60 deletions(-)
diff --git a/tests/klass/test_util.py b/tests/klass/test_util.py
index d33ec9a..ced1fb2 100644
--- a/tests/klass/test_util.py
+++ b/tests/klass/test_util.py
@@ -17,67 +17,87 @@ from snakeoil.klass.util import (
)
-def test_get_attrs_of():
- def _assert_no_duplicates(obj, **kwds):
- seen = set()
- for k, v in get_attrs_of(obj, **kwds):
- assert k not in seen, "duplicate attributes must not be returned"
- yield k, v
- seen.add(k)
-
- def assert_attrs(o, result, **kwds):
- assert result == dict(_assert_no_duplicates(o, **kwds))
-
- def mk_obj(parent=object, slots=None, create=False) -> Any:
- class kls(parent):
- if slots is not None:
- locals()["__slots__"] = (
- (slots,) if isinstance(slots, str) else tuple(slots)
- )
-
- if not create:
- return kls
- obj = kls()
+class Test_get_attrs_of:
+ def test_reason_for_existing(self):
+ # This exists to detect when python fixes the vars() issue, rendering
get_attrs_of redundant.
+ class foon:
+ __slots__ = ("x", "y")
+
+ inst = foon()
+ inst.x, inst.y = 1, 2
+ with pytest.raises(TypeError):
+ vars(inst)
+
+ class with_dict(foon): ...
+
+ inst = with_dict()
+ inst.x, inst.y = (1, 2)
+ assert [] == list(vars(inst))
+ # Push something into the __dict__
+ inst.z = 3 # pyright: ignore[reportAttributeAccessIssue]
+ assert ["z"] == list(vars(inst))
+
+ def test_it(self):
+ def _assert_no_duplicates(obj, **kwds):
+ seen = set()
+ for k, v in get_attrs_of(obj, **kwds):
+ assert k not in seen, "duplicate attributes must not be
returned"
+ yield k, v
+ seen.add(k)
+
+ def assert_attrs(o, result, **kwds):
+ assert result == dict(_assert_no_duplicates(o, **kwds))
+
+ def mk_obj(parent=object, slots=None, create=False) -> Any:
+ class kls(parent):
+ if slots is not None:
+ locals()["__slots__"] = (
+ (slots,) if isinstance(slots, str) else tuple(slots)
+ )
+
+ if not create:
+ return kls
+ obj = kls()
+ assert_attrs(obj, {})
+ return obj
+
+ # make a pure chained slotted class
+ obj = mk_obj(mk_obj(slots="x"), "y", create=True)
+ obj.x = 1
+ assert_attrs(obj, {"x": 1})
+ obj.y = 2
+ assert_attrs(obj, {"x": 1, "y": 2})
+ # verify the reverse direction out of paranoia.
+ del obj.x
+ assert_attrs(obj, {"y": 2})
+
+ # verify it handles shadowed slots
+ obj = mk_obj(mk_obj(slots="x"), slots="x", create=True) # x is
shadowed
+ obj.x = 1
+ assert_attrs(obj, {"x": 1})
+
+ # ...verify it handles layered __slots__ that normal visibility of the
underlying
+ # slot, even if it's still a usable attribute
+ obj = mk_obj(mk_obj(slots="x"), slots=(), create=True)
+
+ # 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_attrs(obj, {"x": 1})
+ obj.y = 2
+ assert_attrs(obj, {"x": 1, "y": 2})
+
+ # check weakref and suppression
+ obj = mk_obj(create=True)
+ ref = weakref.ref(obj)
assert_attrs(obj, {})
- return obj
-
- # make a pure chained slotted class
- obj = mk_obj(mk_obj(slots="x"), "y", create=True)
- obj.x = 1
- assert_attrs(obj, {"x": 1})
- obj.y = 2
- assert_attrs(obj, {"x": 1, "y": 2})
- # verify the reverse direction out of paranoia.
- del obj.x
- assert_attrs(obj, {"y": 2})
-
- # verify it handles shadowed slots
- obj = mk_obj(mk_obj(slots="x"), slots="x", create=True) # x is shadowed
- obj.x = 1
- assert_attrs(obj, {"x": 1})
-
- # ...verify it handles layered __slots__ that normal visibility of the
underlying
- # slot, even if it's still a usable attribute
- obj = mk_obj(mk_obj(slots="x"), slots=(), create=True)
-
- # 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_attrs(obj, {"x": 1})
- obj.y = 2
- assert_attrs(obj, {"x": 1, "y": 2})
-
- # check weakref and suppression
- obj = mk_obj(create=True)
- ref = weakref.ref(obj)
- assert_attrs(obj, {})
-
- assert_attrs(obj, {"__weakref__": ref}, weakref=True)
- obj.blah = 1
- assert_attrs(obj, {}, suppressions=["blah"])
+
+ assert_attrs(obj, {"__weakref__": ref}, weakref=True)
+ obj.blah = 1
+ assert_attrs(obj, {}, suppressions=["blah"])
def test_slots_of():