dimitribarbot commited on
Commit
cb2e1c5
·
unverified ·
1 Parent(s): 199f3e5

Fix max recursion error when pruning OpenAPI definitions (#1092)

Browse files

* Fix max recursion error when pruning open api definitions

* Remove mutable default parameter in def_is_referenced function

src/fastmcp/utilities/json_schema.py CHANGED
@@ -67,13 +67,24 @@ def _prune_unused_defs(schema: dict) -> dict:
67
  walk(value, current_def=def_name)
68
 
69
  # Figure out what defs were referenced directly or recursively
70
- def def_is_referenced(def_name):
71
  if def_name in root_defs:
72
  return True
73
  references = referenced_by.get(def_name)
74
  if references:
75
- for reference in references:
76
- if def_is_referenced(reference):
 
 
 
 
 
 
 
 
 
 
 
77
  return True
78
  return False
79
 
 
67
  walk(value, current_def=def_name)
68
 
69
  # Figure out what defs were referenced directly or recursively
70
+ def def_is_referenced(def_name, parent_def_names: set[str] | None = None):
71
  if def_name in root_defs:
72
  return True
73
  references = referenced_by.get(def_name)
74
  if references:
75
+ if parent_def_names is None:
76
+ parent_def_names = set()
77
+
78
+ # Handle recursion by excluding references already present in parent references
79
+ parent_def_names = parent_def_names | {def_name}
80
+ valid_references = [
81
+ reference
82
+ for reference in references
83
+ if reference not in parent_def_names
84
+ ]
85
+
86
+ for reference in valid_references:
87
+ if def_is_referenced(reference, parent_def_names):
88
  return True
89
  return False
90
 
tests/utilities/test_json_schema.py CHANGED
@@ -107,6 +107,79 @@ class TestPruneUnusedDefs:
107
  result = _prune_unused_defs(schema)
108
  assert "$defs" not in result
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  def test_array_references_kept(self):
111
  """Test that definitions referenced in array items are kept."""
112
  schema = {
 
107
  result = _prune_unused_defs(schema)
108
  assert "$defs" not in result
109
 
110
+ def test_nested_references_with_recursion_kept(self):
111
+ """Test that definitions with recursion referenced via nesting are kept."""
112
+ schema = {
113
+ "properties": {
114
+ "foo": {"$ref": "#/$defs/foo_def"},
115
+ },
116
+ "$defs": {
117
+ "foo_def": {
118
+ "type": "object",
119
+ "properties": {"nested": {"$ref": "#/$defs/foo_def"}},
120
+ },
121
+ "unused_def": {"type": "integer"},
122
+ },
123
+ }
124
+ result = _prune_unused_defs(schema)
125
+ assert "foo_def" in result["$defs"]
126
+ assert "unused_def" not in result["$defs"]
127
+
128
+ def test_nested_references_with_recursion_removed(self):
129
+ """Test that definitions with recursion referenced via nesting in unused defs are removed."""
130
+ schema = {
131
+ "properties": {},
132
+ "$defs": {
133
+ "foo_def": {
134
+ "type": "object",
135
+ "properties": {"nested": {"$ref": "#/$defs/foo_def"}},
136
+ },
137
+ },
138
+ }
139
+ result = _prune_unused_defs(schema)
140
+ assert "$defs" not in result
141
+
142
+ def test_multiple_nested_references_with_recursion_kept(self):
143
+ """Test that definitions with multiple levels of recursion referenced via nesting are kept."""
144
+ schema = {
145
+ "properties": {
146
+ "foo": {"$ref": "#/$defs/foo_def"},
147
+ },
148
+ "$defs": {
149
+ "foo_def": {
150
+ "type": "object",
151
+ "properties": {"nested": {"$ref": "#/$defs/nested_def"}},
152
+ },
153
+ "nested_def": {
154
+ "type": "object",
155
+ "properties": {"nested": {"$ref": "#/$defs/foo_def"}},
156
+ },
157
+ "unused_def": {"type": "integer"},
158
+ },
159
+ }
160
+ result = _prune_unused_defs(schema)
161
+ assert "foo_def" in result["$defs"]
162
+ assert "nested_def" in result["$defs"]
163
+ assert "unused_def" not in result["$defs"]
164
+
165
+ def test_multiple_nested_references_with_recursion_removed(self):
166
+ """Test that definitions with multiple levels of recursion referenced via nesting in unused defs are removed."""
167
+ schema = {
168
+ "properties": {},
169
+ "$defs": {
170
+ "foo_def": {
171
+ "type": "object",
172
+ "properties": {"nested": {"$ref": "#/$defs/nested_def"}},
173
+ },
174
+ "nested_def": {
175
+ "type": "object",
176
+ "properties": {"nested": {"$ref": "#/$defs/foo_def"}},
177
+ },
178
+ },
179
+ }
180
+ result = _prune_unused_defs(schema)
181
+ assert "$defs" not in result
182
+
183
  def test_array_references_kept(self):
184
  """Test that definitions referenced in array items are kept."""
185
  schema = {