mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-23 05:54:19 +02:00
Merge pull request #3198 from jlowin/fix/ty-errors
Fix ty 0.0.17 diagnostics
This commit is contained in:
commit
5dc2432950
10 changed files with 465 additions and 387 deletions
|
|
@ -378,7 +378,7 @@ async def run_with_reload(
|
|||
|
||||
# Watch for either: file changes OR process death
|
||||
watch_task = asyncio.create_task(
|
||||
anext(aiter(awatch(*watch_paths, watch_filter=_watch_filter)))
|
||||
anext(aiter(awatch(*watch_paths, watch_filter=_watch_filter))) # ty: ignore[invalid-argument-type]
|
||||
)
|
||||
wait_task = asyncio.create_task(process.wait())
|
||||
shutdown_task = asyncio.create_task(shutdown_event.wait())
|
||||
|
|
@ -409,7 +409,7 @@ async def run_with_reload(
|
|||
|
||||
# Wait for file change or shutdown (avoid hot loop on crash)
|
||||
watch_task = asyncio.create_task(
|
||||
anext(aiter(awatch(*watch_paths, watch_filter=_watch_filter)))
|
||||
anext(aiter(awatch(*watch_paths, watch_filter=_watch_filter))) # ty: ignore[invalid-argument-type]
|
||||
)
|
||||
shutdown_task = asyncio.create_task(shutdown_event.wait())
|
||||
done, pending = await asyncio.wait(
|
||||
|
|
|
|||
|
|
@ -99,9 +99,9 @@ class FileSystemSource(Source):
|
|||
logger.error("Could not load module", extra={"file": str(file_path)})
|
||||
sys.exit(1)
|
||||
|
||||
module = importlib.util.module_from_spec(spec) # type: ignore[arg-type]
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules["server_module"] = module # Register in sys.modules
|
||||
spec.loader.exec_module(module) # type: ignore[union-attr]
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
return module
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ class TestClientToolTracing:
|
|||
None,
|
||||
)
|
||||
assert client_span is not None
|
||||
assert client_span.attributes is not None
|
||||
# Standard MCP semantic conventions
|
||||
assert client_span.attributes["mcp.method.name"] == "tools/call"
|
||||
# Standard RPC semantic conventions
|
||||
|
|
@ -119,6 +120,7 @@ class TestClientResourceTracing:
|
|||
None,
|
||||
)
|
||||
assert client_span is not None
|
||||
assert client_span.attributes is not None
|
||||
# Standard MCP semantic conventions
|
||||
assert client_span.attributes["mcp.method.name"] == "resources/read"
|
||||
assert "data://" in str(client_span.attributes["mcp.resource.uri"])
|
||||
|
|
@ -178,6 +180,7 @@ class TestClientPromptTracing:
|
|||
None,
|
||||
)
|
||||
assert client_span is not None
|
||||
assert client_span.attributes is not None
|
||||
# Standard MCP semantic conventions
|
||||
assert client_span.attributes["mcp.method.name"] == "prompts/get"
|
||||
# Standard RPC semantic conventions
|
||||
|
|
@ -230,7 +233,9 @@ class TestClientServerSpanHierarchy:
|
|||
|
||||
# Both spans should exist
|
||||
assert client_span is not None, "Client should create a span"
|
||||
assert client_span.attributes is not None
|
||||
assert server_span is not None, "Server should create a span"
|
||||
assert server_span.attributes is not None
|
||||
|
||||
# Verify span kinds are correct
|
||||
assert client_span.kind == SpanKind.CLIENT, "Client span should be CLIENT kind"
|
||||
|
|
@ -539,6 +544,7 @@ class TestSessionIdOnSpans:
|
|||
)
|
||||
|
||||
assert client_span is not None, "Client should create a span"
|
||||
assert client_span.attributes is not None
|
||||
assert "mcp.session.id" in client_span.attributes
|
||||
assert client_span.attributes["mcp.session.id"] is not None
|
||||
|
||||
|
|
@ -570,6 +576,7 @@ class TestSessionIdOnSpans:
|
|||
)
|
||||
|
||||
assert server_span is not None, "Server should create a span"
|
||||
assert server_span.attributes is not None
|
||||
assert "mcp.session.id" in server_span.attributes
|
||||
assert server_span.attributes["mcp.session.id"] is not None
|
||||
|
||||
|
|
@ -611,7 +618,9 @@ class TestSessionIdOnSpans:
|
|||
)
|
||||
|
||||
assert client_span is not None
|
||||
assert client_span.attributes is not None
|
||||
assert server_span is not None
|
||||
assert server_span.attributes is not None
|
||||
|
||||
# Both should have session IDs and they should match
|
||||
client_session = client_span.attributes.get("mcp.session.id")
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ def create_test_server() -> FastMCP:
|
|||
result = await ctx.elicit("What is your name?", response_type=str)
|
||||
|
||||
if result.action == "accept":
|
||||
return f"You said your name was: {result.data}!" # ty: ignore[possibly-missing-attribute]
|
||||
return f"You said your name was: {result.data}!" # ty: ignore[unresolved-attribute]
|
||||
else:
|
||||
return "No name provided"
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ class TestStreamableHTTPAppResourceMetadataURL:
|
|||
assert isinstance(route.endpoint, RequireAuthMiddleware)
|
||||
# Verify methods include GET, POST, DELETE for streamable-http
|
||||
expected_methods = {"GET", "POST", "DELETE"}
|
||||
assert route.methods is not None
|
||||
assert expected_methods.issubset(set(route.methods))
|
||||
|
||||
def test_no_auth_provider_mounts_without_middleware(self, rsa_key_pair):
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import pytest
|
|||
from mcp.types import TextContent
|
||||
|
||||
from fastmcp import Client, Context, FastMCP
|
||||
from fastmcp.prompts.prompt import Prompt
|
||||
from fastmcp.prompts.prompt import Prompt, PromptResult
|
||||
|
||||
|
||||
class TestPromptContext:
|
||||
|
|
@ -58,6 +58,7 @@ class TestPromptDecorator:
|
|||
prompt = next(p for p in prompts if p.name == "fn")
|
||||
assert prompt.name == "fn"
|
||||
content = await prompt.render()
|
||||
assert isinstance(content, PromptResult)
|
||||
assert isinstance(content.messages[0].content, TextContent)
|
||||
assert content.messages[0].content.text == "Hello, world!"
|
||||
|
||||
|
|
@ -88,6 +89,7 @@ class TestPromptDecorator:
|
|||
prompt = next(p for p in prompts_list if p.name == "custom_name")
|
||||
assert prompt.name == "custom_name"
|
||||
content = await prompt.render()
|
||||
assert isinstance(content, PromptResult)
|
||||
assert isinstance(content.messages[0].content, TextContent)
|
||||
assert content.messages[0].content.text == "Hello, world!"
|
||||
|
||||
|
|
@ -103,6 +105,7 @@ class TestPromptDecorator:
|
|||
prompt = next(p for p in prompts_list if p.name == "fn")
|
||||
assert prompt.description == "A custom description"
|
||||
content = await prompt.render()
|
||||
assert isinstance(content, PromptResult)
|
||||
assert isinstance(content.messages[0].content, TextContent)
|
||||
assert content.messages[0].content.text == "Hello, world!"
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class TestFastMCPProviderTracing:
|
|||
|
||||
# Verify delegate span has correct attributes
|
||||
delegate_span = next(s for s in spans if s.name == "delegate child_tool")
|
||||
assert delegate_span.attributes is not None
|
||||
assert delegate_span.attributes["fastmcp.provider.type"] == "FastMCPProvider"
|
||||
assert delegate_span.attributes["fastmcp.component.key"] == "child_tool"
|
||||
|
||||
|
|
@ -96,6 +97,7 @@ class TestFastMCPProviderTracing:
|
|||
|
||||
# Verify delegate span has correct attributes
|
||||
delegate_span = next(s for s in spans if s.name == "delegate child_prompt")
|
||||
assert delegate_span.attributes is not None
|
||||
assert delegate_span.attributes["fastmcp.provider.type"] == "FastMCPProvider"
|
||||
|
||||
|
||||
|
|
@ -125,5 +127,7 @@ class TestProviderSpanHierarchy:
|
|||
child_span = next(s for s in spans if s.name == "tools/call greet")
|
||||
|
||||
# Verify parent-child relationships
|
||||
assert delegate_span.parent is not None
|
||||
assert child_span.parent is not None
|
||||
assert delegate_span.parent.span_id == parent_span.context.span_id
|
||||
assert child_span.parent.span_id == delegate_span.context.span_id
|
||||
|
|
|
|||
|
|
@ -246,6 +246,7 @@ class TestMountedServerVersioning:
|
|||
|
||||
# Calling the v1.0 wrapper should execute v1.0's logic
|
||||
result = await v1_tool.run({"x": 5})
|
||||
assert isinstance(result.content[0], TextContent)
|
||||
assert result.content[0].text == "50" # 5 * 10, not 5 * 100
|
||||
|
||||
async def test_mounted_resource_wrapper_reads_correct_version(self):
|
||||
|
|
|
|||
|
|
@ -554,9 +554,9 @@ class TestNameHandling:
|
|||
Type = json_schema_to_type(schema)
|
||||
assert Type.__name__ == "Parent"
|
||||
child_field_type = get_dataclass_field(Type, "child").type
|
||||
assert child_field_type.__origin__ is Union # ty: ignore[possibly-missing-attribute]
|
||||
assert child_field_type.__args__[0].__name__ == "Child" # ty: ignore[possibly-missing-attribute]
|
||||
assert child_field_type.__args__[1] is type(None) # ty: ignore[possibly-missing-attribute]
|
||||
assert child_field_type.__origin__ is Union # ty: ignore[unresolved-attribute]
|
||||
assert child_field_type.__args__[0].__name__ == "Child" # ty: ignore[unresolved-attribute]
|
||||
assert child_field_type.__args__[1] is type(None) # ty: ignore[unresolved-attribute]
|
||||
|
||||
def test_recursive_schema_naming(self):
|
||||
schema = {
|
||||
|
|
@ -569,9 +569,9 @@ class TestNameHandling:
|
|||
|
||||
next_field_type = get_dataclass_field(Type, "next").type
|
||||
|
||||
assert next_field_type.__origin__ is Union # ty: ignore[possibly-missing-attribute]
|
||||
assert next_field_type.__args__[0].__forward_arg__ == "Node" # ty: ignore[possibly-missing-attribute]
|
||||
assert next_field_type.__args__[1] is type(None) # ty: ignore[possibly-missing-attribute]
|
||||
assert next_field_type.__origin__ is Union # ty: ignore[unresolved-attribute]
|
||||
assert next_field_type.__args__[0].__forward_arg__ == "Node" # ty: ignore[unresolved-attribute]
|
||||
assert next_field_type.__args__[1] is type(None) # ty: ignore[unresolved-attribute]
|
||||
|
||||
def test_name_caching_with_different_titles(self):
|
||||
"""Ensure schemas with different titles create different cached classes"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue