fix: handle __slots__-only objects in comparator

Objects with __slots__ but no __dict__ (e.g. textual.cache.LRUCache)
fell through all comparator branches, logging "Unknown comparator input
type" and returning False — causing spurious test mismatches.
This commit is contained in:
Kevin Turcios 2026-02-23 05:29:40 -05:00
parent eaff96fd45
commit d8582c328a
2 changed files with 52 additions and 0 deletions

View file

@ -561,6 +561,18 @@ def comparator(orig: Any, new: Any, superset_obj: bool = False) -> bool:
new_keys = {k: v for k, v in new.__dict__.items() if k != "parent"}
return comparator(orig_keys, new_keys, superset_obj)
# For objects with __slots__ but no __dict__, compare slot attributes
if hasattr(type(orig), "__slots__"):
all_slots = set()
for cls in type(orig).__mro__:
if hasattr(cls, "__slots__"):
all_slots.update(cls.__slots__)
orig_vals = {s: getattr(orig, s, None) for s in all_slots}
new_vals = {s: getattr(new, s, None) for s in all_slots}
if superset_obj:
return all(k in new_vals and comparator(v, new_vals[k], superset_obj) for k, v in orig_vals.items())
return comparator(orig_vals, new_vals, superset_obj)
if type(orig) in {types.BuiltinFunctionType, types.BuiltinMethodType}:
return new == orig
if str(type(orig)) == "<class 'object'>":

View file

@ -5240,3 +5240,43 @@ class TestUnionType:
def test_union_type_vs_none(self):
assert not comparator(int | str, None)
class SlotsOnly:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
class SlotsInherited(SlotsOnly):
__slots__ = ("z",)
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z
class TestSlotsObjects:
def test_slots_equal(self):
assert comparator(SlotsOnly(1, 2), SlotsOnly(1, 2))
def test_slots_not_equal(self):
assert not comparator(SlotsOnly(1, 2), SlotsOnly(1, 3))
def test_slots_inherited_equal(self):
assert comparator(SlotsInherited(1, 2, 3), SlotsInherited(1, 2, 3))
def test_slots_inherited_not_equal(self):
assert not comparator(SlotsInherited(1, 2, 3), SlotsInherited(1, 2, 4))
def test_slots_nested(self):
a = SlotsOnly(SlotsOnly(1, 2), [3, 4])
b = SlotsOnly(SlotsOnly(1, 2), [3, 4])
assert comparator(a, b)
def test_slots_nested_not_equal(self):
a = SlotsOnly(SlotsOnly(1, 2), [3, 4])
b = SlotsOnly(SlotsOnly(1, 9), [3, 4])
assert not comparator(a, b)