Skip to content

Commit 45f0467

Browse files
mdesmetclaude
andauthored
fix: prevent infinite recursion in to_dict function (#1748)
Co-authored-by: Claude <[email protected]>
1 parent f931577 commit 45f0467

File tree

2 files changed

+57
-14
lines changed

2 files changed

+57
-14
lines changed

dbt_cloud_integration.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,41 @@ def validate_whether_sql_has_columns(sql: str, dialect: str):
6262
raise Exception(str(e))
6363

6464

65-
def to_dict(obj):
65+
def to_dict(obj, visited=None):
66+
if visited is None:
67+
visited = set()
68+
69+
# Check for circular references using object id
70+
obj_id = id(obj)
71+
if obj_id in visited:
72+
return "<circular reference>"
73+
6674
if isinstance(obj, str):
6775
return obj
6876
if isinstance(obj, Decimal):
6977
return float(obj)
7078
if isinstance(obj, (datetime, date, time)):
7179
return obj.isoformat()
7280
elif isinstance(obj, dict):
73-
return dict((key, to_dict(val)) for key, val in obj.items())
81+
visited.add(obj_id)
82+
result = dict((key, to_dict(val, visited)) for key, val in obj.items())
83+
visited.remove(obj_id)
84+
return result
7485
elif isinstance(obj, Iterable):
75-
return [to_dict(val) for val in obj]
86+
visited.add(obj_id)
87+
result = [to_dict(val, visited) for val in obj]
88+
visited.remove(obj_id)
89+
return result
7690
elif hasattr(obj, "__dict__"):
77-
return to_dict(vars(obj))
91+
visited.add(obj_id)
92+
result = to_dict(vars(obj), visited)
93+
visited.remove(obj_id)
94+
return result
7895
elif hasattr(obj, "__slots__"):
79-
return to_dict(
80-
dict((name, getattr(obj, name)) for name in getattr(obj, "__slots__"))
96+
visited.add(obj_id)
97+
result = to_dict(
98+
dict((name, getattr(obj, name)) for name in getattr(obj, "__slots__")), visited
8199
)
100+
visited.remove(obj_id)
101+
return result
82102
return obj

dbt_core_integration.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,29 +142,52 @@ def validate_whether_sql_has_columns(sql: str, dialect: str):
142142
raise Exception(str(e))
143143

144144

145-
def to_dict(obj):
145+
def to_dict(obj, visited=None):
146+
if visited is None:
147+
visited = set()
148+
149+
# Check for circular references using object id
150+
obj_id = id(obj)
151+
if obj_id in visited:
152+
return "<circular reference>"
153+
146154
if isinstance(obj, agate.Table):
147-
return {
148-
"rows": [to_dict(row) for row in obj.rows],
155+
visited.add(obj_id)
156+
result = {
157+
"rows": [to_dict(row, visited) for row in obj.rows],
149158
"column_names": obj.column_names,
150159
"column_types": list(map(lambda x: x.__class__.__name__, obj.column_types)),
151160
}
161+
visited.remove(obj_id)
162+
return result
152163
if isinstance(obj, str):
153164
return obj
154165
if isinstance(obj, Decimal):
155166
return float(obj)
156167
if isinstance(obj, (datetime, date, time)):
157168
return obj.isoformat()
158169
elif isinstance(obj, dict):
159-
return dict((key, to_dict(val)) for key, val in obj.items())
170+
visited.add(obj_id)
171+
result = dict((key, to_dict(val, visited)) for key, val in obj.items())
172+
visited.remove(obj_id)
173+
return result
160174
elif isinstance(obj, Iterable):
161-
return [to_dict(val) for val in obj]
175+
visited.add(obj_id)
176+
result = [to_dict(val, visited) for val in obj]
177+
visited.remove(obj_id)
178+
return result
162179
elif hasattr(obj, "__dict__"):
163-
return to_dict(vars(obj))
180+
visited.add(obj_id)
181+
result = to_dict(vars(obj), visited)
182+
visited.remove(obj_id)
183+
return result
164184
elif hasattr(obj, "__slots__"):
165-
return to_dict(
166-
dict((name, getattr(obj, name)) for name in getattr(obj, "__slots__"))
185+
visited.add(obj_id)
186+
result = to_dict(
187+
dict((name, getattr(obj, name)) for name in getattr(obj, "__slots__")), visited
167188
)
189+
visited.remove(obj_id)
190+
return result
168191
return obj
169192

170193

0 commit comments

Comments
 (0)