Skip to content

Commit e7cae8e

Browse files
committed
deploy: 940e6ee
1 parent 492861a commit e7cae8e

2 files changed

Lines changed: 82 additions & 14 deletions

File tree

protocols/index.html

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -894,12 +894,44 @@ <h3>Inherited members</h3>
894894
logger.debug(f&#34;MCP session cleanup cancelled {context}&#34;)
895895
return
896896

897-
# Handle ExceptionGroup from task group failures (Python 3.11+)
898-
if _ExceptionGroup is not None and isinstance(
899-
cleanup_error, _ExceptionGroup
900-
):
901-
for exc in cleanup_error.exceptions: # type: ignore[attr-defined]
902-
self._log_cleanup_error(exc, context)
897+
# Handle ExceptionGroup/BaseExceptionGroup from task group failures (Python 3.11+)
898+
# ExceptionGroup: for Exception subclasses (e.g., HTTPStatusError)
899+
# BaseExceptionGroup: for BaseException subclasses (e.g., CancelledError)
900+
# We need both because CancelledError is a BaseException, not an Exception
901+
is_exception_group = (
902+
_ExceptionGroup is not None and isinstance(cleanup_error, _ExceptionGroup)
903+
) or (
904+
_BaseExceptionGroup is not None
905+
and isinstance(cleanup_error, _BaseExceptionGroup)
906+
)
907+
908+
if is_exception_group:
909+
# Check if all exceptions in the group are CancelledError
910+
# If so, treat the entire group as a cancellation
911+
all_cancelled = all(
912+
isinstance(exc, asyncio.CancelledError)
913+
for exc in cleanup_error.exceptions # type: ignore[attr-defined]
914+
)
915+
if all_cancelled:
916+
logger.debug(f&#34;MCP session cleanup cancelled {context}&#34;)
917+
return
918+
919+
# Mixed group: skip CancelledErrors and log real errors
920+
exceptions = cleanup_error.exceptions # type: ignore[attr-defined]
921+
cancelled_errors = [
922+
exc for exc in exceptions if isinstance(exc, asyncio.CancelledError)
923+
]
924+
cancelled_count = len(cancelled_errors)
925+
if cancelled_count &gt; 0:
926+
logger.debug(
927+
f&#34;Skipping {cancelled_count} CancelledError(s) &#34;
928+
f&#34;in mixed exception group {context}&#34;
929+
)
930+
931+
# Log each non-cancelled exception individually
932+
for exc in exceptions:
933+
if not isinstance(exc, asyncio.CancelledError):
934+
self._log_cleanup_error(exc, context)
903935
else:
904936
self._log_cleanup_error(cleanup_error, context)
905937

@@ -914,7 +946,9 @@ <h3>Inherited members</h3>
914946
and (&#34;cancel scope&#34; in exc_str or &#34;async context&#34; in exc_str)
915947
) or (
916948
# HTTP errors during cleanup (if httpx is available)
917-
HTTPX_AVAILABLE and HTTPStatusError is not None and isinstance(exc, HTTPStatusError)
949+
HTTPX_AVAILABLE
950+
and HTTPStatusError is not None
951+
and isinstance(exc, HTTPStatusError)
918952
)
919953

920954
if is_known_cleanup_error:

protocols/mcp.html

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,44 @@ <h2 class="section-title" id="header-classes">Classes</h2>
9393
logger.debug(f&#34;MCP session cleanup cancelled {context}&#34;)
9494
return
9595

96-
# Handle ExceptionGroup from task group failures (Python 3.11+)
97-
if _ExceptionGroup is not None and isinstance(
98-
cleanup_error, _ExceptionGroup
99-
):
100-
for exc in cleanup_error.exceptions: # type: ignore[attr-defined]
101-
self._log_cleanup_error(exc, context)
96+
# Handle ExceptionGroup/BaseExceptionGroup from task group failures (Python 3.11+)
97+
# ExceptionGroup: for Exception subclasses (e.g., HTTPStatusError)
98+
# BaseExceptionGroup: for BaseException subclasses (e.g., CancelledError)
99+
# We need both because CancelledError is a BaseException, not an Exception
100+
is_exception_group = (
101+
_ExceptionGroup is not None and isinstance(cleanup_error, _ExceptionGroup)
102+
) or (
103+
_BaseExceptionGroup is not None
104+
and isinstance(cleanup_error, _BaseExceptionGroup)
105+
)
106+
107+
if is_exception_group:
108+
# Check if all exceptions in the group are CancelledError
109+
# If so, treat the entire group as a cancellation
110+
all_cancelled = all(
111+
isinstance(exc, asyncio.CancelledError)
112+
for exc in cleanup_error.exceptions # type: ignore[attr-defined]
113+
)
114+
if all_cancelled:
115+
logger.debug(f&#34;MCP session cleanup cancelled {context}&#34;)
116+
return
117+
118+
# Mixed group: skip CancelledErrors and log real errors
119+
exceptions = cleanup_error.exceptions # type: ignore[attr-defined]
120+
cancelled_errors = [
121+
exc for exc in exceptions if isinstance(exc, asyncio.CancelledError)
122+
]
123+
cancelled_count = len(cancelled_errors)
124+
if cancelled_count &gt; 0:
125+
logger.debug(
126+
f&#34;Skipping {cancelled_count} CancelledError(s) &#34;
127+
f&#34;in mixed exception group {context}&#34;
128+
)
129+
130+
# Log each non-cancelled exception individually
131+
for exc in exceptions:
132+
if not isinstance(exc, asyncio.CancelledError):
133+
self._log_cleanup_error(exc, context)
102134
else:
103135
self._log_cleanup_error(cleanup_error, context)
104136

@@ -113,7 +145,9 @@ <h2 class="section-title" id="header-classes">Classes</h2>
113145
and (&#34;cancel scope&#34; in exc_str or &#34;async context&#34; in exc_str)
114146
) or (
115147
# HTTP errors during cleanup (if httpx is available)
116-
HTTPX_AVAILABLE and HTTPStatusError is not None and isinstance(exc, HTTPStatusError)
148+
HTTPX_AVAILABLE
149+
and HTTPStatusError is not None
150+
and isinstance(exc, HTTPStatusError)
117151
)
118152

119153
if is_known_cleanup_error:

0 commit comments

Comments
 (0)