import base64 import dataclasses import decimal import importlib import uuid import warnings from datetime import date, datetime, timezone from threading import Lock from typing import Any, Optional import numpy as np import pytest import opik.jsonable_encoder as jsonable_encoder from opik.rest_api.core import pydantic_utilities @dataclasses.dataclass class Node: value: int child: Optional["Node"] = None def test_jsonable_encoder__cyclic_reference(): """ Test that the encoder detects cyclic references and does not infinitely recurse. """ # Create a simple two-node cycle: A -> B -> A node_a = Node(value=1) node_b = Node(value=2) node_a.child = node_b node_b.child = node_a encoded = jsonable_encoder.encode(node_a) # The exact format of the cycle marker can vary; we check that: # 1. We get some structure for node_a (like a dict). # 2. Inside node_a, there's a reference to node_b (a dict). # 3. Inside node_b, there's a "cyclic reference" marker instead of a full node_a object. print("=" * 150) print(encoded) assert isinstance(encoded, dict) assert "value" in encoded assert "child" in encoded # node_a.child (which is node_b) should be a dict assert isinstance(encoded["child"], dict) assert "value" in encoded["child"] assert "child" in encoded["child"] # node_b.child should be the cycle marker cycle_marker = encoded["child"]["child"] print("=" * 150) print(cycle_marker) assert isinstance(cycle_marker, str), ( "Expected a string marker for cyclic reference" ) assert "