Spaces:
Running
Running
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 |
+
)
|