docs: add code fences to Example: docstring blocks (#2104)
This commit is contained in:
parent
c0328540c9
commit
cb07adeca3
18 changed files with 83 additions and 18 deletions
|
|
@ -187,11 +187,13 @@ class ExperimentalTaskHandlers:
|
||||||
WARNING: These APIs are experimental and may change without notice.
|
WARNING: These APIs are experimental and may change without notice.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
handlers = ExperimentalTaskHandlers(
|
handlers = ExperimentalTaskHandlers(
|
||||||
get_task=my_get_task_handler,
|
get_task=my_get_task_handler,
|
||||||
list_tasks=my_list_tasks_handler,
|
list_tasks=my_list_tasks_handler,
|
||||||
)
|
)
|
||||||
session = ClientSession(..., experimental_task_handlers=handlers)
|
session = ClientSession(..., experimental_task_handlers=handlers)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Pure task request handlers
|
# Pure task request handlers
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ This module provides client methods for interacting with MCP tasks.
|
||||||
WARNING: These APIs are experimental and may change without notice.
|
WARNING: These APIs are experimental and may change without notice.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
# Call a tool as a task
|
# Call a tool as a task
|
||||||
result = await session.experimental.call_tool_as_task("tool_name", {"arg": "value"})
|
result = await session.experimental.call_tool_as_task("tool_name", {"arg": "value"})
|
||||||
task_id = result.task.task_id
|
task_id = result.task.task_id
|
||||||
|
|
@ -21,6 +22,7 @@ Example:
|
||||||
|
|
||||||
# Cancel a task
|
# Cancel a task
|
||||||
await session.experimental.cancel_task(task_id)
|
await session.experimental.cancel_task(task_id)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from collections.abc import AsyncIterator
|
from collections.abc import AsyncIterator
|
||||||
|
|
@ -72,6 +74,7 @@ class ExperimentalClientFeatures:
|
||||||
CreateTaskResult containing the task reference
|
CreateTaskResult containing the task reference
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
# Create task
|
# Create task
|
||||||
result = await session.experimental.call_tool_as_task(
|
result = await session.experimental.call_tool_as_task(
|
||||||
"long_running_tool", {"input": "data"}
|
"long_running_tool", {"input": "data"}
|
||||||
|
|
@ -87,6 +90,7 @@ class ExperimentalClientFeatures:
|
||||||
|
|
||||||
# Get result
|
# Get result
|
||||||
final = await session.experimental.get_task_result(task_id, CallToolResult)
|
final = await session.experimental.get_task_result(task_id, CallToolResult)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
return await self._session.send_request(
|
return await self._session.send_request(
|
||||||
types.CallToolRequest(
|
types.CallToolRequest(
|
||||||
|
|
@ -189,6 +193,7 @@ class ExperimentalClientFeatures:
|
||||||
GetTaskResult for each poll
|
GetTaskResult for each poll
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
async for status in session.experimental.poll_task(task_id):
|
async for status in session.experimental.poll_task(task_id):
|
||||||
print(f"Status: {status.status}")
|
print(f"Status: {status.status}")
|
||||||
if status.status == "input_required":
|
if status.status == "input_required":
|
||||||
|
|
@ -197,6 +202,7 @@ class ExperimentalClientFeatures:
|
||||||
|
|
||||||
# Task is now terminal, get the result
|
# Task is now terminal, get the result
|
||||||
result = await session.experimental.get_task_result(task_id, CallToolResult)
|
result = await session.experimental.get_task_result(task_id, CallToolResult)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
async for status in poll_until_terminal(self.get_task, task_id):
|
async for status in poll_until_terminal(self.get_task, task_id):
|
||||||
yield status
|
yield status
|
||||||
|
|
|
||||||
|
|
@ -206,8 +206,10 @@ class ClientSession(
|
||||||
These APIs are experimental and may change without notice.
|
These APIs are experimental and may change without notice.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
status = await session.experimental.get_task(task_id)
|
status = await session.experimental.get_task(task_id)
|
||||||
result = await session.experimental.get_task_result(task_id, CallToolResult)
|
result = await session.experimental.get_task_result(task_id, CallToolResult)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
if self._experimental_features is None:
|
if self._experimental_features is None:
|
||||||
self._experimental_features = ExperimentalClientFeatures(self)
|
self._experimental_features = ExperimentalClientFeatures(self)
|
||||||
|
|
|
||||||
|
|
@ -91,13 +91,14 @@ class ClientSessionGroup:
|
||||||
For auxiliary handlers, such as resource subscription, this is delegated to
|
For auxiliary handlers, such as resource subscription, this is delegated to
|
||||||
the client and can be accessed via the session.
|
the client and can be accessed via the session.
|
||||||
|
|
||||||
Example Usage:
|
Example:
|
||||||
|
```python
|
||||||
name_fn = lambda name, server_info: f"{(server_info.name)}_{name}"
|
name_fn = lambda name, server_info: f"{(server_info.name)}_{name}"
|
||||||
async with ClientSessionGroup(component_name_hook=name_fn) as group:
|
async with ClientSessionGroup(component_name_hook=name_fn) as group:
|
||||||
for server_param in server_params:
|
for server_param in server_params:
|
||||||
await group.connect_to_server(server_param)
|
await group.connect_to_server(server_param)
|
||||||
...
|
...
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class _ComponentNames(BaseModel):
|
class _ComponentNames(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,7 @@ class Experimental:
|
||||||
RuntimeError: If task support is not enabled or task_metadata is missing
|
RuntimeError: If task support is not enabled or task_metadata is missing
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
async def handle_tool(ctx: RequestContext, params: CallToolRequestParams) -> CallToolResult:
|
async def handle_tool(ctx: RequestContext, params: CallToolRequestParams) -> CallToolResult:
|
||||||
async def work(task: ServerTaskContext) -> CallToolResult:
|
async def work(task: ServerTaskContext) -> CallToolResult:
|
||||||
result = await task.elicit(
|
result = await task.elicit(
|
||||||
|
|
@ -170,6 +171,7 @@ class Experimental:
|
||||||
return CallToolResult(content=[TextContent(text="Done" if confirmed else "Cancelled")])
|
return CallToolResult(content=[TextContent(text="Done" if confirmed else "Cancelled")])
|
||||||
|
|
||||||
return await ctx.experimental.run_task(work)
|
return await ctx.experimental.run_task(work)
|
||||||
|
```
|
||||||
|
|
||||||
WARNING: This API is experimental and may change without notice.
|
WARNING: This API is experimental and may change without notice.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ class ServerTaskContext:
|
||||||
- Status notifications via the session
|
- Status notifications via the session
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
async def my_task_work(task: ServerTaskContext) -> CallToolResult:
|
async def my_task_work(task: ServerTaskContext) -> CallToolResult:
|
||||||
await task.update_status("Starting...")
|
await task.update_status("Starting...")
|
||||||
|
|
||||||
|
|
@ -68,6 +69,7 @@ class ServerTaskContext:
|
||||||
return CallToolResult(content=[TextContent(text="Done!")])
|
return CallToolResult(content=[TextContent(text="Done!")])
|
||||||
else:
|
else:
|
||||||
return CallToolResult(content=[TextContent(text="Cancelled")])
|
return CallToolResult(content=[TextContent(text="Cancelled")])
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
|
||||||
|
|
@ -31,14 +31,20 @@ class TaskSupport:
|
||||||
- Manages a task group for background task execution
|
- Manages a task group for background task execution
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
# Simple in-memory setup
|
Simple in-memory setup:
|
||||||
server.experimental.enable_tasks()
|
|
||||||
|
|
||||||
# Custom store/queue for distributed systems
|
```python
|
||||||
|
server.experimental.enable_tasks()
|
||||||
|
```
|
||||||
|
|
||||||
|
Custom store/queue for distributed systems:
|
||||||
|
|
||||||
|
```python
|
||||||
server.experimental.enable_tasks(
|
server.experimental.enable_tasks(
|
||||||
store=RedisTaskStore(redis_url),
|
store=RedisTaskStore(redis_url),
|
||||||
queue=RedisTaskMessageQueue(redis_url),
|
queue=RedisTaskMessageQueue(redis_url),
|
||||||
)
|
)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
store: TaskStore
|
store: TaskStore
|
||||||
|
|
|
||||||
|
|
@ -118,14 +118,20 @@ class ExperimentalHandlers(Generic[LifespanResultT]):
|
||||||
The TaskSupport configuration object
|
The TaskSupport configuration object
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
# Simple in-memory setup
|
Simple in-memory setup:
|
||||||
server.experimental.enable_tasks()
|
|
||||||
|
|
||||||
# Custom store/queue for distributed systems
|
```python
|
||||||
|
server.experimental.enable_tasks()
|
||||||
|
```
|
||||||
|
|
||||||
|
Custom store/queue for distributed systems:
|
||||||
|
|
||||||
|
```python
|
||||||
server.experimental.enable_tasks(
|
server.experimental.enable_tasks(
|
||||||
store=RedisTaskStore(redis_url),
|
store=RedisTaskStore(redis_url),
|
||||||
queue=RedisTaskMessageQueue(redis_url),
|
queue=RedisTaskMessageQueue(redis_url),
|
||||||
)
|
)
|
||||||
|
```
|
||||||
|
|
||||||
WARNING: This API is experimental and may change without notice.
|
WARNING: This API is experimental and may change without notice.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -535,19 +535,25 @@ class MCPServer(Generic[LifespanResultT]):
|
||||||
- If False, unconditionally creates an unstructured tool
|
- If False, unconditionally creates an unstructured tool
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
@server.tool()
|
@server.tool()
|
||||||
def my_tool(x: int) -> str:
|
def my_tool(x: int) -> str:
|
||||||
return str(x)
|
return str(x)
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
@server.tool()
|
@server.tool()
|
||||||
async def tool_with_context(x: int, ctx: Context) -> str:
|
async def tool_with_context(x: int, ctx: Context) -> str:
|
||||||
await ctx.info(f"Processing {x}")
|
await ctx.info(f"Processing {x}")
|
||||||
return str(x)
|
return str(x)
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
@server.tool()
|
@server.tool()
|
||||||
async def async_tool(x: int, context: Context) -> str:
|
async def async_tool(x: int, context: Context) -> str:
|
||||||
await context.report_progress(50, 100)
|
await context.report_progress(50, 100)
|
||||||
return str(x)
|
return str(x)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
# Check if user passed function directly instead of calling decorator
|
# Check if user passed function directly instead of calling decorator
|
||||||
if callable(name):
|
if callable(name):
|
||||||
|
|
@ -579,12 +585,14 @@ class MCPServer(Generic[LifespanResultT]):
|
||||||
- context: Optional CompletionContext with previously resolved arguments
|
- context: Optional CompletionContext with previously resolved arguments
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
@mcp.completion()
|
@mcp.completion()
|
||||||
async def handle_completion(ref, argument, context):
|
async def handle_completion(ref, argument, context):
|
||||||
if isinstance(ref, ResourceTemplateReference):
|
if isinstance(ref, ResourceTemplateReference):
|
||||||
# Return completions based on ref, argument, and context
|
# Return completions based on ref, argument, and context
|
||||||
return Completion(values=["option1", "option2"])
|
return Completion(values=["option1", "option2"])
|
||||||
return None
|
return None
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(func: _CallableT) -> _CallableT:
|
def decorator(func: _CallableT) -> _CallableT:
|
||||||
|
|
@ -647,6 +655,7 @@ class MCPServer(Generic[LifespanResultT]):
|
||||||
meta: Optional metadata dictionary for the resource
|
meta: Optional metadata dictionary for the resource
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
@server.resource("resource://my-resource")
|
@server.resource("resource://my-resource")
|
||||||
def get_data() -> str:
|
def get_data() -> str:
|
||||||
return "Hello, world!"
|
return "Hello, world!"
|
||||||
|
|
@ -664,6 +673,7 @@ class MCPServer(Generic[LifespanResultT]):
|
||||||
async def get_weather(city: str) -> str:
|
async def get_weather(city: str) -> str:
|
||||||
data = await fetch_weather(city)
|
data = await fetch_weather(city)
|
||||||
return f"Weather for {city}: {data}"
|
return f"Weather for {city}: {data}"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
# Check if user passed function directly instead of calling decorator
|
# Check if user passed function directly instead of calling decorator
|
||||||
if callable(uri):
|
if callable(uri):
|
||||||
|
|
@ -747,6 +757,7 @@ class MCPServer(Generic[LifespanResultT]):
|
||||||
icons: Optional list of icons for the prompt
|
icons: Optional list of icons for the prompt
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
@server.prompt()
|
@server.prompt()
|
||||||
def analyze_table(table_name: str) -> list[Message]:
|
def analyze_table(table_name: str) -> list[Message]:
|
||||||
schema = read_table_schema(table_name)
|
schema = read_table_schema(table_name)
|
||||||
|
|
@ -772,6 +783,7 @@ class MCPServer(Generic[LifespanResultT]):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
# Check if user passed function directly instead of calling decorator
|
# Check if user passed function directly instead of calling decorator
|
||||||
if callable(name):
|
if callable(name):
|
||||||
|
|
@ -813,9 +825,11 @@ class MCPServer(Generic[LifespanResultT]):
|
||||||
include_in_schema: Whether to include in OpenAPI schema, defaults to True
|
include_in_schema: Whether to include in OpenAPI schema, defaults to True
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
@server.custom_route("/health", methods=["GET"])
|
@server.custom_route("/health", methods=["GET"])
|
||||||
async def health_check(request: Request) -> Response:
|
async def health_check(request: Request) -> Response:
|
||||||
return JSONResponse({"status": "ok"})
|
return JSONResponse({"status": "ok"})
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator( # pragma: no cover
|
def decorator( # pragma: no cover
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
This module implements a Server-Sent Events (SSE) transport layer for MCP servers.
|
This module implements a Server-Sent Events (SSE) transport layer for MCP servers.
|
||||||
|
|
||||||
Example usage:
|
Example:
|
||||||
```
|
```python
|
||||||
# Create an SSE transport at an endpoint
|
# Create an SSE transport at an endpoint
|
||||||
sse = SseServerTransport("/messages/")
|
sse = SseServerTransport("/messages/")
|
||||||
|
|
||||||
|
|
@ -27,7 +27,7 @@ Example usage:
|
||||||
# Create and run Starlette app
|
# Create and run Starlette app
|
||||||
starlette_app = Starlette(routes=routes)
|
starlette_app = Starlette(routes=routes)
|
||||||
uvicorn.run(starlette_app, host="127.0.0.1", port=port)
|
uvicorn.run(starlette_app, host="127.0.0.1", port=port)
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: The handle_sse function must return a Response to avoid a
|
Note: The handle_sse function must return a Response to avoid a
|
||||||
"TypeError: 'NoneType' object is not callable" error when client disconnects. The example above returns
|
"TypeError: 'NoneType' object is not callable" error when client disconnects. The example above returns
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ This module provides functionality for creating an stdio-based transport layer
|
||||||
that can be used to communicate with an MCP client through standard input/output
|
that can be used to communicate with an MCP client through standard input/output
|
||||||
streams.
|
streams.
|
||||||
|
|
||||||
Example usage:
|
Example:
|
||||||
```
|
```python
|
||||||
async def run_server():
|
async def run_server():
|
||||||
async with stdio_server() as (read_stream, write_stream):
|
async with stdio_server() as (read_stream, write_stream):
|
||||||
# read_stream contains incoming JSONRPCMessages from stdin
|
# read_stream contains incoming JSONRPCMessages from stdin
|
||||||
|
|
@ -14,7 +14,7 @@ Example usage:
|
||||||
await server.run(read_stream, write_stream, init_options)
|
await server.run(read_stream, write_stream, init_options)
|
||||||
|
|
||||||
anyio.run(run_server)
|
anyio.run(run_server)
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
|
||||||
|
|
@ -44,26 +44,38 @@ def create_mcp_http_client(
|
||||||
The returned AsyncClient must be used as a context manager to ensure
|
The returned AsyncClient must be used as a context manager to ensure
|
||||||
proper cleanup of connections.
|
proper cleanup of connections.
|
||||||
|
|
||||||
Examples:
|
Example:
|
||||||
# Basic usage with MCP defaults
|
Basic usage with MCP defaults:
|
||||||
|
|
||||||
|
```python
|
||||||
async with create_mcp_http_client() as client:
|
async with create_mcp_http_client() as client:
|
||||||
response = await client.get("https://api.example.com")
|
response = await client.get("https://api.example.com")
|
||||||
|
```
|
||||||
|
|
||||||
# With custom headers
|
With custom headers:
|
||||||
|
|
||||||
|
```python
|
||||||
headers = {"Authorization": "Bearer token"}
|
headers = {"Authorization": "Bearer token"}
|
||||||
async with create_mcp_http_client(headers) as client:
|
async with create_mcp_http_client(headers) as client:
|
||||||
response = await client.get("/endpoint")
|
response = await client.get("/endpoint")
|
||||||
|
```
|
||||||
|
|
||||||
# With both custom headers and timeout
|
With both custom headers and timeout:
|
||||||
|
|
||||||
|
```python
|
||||||
timeout = httpx.Timeout(60.0, read=300.0)
|
timeout = httpx.Timeout(60.0, read=300.0)
|
||||||
async with create_mcp_http_client(headers, timeout) as client:
|
async with create_mcp_http_client(headers, timeout) as client:
|
||||||
response = await client.get("/long-request")
|
response = await client.get("/long-request")
|
||||||
|
```
|
||||||
|
|
||||||
# With authentication
|
With authentication:
|
||||||
|
|
||||||
|
```python
|
||||||
from httpx import BasicAuth
|
from httpx import BasicAuth
|
||||||
auth = BasicAuth(username="user", password="pass")
|
auth = BasicAuth(username="user", password="pass")
|
||||||
async with create_mcp_http_client(headers, timeout, auth) as client:
|
async with create_mcp_http_client(headers, timeout, auth) as client:
|
||||||
response = await client.get("/protected-endpoint")
|
response = await client.get("/protected-endpoint")
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
# Set MCP defaults
|
# Set MCP defaults
|
||||||
kwargs: dict[str, Any] = {"follow_redirects": True}
|
kwargs: dict[str, Any] = {"follow_redirects": True}
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ class UrlElicitationRequiredError(MCPError):
|
||||||
must complete one or more URL elicitations before the request can be processed.
|
must complete one or more URL elicitations before the request can be processed.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
raise UrlElicitationRequiredError([
|
raise UrlElicitationRequiredError([
|
||||||
ElicitRequestURLParams(
|
ElicitRequestURLParams(
|
||||||
message="Authorization required for your files",
|
message="Authorization required for your files",
|
||||||
|
|
@ -72,6 +73,7 @@ class UrlElicitationRequiredError(MCPError):
|
||||||
elicitation_id="auth-001"
|
elicitation_id="auth-001"
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, elicitations: list[ElicitRequestURLParams], message: str | None = None):
|
def __init__(self, elicitations: list[ElicitRequestURLParams], message: str | None = None):
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,10 @@ async def cancel_task(
|
||||||
- Task is already in a terminal state (completed, failed, cancelled)
|
- Task is already in a terminal state (completed, failed, cancelled)
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
async def handle_cancel(ctx, params: CancelTaskRequestParams) -> CancelTaskResult:
|
async def handle_cancel(ctx, params: CancelTaskRequestParams) -> CancelTaskResult:
|
||||||
return await cancel_task(store, params.task_id)
|
return await cancel_task(store, params.task_id)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
task = await store.get_task(task_id)
|
task = await store.get_task(task_id)
|
||||||
if task is None:
|
if task is None:
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,13 @@ def get_display_name(obj: Tool | Resource | Prompt | ResourceTemplate | Implemen
|
||||||
For other objects: title > name
|
For other objects: title > name
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
# In a client displaying available tools
|
# In a client displaying available tools
|
||||||
tools = await session.list_tools()
|
tools = await session.list_tools()
|
||||||
for tool in tools.tools:
|
for tool in tools.tools:
|
||||||
display_name = get_display_name(tool)
|
display_name = get_display_name(tool)
|
||||||
print(f"Available tool: {display_name}")
|
print(f"Available tool: {display_name}")
|
||||||
|
```
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
obj: An MCP object with name and optional title fields
|
obj: An MCP object with name and optional title fields
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ class ResponseRouter(Protocol):
|
||||||
and deliver the response/error to the appropriate handler.
|
and deliver the response/error to the appropriate handler.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
class TaskResultHandler(ResponseRouter):
|
class TaskResultHandler(ResponseRouter):
|
||||||
def route_response(self, request_id, response):
|
def route_response(self, request_id, response):
|
||||||
resolver = self._pending_requests.pop(request_id, None)
|
resolver = self._pending_requests.pop(request_id, None)
|
||||||
|
|
@ -32,6 +33,7 @@ class ResponseRouter(Protocol):
|
||||||
resolver.set_result(response)
|
resolver.set_result(response)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def route_response(self, request_id: RequestId, response: dict[str, Any]) -> bool:
|
def route_response(self, request_id: RequestId, response: dict[str, Any]) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,10 @@ class RequestResponder(Generic[ReceiveRequestT, SendResultT]):
|
||||||
cancellation handling:
|
cancellation handling:
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```python
|
||||||
with request_responder as resp:
|
with request_responder as resp:
|
||||||
await resp.respond(result)
|
await resp.respond(result)
|
||||||
|
```
|
||||||
|
|
||||||
The context manager ensures:
|
The context manager ensures:
|
||||||
1. Proper cancellation scope setup and cleanup
|
1. Proper cancellation scope setup and cleanup
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,8 @@ class StreamSpyCollection:
|
||||||
def stream_spy() -> Generator[Callable[[], StreamSpyCollection], None, None]:
|
def stream_spy() -> Generator[Callable[[], StreamSpyCollection], None, None]:
|
||||||
"""Fixture that provides spies for both client and server write streams.
|
"""Fixture that provides spies for both client and server write streams.
|
||||||
|
|
||||||
Example usage:
|
Example:
|
||||||
|
```python
|
||||||
async def test_something(stream_spy):
|
async def test_something(stream_spy):
|
||||||
# ... set up server and client ...
|
# ... set up server and client ...
|
||||||
|
|
||||||
|
|
@ -92,6 +93,7 @@ def stream_spy() -> Generator[Callable[[], StreamSpyCollection], None, None]:
|
||||||
|
|
||||||
# Clear for the next operation
|
# Clear for the next operation
|
||||||
spies.clear()
|
spies.clear()
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
client_spy = None
|
client_spy = None
|
||||||
server_spy = None
|
server_spy = None
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue