1515"""Handle typing imports based on system compatibility."""
1616
1717import sys
18- from typing import Any , Callable , List , Literal , Type , TypeVar , Union , get_args , get_origin
18+ from typing import Any , Callable , List , Literal , Optional , Set , Type , TypeVar , Union , get_args , get_origin
1919
2020
2121UNION_TYPES : List [Any ] = [Union ]
3333_JSON_SERIALIZABLE_TYPES = (int , float , str , bool , type (None ))
3434
3535
36- def is_jsonable (obj : Any ) -> bool :
36+ def is_jsonable (obj : Any , _visited : Optional [ Set [ int ]] = None ) -> bool :
3737 """Check if an object is JSON serializable.
3838
3939 This is a weak check, as it does not check for the actual JSON serialization, but only for the types of the object.
@@ -43,19 +43,39 @@ def is_jsonable(obj: Any) -> bool:
4343 - it is an instance of int, float, str, bool, or NoneType
4444 - it is a list or tuple and all its items are json serializable
4545 - it is a dict and all its keys are strings and all its values are json serializable
46+
47+ Uses a visited set to avoid infinite recursion on circular references. If object has already been visited, it is
48+ considered not json serializable.
4649 """
50+ # Initialize visited set to track object ids and detect circular references
51+ if _visited is None :
52+ _visited = set ()
53+
54+ # Detect circular reference
55+ obj_id = id (obj )
56+ if obj_id in _visited :
57+ return False
58+
59+ # Add current object to visited before recursive checks
60+ _visited .add (obj_id )
4761 try :
4862 if isinstance (obj , _JSON_SERIALIZABLE_TYPES ):
4963 return True
5064 if isinstance (obj , (list , tuple )):
51- return all (is_jsonable (item ) for item in obj )
65+ return all (is_jsonable (item , _visited ) for item in obj )
5266 if isinstance (obj , dict ):
53- return all (isinstance (key , _JSON_SERIALIZABLE_TYPES ) and is_jsonable (value ) for key , value in obj .items ())
67+ return all (
68+ isinstance (key , _JSON_SERIALIZABLE_TYPES ) and is_jsonable (value , _visited )
69+ for key , value in obj .items ()
70+ )
5471 if hasattr (obj , "__json__" ):
5572 return True
5673 return False
5774 except RecursionError :
5875 return False
76+ finally :
77+ # Remove the object id from visited to avoid side‑effects for other branches
78+ _visited .discard (obj_id )
5979
6080
6181def is_simple_optional_type (type_ : Type ) -> bool :
0 commit comments