Jeremiah Lowin commited on
Commit
e1a4799
·
1 Parent(s): 05ba2c0

Add reprs for OpenAPI objects

Browse files
src/fastmcp/server/openapi.py CHANGED
@@ -138,6 +138,10 @@ class OpenAPITool(Tool):
138
  self._route = route
139
  self._timeout = timeout
140
 
 
 
 
 
141
  async def _execute_request(self, *args, **kwargs):
142
  """Execute the HTTP request based on the route configuration."""
143
  context = kwargs.get("context")
@@ -287,6 +291,10 @@ class OpenAPIResource(Resource):
287
  self._route = route
288
  self._timeout = timeout
289
 
 
 
 
 
290
  async def read(self) -> str | bytes:
291
  """Fetch the resource data by making an HTTP request."""
292
  try:
@@ -397,6 +405,10 @@ class OpenAPIResourceTemplate(ResourceTemplate):
397
  self._route = route
398
  self._timeout = timeout
399
 
 
 
 
 
400
  async def create_resource(
401
  self,
402
  uri: str,
 
138
  self._route = route
139
  self._timeout = timeout
140
 
141
+ def __repr__(self) -> str:
142
+ """Custom representation to prevent recursion errors when printing."""
143
+ return f"OpenAPITool(name={self.name!r}, method={self._route.method}, path={self._route.path})"
144
+
145
  async def _execute_request(self, *args, **kwargs):
146
  """Execute the HTTP request based on the route configuration."""
147
  context = kwargs.get("context")
 
291
  self._route = route
292
  self._timeout = timeout
293
 
294
+ def __repr__(self) -> str:
295
+ """Custom representation to prevent recursion errors when printing."""
296
+ return f"OpenAPIResource(name={self.name!r}, uri={self.uri!r}, path={self._route.path})"
297
+
298
  async def read(self) -> str | bytes:
299
  """Fetch the resource data by making an HTTP request."""
300
  try:
 
405
  self._route = route
406
  self._timeout = timeout
407
 
408
+ def __repr__(self) -> str:
409
+ """Custom representation to prevent recursion errors when printing."""
410
+ return f"OpenAPIResourceTemplate(name={self.name!r}, uri_template={self.uri_template!r}, path={self._route.path})"
411
+
412
  async def create_resource(
413
  self,
414
  uri: str,
tests/server/test_openapi.py CHANGED
@@ -1771,3 +1771,49 @@ class TestFastAPIDescriptionPropagation:
1771
  "name parameter missing from Tool schema in client API"
1772
  )
1773
  # We don't test for the description field content as it may not be consistently propagated
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1771
  "name parameter missing from Tool schema in client API"
1772
  )
1773
  # We don't test for the description field content as it may not be consistently propagated
1774
+
1775
+
1776
+ class TestReprMethods:
1777
+ """Tests for the custom __repr__ methods of OpenAPI objects."""
1778
+
1779
+ async def test_openapi_tool_repr(self, fastmcp_openapi_server: FastMCPOpenAPI):
1780
+ """Test that OpenAPITool's __repr__ method works without recursion errors."""
1781
+ tools = fastmcp_openapi_server._tool_manager.list_tools()
1782
+ tool = next(iter(tools))
1783
+
1784
+ # Verify repr doesn't cause recursion and contains expected elements
1785
+ tool_repr = repr(tool)
1786
+ assert "OpenAPITool" in tool_repr
1787
+ assert f"name={tool.name!r}" in tool_repr
1788
+ assert "method=" in tool_repr
1789
+ assert "path=" in tool_repr
1790
+
1791
+ async def test_openapi_resource_repr(self, fastmcp_openapi_server: FastMCPOpenAPI):
1792
+ """Test that OpenAPIResource's __repr__ method works without recursion errors."""
1793
+ resources = list(
1794
+ fastmcp_openapi_server._resource_manager.get_resources().values()
1795
+ )
1796
+ resource = next(iter(resources))
1797
+
1798
+ # Verify repr doesn't cause recursion and contains expected elements
1799
+ resource_repr = repr(resource)
1800
+ assert "OpenAPIResource" in resource_repr
1801
+ assert f"name={resource.name!r}" in resource_repr
1802
+ assert "uri=" in resource_repr
1803
+ assert "path=" in resource_repr
1804
+
1805
+ async def test_openapi_resource_template_repr(
1806
+ self, fastmcp_openapi_server: FastMCPOpenAPI
1807
+ ):
1808
+ """Test that OpenAPIResourceTemplate's __repr__ method works without recursion errors."""
1809
+ templates = list(
1810
+ fastmcp_openapi_server._resource_manager.get_templates().values()
1811
+ )
1812
+ template = next(iter(templates))
1813
+
1814
+ # Verify repr doesn't cause recursion and contains expected elements
1815
+ template_repr = repr(template)
1816
+ assert "OpenAPIResourceTemplate" in template_repr
1817
+ assert f"name={template.name!r}" in template_repr
1818
+ assert "uri_template=" in template_repr
1819
+ assert "path=" in template_repr
tests/utilities/openapi/test_openapi_fastapi.py CHANGED
@@ -520,3 +520,20 @@ def test_duplicate_tags_handling(fastapi_server):
520
  # We'll test both possibilities to be safe
521
  assert "duplicate" in test_route.tags, "Tag 'duplicate' should be present"
522
  assert "items" in test_route.tags, "Tag 'items' should be present"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
  # We'll test both possibilities to be safe
521
  assert "duplicate" in test_route.tags, "Tag 'duplicate' should be present"
522
  assert "items" in test_route.tags, "Tag 'items' should be present"
523
+
524
+
525
+ def test_repr_http_routes(parsed_routes):
526
+ """Test that HTTPRoute objects can be represented without recursion errors."""
527
+ # Test repr on all parsed routes
528
+ for route in parsed_routes:
529
+ route_repr = repr(route)
530
+
531
+ # Verify repr contains essential information
532
+ assert route.method in route_repr, f"Method {route.method} missing from repr"
533
+ assert route.path in route_repr, f"Path {route.path} missing from repr"
534
+
535
+ # If operation_id exists, it should be in the repr
536
+ if route.operation_id:
537
+ assert route.operation_id in route_repr, (
538
+ f"Operation ID {route.operation_id} missing from repr"
539
+ )