mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-24 06:24:18 +02:00
Fix uvicorn 0.39+ test timeouts and FastMCPError propagation (#2699)
This commit is contained in:
parent
fec24a9eea
commit
9147518c01
5 changed files with 35 additions and 18 deletions
|
|
@ -7,7 +7,7 @@ from typing import Any
|
|||
import mcp.types
|
||||
|
||||
from fastmcp import settings
|
||||
from fastmcp.exceptions import NotFoundError, PromptError
|
||||
from fastmcp.exceptions import FastMCPError, NotFoundError, PromptError
|
||||
from fastmcp.prompts.prompt import (
|
||||
FunctionPrompt,
|
||||
Prompt,
|
||||
|
|
@ -121,7 +121,7 @@ class PromptManager:
|
|||
prompt = await self.get_prompt(name)
|
||||
try:
|
||||
return await prompt._render(arguments)
|
||||
except PromptError:
|
||||
except FastMCPError:
|
||||
raise
|
||||
except Exception as e:
|
||||
if self.mask_error_details:
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import mcp.types
|
|||
from pydantic import AnyUrl
|
||||
|
||||
from fastmcp import settings
|
||||
from fastmcp.exceptions import NotFoundError, ResourceError
|
||||
from fastmcp.exceptions import FastMCPError, NotFoundError, ResourceError
|
||||
from fastmcp.resources.resource import Resource, ResourceContent
|
||||
from fastmcp.resources.template import (
|
||||
ResourceTemplate,
|
||||
|
|
@ -269,8 +269,8 @@ class ResourceManager:
|
|||
uri_str,
|
||||
params=params,
|
||||
)
|
||||
# Pass through ResourceErrors as-is
|
||||
except ResourceError as e:
|
||||
# Pass through FastMCPErrors as-is
|
||||
except FastMCPError as e:
|
||||
logger.error(f"Error creating resource from template: {e}")
|
||||
raise e
|
||||
# Handle other exceptions
|
||||
|
|
@ -307,7 +307,7 @@ class ResourceManager:
|
|||
resource = await self.get_resource(uri_str)
|
||||
try:
|
||||
return await resource._read()
|
||||
except ResourceError:
|
||||
except FastMCPError:
|
||||
raise
|
||||
except Exception as e:
|
||||
if self.mask_error_details:
|
||||
|
|
@ -320,7 +320,7 @@ class ResourceManager:
|
|||
try:
|
||||
resource = await template.create_resource(uri_str, params=params)
|
||||
return await resource._read()
|
||||
except ResourceError:
|
||||
except FastMCPError:
|
||||
raise
|
||||
except Exception as e:
|
||||
if self.mask_error_details:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from mcp.types import ToolAnnotations
|
|||
from pydantic import ValidationError
|
||||
|
||||
from fastmcp import settings
|
||||
from fastmcp.exceptions import NotFoundError, ToolError
|
||||
from fastmcp.exceptions import FastMCPError, NotFoundError, ToolError
|
||||
from fastmcp.settings import DuplicateBehavior
|
||||
from fastmcp.tools.tool import Tool, ToolResult
|
||||
from fastmcp.tools.tool_transform import (
|
||||
|
|
@ -160,9 +160,9 @@ class ToolManager:
|
|||
tool = await self.get_tool(key)
|
||||
try:
|
||||
return await tool.run(arguments)
|
||||
except ValidationError:
|
||||
except FastMCPError:
|
||||
raise
|
||||
except ToolError:
|
||||
except ValidationError:
|
||||
raise
|
||||
except Exception as e:
|
||||
if self.mask_error_details:
|
||||
|
|
|
|||
|
|
@ -119,13 +119,15 @@ async def nested_sse_server():
|
|||
ws="websockets-sansio",
|
||||
)
|
||||
|
||||
server_task = asyncio.create_task(uvicorn.Server(config).serve())
|
||||
uvicorn_server = uvicorn.Server(config)
|
||||
server_task = asyncio.create_task(uvicorn_server.serve())
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
try:
|
||||
yield f"http://127.0.0.1:{port}/nest-outer/nest-inner/mcp/sse/"
|
||||
finally:
|
||||
server_task.cancel()
|
||||
# Graceful shutdown - required for uvicorn 0.39+ due to context isolation
|
||||
uvicorn_server.should_exit = True
|
||||
try:
|
||||
await server_task
|
||||
except asyncio.CancelledError:
|
||||
|
|
|
|||
|
|
@ -49,9 +49,14 @@ async def test_uvicorn_logging_default_level(
|
|||
)
|
||||
mock_server_instance.serve.assert_awaited_once()
|
||||
|
||||
# Signal the mock to finish and cancel with timeout
|
||||
# Required for uvicorn 0.39+ due to context isolation
|
||||
serve_finished_event.set()
|
||||
server_task.cancel()
|
||||
with pytest.raises(asyncio.CancelledError):
|
||||
await server_task
|
||||
try:
|
||||
await asyncio.wait_for(server_task, timeout=2.0)
|
||||
except (asyncio.CancelledError, asyncio.TimeoutError):
|
||||
pass
|
||||
|
||||
|
||||
@patch("fastmcp.server.server.uvicorn.Server")
|
||||
|
|
@ -109,9 +114,14 @@ async def test_uvicorn_logging_with_custom_log_config(
|
|||
)
|
||||
mock_server_instance.serve.assert_awaited_once()
|
||||
|
||||
# Signal the mock to finish and cancel with timeout
|
||||
# Required for uvicorn 0.39+ due to context isolation
|
||||
serve_finished_event.set()
|
||||
server_task.cancel()
|
||||
with pytest.raises(asyncio.CancelledError):
|
||||
await server_task
|
||||
try:
|
||||
await asyncio.wait_for(server_task, timeout=2.0)
|
||||
except (asyncio.CancelledError, asyncio.TimeoutError):
|
||||
pass
|
||||
|
||||
|
||||
@patch("fastmcp.server.server.uvicorn.Server")
|
||||
|
|
@ -172,6 +182,11 @@ async def test_uvicorn_logging_custom_log_config_overrides_log_level_param(
|
|||
)
|
||||
mock_server_instance.serve.assert_awaited_once()
|
||||
|
||||
# Signal the mock to finish and cancel with timeout
|
||||
# Required for uvicorn 0.39+ due to context isolation
|
||||
serve_finished_event.set()
|
||||
server_task.cancel()
|
||||
with pytest.raises(asyncio.CancelledError):
|
||||
await server_task
|
||||
try:
|
||||
await asyncio.wait_for(server_task, timeout=2.0)
|
||||
except (asyncio.CancelledError, asyncio.TimeoutError):
|
||||
pass
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue