You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(serializer): handle single-string __slots__ and detect cycles in slotted objects #2014
Single string __slots__ attribute loss:
According to the Python Language Reference (Data Model §3.3.2.4: __slots__), __slots__ can be defined as a single string naming a single attribute (e.g. __slots__ = "query"). When __slots__ is a string, iterating directly over it yields individual characters (q, u, e, r, y). getattr(obj, "q", None) returns None, resulting in {"q": null, "u": null, ...} while the actual attribute query is lost.
Missing circular reference detection:
Unlike the __dict__ branch which tracks obj_id in self.seen to immediately break reference cycles, the __slots__ branch lacks self.seen protection. When slotted objects contain reference cycles, each branch expands down to _MAX_DEPTH = 20, causing redundant recursion and bloated payloads.
To Reproduce
importjsonfromlangfuse._utils.serializerimportEventSerializer# Case 1: Single string slotclassSingleSlot:
__slots__="query"def__init__(self):
self.query="SELECT 1"res=json.loads(EventSerializer().encode(SingleSlot()))
print(res)
# Current output: {"q": null, "u": null, "e": null, "r": null, "y": null}# Expected: {"query": "SELECT 1"}# Case 2: Self-referential slotted nodeclassSlotNode:
__slots__= ("next",)
node=SlotNode()
node.next=noderes=json.loads(EventSerializer().encode(node))
# Current output: recurses 20 layers deep# Expected: {"next": "SlotNode"}
Proposed Fix
Normalize string __slots__ to a 1-tuple (obj.__slots__,) if isinstance(obj.__slots__, str).
Add self.seen tracking with try...finally to __slots__ (matching __dict__).
Activity