fix(serializer): handle cyclic dataclasses - #1838
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: arpan sahu <28574248+arpansahu@users.noreply.github.com>
|
|
| return { | ||
| field.name: self.default(getattr(obj, field.name)) |
There was a problem hiding this comment.
When a cyclic dataclass reference passes through a tuple, set, or frozenset field, that container returns the repeated dataclass as a raw element and the enclosing finally removes it from seen before JSONEncoder encounters it, causing serialization to recurse instead of emitting the type marker and preserving the structured fields.
Knowledge Base Used: Shared models and data serialization
Prompt To Fix With AI
This is a comment left during a code review.
Path: langfuse/_utils/serializer.py
Line: 137-138
Comment:
**Container cycles escape guard**
When a cyclic dataclass reference passes through a tuple, set, or frozenset field, that container returns the repeated dataclass as a raw element and the enclosing `finally` removes it from `seen` before `JSONEncoder` encounters it, causing serialization to recurse instead of emitting the type marker and preserving the structured fields.
**Knowledge Base Used:** [Shared models and data serialization](https://app.greptile.com/personal-org-4986/-/custom-context/knowledge-base/langfuse/langfuse-python/-/docs/shared-models-and-data-serialization.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
Disclosure: this contribution was prepared with AI assistance (GitHub Copilot CLI), and The defect, the fix and the regression test were verified locally: the new test fails on If your project would prefer not to take AI-assisted contributions, or you would rather this |
Problem
Serializing a dataclass that contains a cycle currently falls through to the top-level fallback instead of preserving the serializable fields.
dataclasses.asdict()recurses without using the serializer's cycle guard, so a cyclic dataclass becomes a quoted fallback string.Reproducer
Fix
Serialize dataclass fields through
EventSerializer.default()instead ofasdict(), and reuse the existingseenguard so recursive dataclass references are represented by their type name like other cyclic objects.Testing
python -m pytest tests\\unit\\test_serializer.py::test_circular_dataclass_reference -qfailed before the fix and passed after.python -m pytest tests\\unit\\test_serializer.py -k "not test_path" -q-> 30 passed, 1 deselected.ruff format langfuse\\_utils\\serializer.py tests\\unit\\test_serializer.py --check-> 2 files already formatted.ruff check langfuse\\_utils\\serializer.py tests\\unit\\test_serializer.py-> All checks passed.Note: full offline
tests\\unitbaseline has pre-existing Windows/environment failures unrelated to this change (test_path, prompt subprocess missingopentelemetry, and prompt mock errors); the changed serializer subset passes.Greptile Summary
The PR replaces
dataclasses.asdict()with field-wise serialization guarded by the serializer’s existing cycle tracking and adds coverage for direct cyclic dataclass references.EventSerializer.default().Confidence Score: 4/5
The cyclic-dataclass fix should be completed for tuple, set, and frozenset fields before merging because those supported containers can still bypass cycle detection.
Field-wise serialization handles direct and recursively traversed container references, but raw elements returned from tuple, set, and frozenset conversion are encoded only after the enclosing dataclass has been removed from the cycle guard.
Files Needing Attention: langfuse/_utils/serializer.py
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Encode dataclass] --> B[Add object ID to seen] B --> C[Serialize each field] C --> D{Field container type} D -->|dict, list, Sequence| E[Recursively call default] D -->|tuple, set, frozenset| F[Return list with raw elements] E --> G[Repeated dataclass becomes type marker] F --> H[Remove enclosing dataclass from seen] H --> I[JSONEncoder encounters raw dataclass again] I --> APrompt To Fix All With AI
Reviews (1): Last reviewed commit: "fix(serializer): handle cyclic dataclass..." | Re-trigger Greptile
Context used: