Get baseline 100% clean coverage (#1553)

This commit is contained in:
Max Isbey 2025-11-11 13:09:32 +00:00 • committed by GitHub
commit 89e9c43acf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 1032 additions and 1084 deletions

View file

@ -7,8 +7,8 @@ import httpx
__all__ = ["create_mcp_http_client"]
class McpHttpClientFactory(Protocol):
def __call__(
class McpHttpClientFactory(Protocol): # pragma: no branch
def __call__( # pragma: no branch
self,
headers: dict[str, str] | None = None,
timeout: httpx.Timeout | None = None,
@ -77,7 +77,7 @@ def create_mcp_http_client(
kwargs["headers"] = headers
# Handle authentication
if auth is not None:
if auth is not None: # pragma: no cover
kwargs["auth"] = auth
return httpx.AsyncClient(**kwargs)

View file

@ -21,7 +21,7 @@ class OAuthToken(BaseModel):
# Bearer is title-cased in the spec, so we normalize it
# https://datatracker.ietf.org/doc/html/rfc6750#section-4
return v.title()
return v
return v # pragma: no cover
class InvalidScopeError(Exception):
@ -75,9 +75,9 @@ class OAuthClientMetadata(BaseModel):
requested_scopes = requested_scope.split(" ")
allowed_scopes = [] if self.scope is None else self.scope.split(" ")
for scope in requested_scopes:
if scope not in allowed_scopes:
if scope not in allowed_scopes: # pragma: no branch
raise InvalidScopeError(f"Client was not registered with scope {scope}")
return requested_scopes
return requested_scopes # pragma: no cover
def validate_redirect_uri(self, redirect_uri: AnyUrl | None) -> AnyUrl:
if redirect_uri is not None:

View file

@ -62,7 +62,7 @@ async def create_connected_server_and_client_session(
# TODO(Marcelo): we should have a proper `Client` that can use this "in-memory transport",
# and we should expose a method in the `FastMCP` so we don't access a private attribute.
if isinstance(server, FastMCP):
if isinstance(server, FastMCP): # pragma: no cover
server = server._mcp_server # type: ignore[reportPrivateUsage]
async with create_client_server_memory_streams() as (client_streams, server_streams):
@ -94,5 +94,5 @@ async def create_connected_server_and_client_session(
) as client_session:
await client_session.initialize()
yield client_session
finally:
finally: # pragma: no cover
tg.cancel_scope.cancel()

View file

@ -48,7 +48,7 @@ def progress(
ProgressContext[SendRequestT, SendNotificationT, SendResultT, ReceiveRequestT, ReceiveNotificationT],
None,
]:
if ctx.meta is None or ctx.meta.progressToken is None:
if ctx.meta is None or ctx.meta.progressToken is None: # pragma: no cover
raise ValueError("No progress token provided")
progress_ctx = ProgressContext(ctx.session, ctx.meta.progressToken, total)

View file

@ -46,7 +46,9 @@ RequestId = str | int
class ProgressFnT(Protocol):
"""Protocol for progress notification callbacks."""
async def __call__(self, progress: float, total: float | None, message: str | None) -> None: ...
async def __call__(
self, progress: float, total: float | None, message: str | None
) -> None: ... # pragma: no branch
class RequestResponder(Generic[ReceiveRequestT, SendResultT]):
@ -105,11 +107,11 @@ class RequestResponder(Generic[ReceiveRequestT, SendResultT]):
) -> None:
"""Exit the context manager, performing cleanup and notifying completion."""
try:
if self._completed:
if self._completed: # pragma: no branch
self._on_complete(self)
finally:
self._entered = False
if not self._cancel_scope:
if not self._cancel_scope: # pragma: no cover
raise RuntimeError("No active cancel scope")
self._cancel_scope.__exit__(exc_type, exc_val, exc_tb)
@ -121,11 +123,11 @@ class RequestResponder(Generic[ReceiveRequestT, SendResultT]):
RuntimeError: If not used within a context manager
AssertionError: If request was already responded to
"""
if not self._entered:
if not self._entered: # pragma: no cover
raise RuntimeError("RequestResponder must be used as a context manager")
assert not self._completed, "Request already responded to"
if not self.cancelled:
if not self.cancelled: # pragma: no branch
self._completed = True
await self._session._send_response( # type: ignore[reportPrivateUsage]
@ -134,9 +136,9 @@ class RequestResponder(Generic[ReceiveRequestT, SendResultT]):
async def cancel(self) -> None:
"""Cancel this request and mark it as completed."""
if not self._entered:
if not self._entered: # pragma: no cover
raise RuntimeError("RequestResponder must be used as a context manager")
if not self._cancel_scope:
if not self._cancel_scope: # pragma: no cover
raise RuntimeError("No active cancel scope")
self._cancel_scope.cancel()
@ -148,11 +150,11 @@ class RequestResponder(Generic[ReceiveRequestT, SendResultT]):
)
@property
def in_flight(self) -> bool:
def in_flight(self) -> bool: # pragma: no cover
return not self._completed and not self.cancelled
@property
def cancelled(self) -> bool:
def cancelled(self) -> bool: # pragma: no cover
return self._cancel_scope.cancel_called
@ -241,11 +243,11 @@ class BaseSession(
# Set up progress token if progress callback is provided
request_data = request.model_dump(by_alias=True, mode="json", exclude_none=True)
if progress_callback is not None:
if progress_callback is not None: # pragma: no cover
# Use request_id as progress token
if "params" not in request_data:
request_data["params"] = {}
if "_meta" not in request_data["params"]:
if "_meta" not in request_data["params"]: # pragma: no branch
request_data["params"]["_meta"] = {}
request_data["params"]["_meta"]["progressToken"] = request_id
# Store the callback for this request
@ -262,9 +264,9 @@ class BaseSession(
# request read timeout takes precedence over session read timeout
timeout = None
if request_read_timeout_seconds is not None:
if request_read_timeout_seconds is not None: # pragma: no cover
timeout = request_read_timeout_seconds.total_seconds()
elif self._session_read_timeout_seconds is not None:
elif self._session_read_timeout_seconds is not None: # pragma: no cover
timeout = self._session_read_timeout_seconds.total_seconds()
try:
@ -308,7 +310,7 @@ class BaseSession(
jsonrpc="2.0",
**notification.model_dump(by_alias=True, mode="json", exclude_none=True),
)
session_message = SessionMessage(
session_message = SessionMessage( # pragma: no cover
message=JSONRPCMessage(jsonrpc_notification),
metadata=ServerMessageMetadata(related_request_id=related_request_id) if related_request_id else None,
)
@ -335,7 +337,7 @@ class BaseSession(
):
try:
async for message in self._read_stream:
if isinstance(message, Exception):
if isinstance(message, Exception): # pragma: no cover
await self._handle_incoming(message)
elif isinstance(message.message.root, JSONRPCRequest):
try:
@ -382,11 +384,11 @@ class BaseSession(
# Handle cancellation notifications
if isinstance(notification.root, CancelledNotification):
cancelled_id = notification.root.params.requestId
if cancelled_id in self._in_flight:
if cancelled_id in self._in_flight: # pragma: no branch
await self._in_flight[cancelled_id].cancel()
else:
# Handle progress notifications callback
if isinstance(notification.root, ProgressNotification):
if isinstance(notification.root, ProgressNotification): # pragma: no cover
progress_token = notification.root.params.progressToken
# If there is a progress callback for this token,
# call it with the progress information
@ -405,16 +407,16 @@ class BaseSession(
)
await self._received_notification(notification)
await self._handle_incoming(notification)
except Exception as e:
except Exception as e: # pragma: no cover
# For other validation errors, log and continue
logging.warning(
f"Failed to validate notification: {e}. Message was: {message.message.root}"
)
else: # Response or error
stream = self._response_streams.pop(message.message.root.id, None)
if stream:
if stream: # pragma: no cover
await stream.send(message.message.root)
else:
else: # pragma: no cover
await self._handle_incoming(
RuntimeError(f"Received response with an unknown request ID: {message}")
)
@ -423,8 +425,8 @@ class BaseSession(
# This is expected when the client disconnects abruptly.
# Without this handler, the exception would propagate up and
# crash the server's task group.
logging.debug("Read stream closed by client")
except Exception as e:
logging.debug("Read stream closed by client") # pragma: no cover
except Exception as e: # pragma: no cover
# Other exceptions are not expected and should be logged. We purposefully
# catch all exceptions here to avoid crashing the server.
logging.exception(f"Unhandled exception in receive loop: {e}")
@ -436,7 +438,7 @@ class BaseSession(
try:
await stream.send(JSONRPCError(jsonrpc="2.0", id=id, error=error))
await stream.aclose()
except Exception:
except Exception: # pragma: no cover
# Stream might already be closed
pass
self._response_streams.clear()
@ -473,4 +475,4 @@ class BaseSession(
req: RequestResponder[ReceiveRequestT, SendResultT] | ReceiveNotificationT | Exception,
) -> None:
"""A generic handler for incoming messages. Overwritten by subclasses."""
pass
pass # pragma: no cover