fastmcp 2.14.5__py3-none-any.whl → 3.0.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastmcp/_vendor/__init__.py +1 -0
- fastmcp/_vendor/docket_di/README.md +7 -0
- fastmcp/_vendor/docket_di/__init__.py +163 -0
- fastmcp/cli/cli.py +112 -28
- fastmcp/cli/install/claude_code.py +1 -5
- fastmcp/cli/install/claude_desktop.py +1 -5
- fastmcp/cli/install/cursor.py +1 -5
- fastmcp/cli/install/gemini_cli.py +1 -5
- fastmcp/cli/install/mcp_json.py +1 -6
- fastmcp/cli/run.py +146 -5
- fastmcp/client/__init__.py +7 -9
- fastmcp/client/auth/oauth.py +18 -17
- fastmcp/client/client.py +100 -870
- fastmcp/client/elicitation.py +1 -1
- fastmcp/client/mixins/__init__.py +13 -0
- fastmcp/client/mixins/prompts.py +295 -0
- fastmcp/client/mixins/resources.py +325 -0
- fastmcp/client/mixins/task_management.py +157 -0
- fastmcp/client/mixins/tools.py +397 -0
- fastmcp/client/sampling/handlers/anthropic.py +2 -2
- fastmcp/client/sampling/handlers/openai.py +1 -1
- fastmcp/client/tasks.py +3 -3
- fastmcp/client/telemetry.py +47 -0
- fastmcp/client/transports/__init__.py +38 -0
- fastmcp/client/transports/base.py +82 -0
- fastmcp/client/transports/config.py +170 -0
- fastmcp/client/transports/http.py +145 -0
- fastmcp/client/transports/inference.py +154 -0
- fastmcp/client/transports/memory.py +90 -0
- fastmcp/client/transports/sse.py +89 -0
- fastmcp/client/transports/stdio.py +543 -0
- fastmcp/contrib/component_manager/README.md +4 -10
- fastmcp/contrib/component_manager/__init__.py +1 -2
- fastmcp/contrib/component_manager/component_manager.py +95 -160
- fastmcp/contrib/component_manager/example.py +1 -1
- fastmcp/contrib/mcp_mixin/example.py +4 -4
- fastmcp/contrib/mcp_mixin/mcp_mixin.py +11 -4
- fastmcp/decorators.py +41 -0
- fastmcp/dependencies.py +12 -1
- fastmcp/exceptions.py +4 -0
- fastmcp/experimental/server/openapi/__init__.py +18 -15
- fastmcp/mcp_config.py +13 -4
- fastmcp/prompts/__init__.py +6 -3
- fastmcp/prompts/function_prompt.py +465 -0
- fastmcp/prompts/prompt.py +321 -271
- fastmcp/resources/__init__.py +5 -3
- fastmcp/resources/function_resource.py +335 -0
- fastmcp/resources/resource.py +325 -115
- fastmcp/resources/template.py +215 -43
- fastmcp/resources/types.py +27 -12
- fastmcp/server/__init__.py +2 -2
- fastmcp/server/auth/__init__.py +14 -0
- fastmcp/server/auth/auth.py +30 -10
- fastmcp/server/auth/authorization.py +190 -0
- fastmcp/server/auth/oauth_proxy/__init__.py +14 -0
- fastmcp/server/auth/oauth_proxy/consent.py +361 -0
- fastmcp/server/auth/oauth_proxy/models.py +178 -0
- fastmcp/server/auth/{oauth_proxy.py → oauth_proxy/proxy.py} +24 -778
- fastmcp/server/auth/oauth_proxy/ui.py +277 -0
- fastmcp/server/auth/oidc_proxy.py +2 -2
- fastmcp/server/auth/providers/auth0.py +24 -94
- fastmcp/server/auth/providers/aws.py +26 -95
- fastmcp/server/auth/providers/azure.py +41 -129
- fastmcp/server/auth/providers/descope.py +18 -49
- fastmcp/server/auth/providers/discord.py +25 -86
- fastmcp/server/auth/providers/github.py +23 -87
- fastmcp/server/auth/providers/google.py +24 -87
- fastmcp/server/auth/providers/introspection.py +60 -79
- fastmcp/server/auth/providers/jwt.py +30 -67
- fastmcp/server/auth/providers/oci.py +47 -110
- fastmcp/server/auth/providers/scalekit.py +23 -61
- fastmcp/server/auth/providers/supabase.py +18 -47
- fastmcp/server/auth/providers/workos.py +34 -127
- fastmcp/server/context.py +372 -419
- fastmcp/server/dependencies.py +541 -251
- fastmcp/server/elicitation.py +20 -18
- fastmcp/server/event_store.py +3 -3
- fastmcp/server/http.py +16 -6
- fastmcp/server/lifespan.py +198 -0
- fastmcp/server/low_level.py +92 -2
- fastmcp/server/middleware/__init__.py +5 -1
- fastmcp/server/middleware/authorization.py +312 -0
- fastmcp/server/middleware/caching.py +101 -54
- fastmcp/server/middleware/middleware.py +6 -9
- fastmcp/server/middleware/ping.py +70 -0
- fastmcp/server/middleware/tool_injection.py +2 -2
- fastmcp/server/mixins/__init__.py +7 -0
- fastmcp/server/mixins/lifespan.py +217 -0
- fastmcp/server/mixins/mcp_operations.py +392 -0
- fastmcp/server/mixins/transport.py +342 -0
- fastmcp/server/openapi/__init__.py +41 -21
- fastmcp/server/openapi/components.py +16 -339
- fastmcp/server/openapi/routing.py +34 -118
- fastmcp/server/openapi/server.py +67 -392
- fastmcp/server/providers/__init__.py +71 -0
- fastmcp/server/providers/aggregate.py +261 -0
- fastmcp/server/providers/base.py +578 -0
- fastmcp/server/providers/fastmcp_provider.py +674 -0
- fastmcp/server/providers/filesystem.py +226 -0
- fastmcp/server/providers/filesystem_discovery.py +327 -0
- fastmcp/server/providers/local_provider/__init__.py +11 -0
- fastmcp/server/providers/local_provider/decorators/__init__.py +15 -0
- fastmcp/server/providers/local_provider/decorators/prompts.py +256 -0
- fastmcp/server/providers/local_provider/decorators/resources.py +240 -0
- fastmcp/server/providers/local_provider/decorators/tools.py +315 -0
- fastmcp/server/providers/local_provider/local_provider.py +465 -0
- fastmcp/server/providers/openapi/__init__.py +39 -0
- fastmcp/server/providers/openapi/components.py +332 -0
- fastmcp/server/providers/openapi/provider.py +405 -0
- fastmcp/server/providers/openapi/routing.py +109 -0
- fastmcp/server/providers/proxy.py +867 -0
- fastmcp/server/providers/skills/__init__.py +59 -0
- fastmcp/server/providers/skills/_common.py +101 -0
- fastmcp/server/providers/skills/claude_provider.py +44 -0
- fastmcp/server/providers/skills/directory_provider.py +153 -0
- fastmcp/server/providers/skills/skill_provider.py +432 -0
- fastmcp/server/providers/skills/vendor_providers.py +142 -0
- fastmcp/server/providers/wrapped_provider.py +140 -0
- fastmcp/server/proxy.py +34 -700
- fastmcp/server/sampling/run.py +341 -2
- fastmcp/server/sampling/sampling_tool.py +4 -3
- fastmcp/server/server.py +1214 -2171
- fastmcp/server/tasks/__init__.py +2 -1
- fastmcp/server/tasks/capabilities.py +13 -1
- fastmcp/server/tasks/config.py +66 -3
- fastmcp/server/tasks/handlers.py +65 -273
- fastmcp/server/tasks/keys.py +4 -6
- fastmcp/server/tasks/requests.py +474 -0
- fastmcp/server/tasks/routing.py +76 -0
- fastmcp/server/tasks/subscriptions.py +20 -11
- fastmcp/server/telemetry.py +131 -0
- fastmcp/server/transforms/__init__.py +244 -0
- fastmcp/server/transforms/namespace.py +193 -0
- fastmcp/server/transforms/prompts_as_tools.py +175 -0
- fastmcp/server/transforms/resources_as_tools.py +190 -0
- fastmcp/server/transforms/tool_transform.py +96 -0
- fastmcp/server/transforms/version_filter.py +124 -0
- fastmcp/server/transforms/visibility.py +526 -0
- fastmcp/settings.py +34 -96
- fastmcp/telemetry.py +122 -0
- fastmcp/tools/__init__.py +10 -3
- fastmcp/tools/function_parsing.py +201 -0
- fastmcp/tools/function_tool.py +467 -0
- fastmcp/tools/tool.py +215 -362
- fastmcp/tools/tool_transform.py +38 -21
- fastmcp/utilities/async_utils.py +69 -0
- fastmcp/utilities/components.py +152 -91
- fastmcp/utilities/inspect.py +8 -20
- fastmcp/utilities/json_schema.py +12 -5
- fastmcp/utilities/json_schema_type.py +17 -15
- fastmcp/utilities/lifespan.py +56 -0
- fastmcp/utilities/logging.py +12 -4
- fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +3 -3
- fastmcp/utilities/openapi/parser.py +3 -3
- fastmcp/utilities/pagination.py +80 -0
- fastmcp/utilities/skills.py +253 -0
- fastmcp/utilities/tests.py +0 -16
- fastmcp/utilities/timeout.py +47 -0
- fastmcp/utilities/types.py +1 -1
- fastmcp/utilities/versions.py +285 -0
- {fastmcp-2.14.5.dist-info → fastmcp-3.0.0b1.dist-info}/METADATA +8 -5
- fastmcp-3.0.0b1.dist-info/RECORD +228 -0
- fastmcp/client/transports.py +0 -1170
- fastmcp/contrib/component_manager/component_service.py +0 -209
- fastmcp/prompts/prompt_manager.py +0 -117
- fastmcp/resources/resource_manager.py +0 -338
- fastmcp/server/tasks/converters.py +0 -206
- fastmcp/server/tasks/protocol.py +0 -359
- fastmcp/tools/tool_manager.py +0 -170
- fastmcp/utilities/mcp_config.py +0 -56
- fastmcp-2.14.5.dist-info/RECORD +0 -161
- /fastmcp/server/{openapi → providers/openapi}/README.md +0 -0
- {fastmcp-2.14.5.dist-info → fastmcp-3.0.0b1.dist-info}/WHEEL +0 -0
- {fastmcp-2.14.5.dist-info → fastmcp-3.0.0b1.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.14.5.dist-info → fastmcp-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
fastmcp/cli/run.py
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
"""FastMCP run command implementation with enhanced type hints."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
4
|
+
import contextlib
|
|
3
5
|
import json
|
|
4
6
|
import re
|
|
7
|
+
import signal
|
|
5
8
|
import sys
|
|
6
9
|
from pathlib import Path
|
|
7
10
|
from typing import Any, Literal
|
|
8
11
|
|
|
9
12
|
from mcp.server.fastmcp import FastMCP as FastMCP1x
|
|
13
|
+
from watchfiles import Change, awatch
|
|
10
14
|
|
|
11
|
-
from fastmcp.server.server import FastMCP
|
|
15
|
+
from fastmcp.server.server import FastMCP, create_proxy
|
|
12
16
|
from fastmcp.utilities.logging import get_logger
|
|
13
17
|
from fastmcp.utilities.mcp_server_config import (
|
|
14
18
|
MCPServerConfig,
|
|
@@ -41,7 +45,7 @@ def create_client_server(url: str) -> Any:
|
|
|
41
45
|
import fastmcp
|
|
42
46
|
|
|
43
47
|
client = fastmcp.Client(url)
|
|
44
|
-
server =
|
|
48
|
+
server = create_proxy(client)
|
|
45
49
|
return server
|
|
46
50
|
except Exception as e:
|
|
47
51
|
logger.error(f"Failed to create client for URL {url}: {e}")
|
|
@@ -50,12 +54,10 @@ def create_client_server(url: str) -> Any:
|
|
|
50
54
|
|
|
51
55
|
def create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None]:
|
|
52
56
|
"""Create a FastMCP server from a MCPConfig."""
|
|
53
|
-
from fastmcp import FastMCP
|
|
54
|
-
|
|
55
57
|
with mcp_config_path.open() as src:
|
|
56
58
|
mcp_config = json.load(src)
|
|
57
59
|
|
|
58
|
-
server =
|
|
60
|
+
server = create_proxy(mcp_config)
|
|
59
61
|
return server
|
|
60
62
|
|
|
61
63
|
|
|
@@ -87,6 +89,7 @@ async def run_command(
|
|
|
87
89
|
show_banner: bool = True,
|
|
88
90
|
use_direct_import: bool = False,
|
|
89
91
|
skip_source: bool = False,
|
|
92
|
+
stateless: bool = False,
|
|
90
93
|
) -> None:
|
|
91
94
|
"""Run a MCP server or connect to a remote one.
|
|
92
95
|
|
|
@@ -101,6 +104,7 @@ async def run_command(
|
|
|
101
104
|
show_banner: Whether to show the server banner
|
|
102
105
|
use_direct_import: Whether to use direct import instead of subprocess
|
|
103
106
|
skip_source: Whether to skip source preparation step
|
|
107
|
+
stateless: Whether to run in stateless mode (no session)
|
|
104
108
|
"""
|
|
105
109
|
# Special case: URLs
|
|
106
110
|
if is_url(server_spec):
|
|
@@ -186,6 +190,8 @@ async def run_command(
|
|
|
186
190
|
kwargs["path"] = path
|
|
187
191
|
if log_level:
|
|
188
192
|
kwargs["log_level"] = log_level
|
|
193
|
+
if stateless:
|
|
194
|
+
kwargs["stateless"] = True
|
|
189
195
|
|
|
190
196
|
if not show_banner:
|
|
191
197
|
kwargs["show_banner"] = False
|
|
@@ -223,3 +229,138 @@ async def run_v1_server_async(
|
|
|
223
229
|
await server.run_streamable_http_async()
|
|
224
230
|
case "sse":
|
|
225
231
|
await server.run_sse_async()
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _python_file_filter(change: Change, path: str) -> bool:
|
|
235
|
+
"""Filter for Python files only."""
|
|
236
|
+
return path.endswith(".py")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
async def _terminate_process(process: asyncio.subprocess.Process) -> None:
|
|
240
|
+
"""Terminate a subprocess immediately."""
|
|
241
|
+
if process.returncode is not None:
|
|
242
|
+
return
|
|
243
|
+
process.kill()
|
|
244
|
+
await process.wait()
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
async def run_with_reload(
|
|
248
|
+
cmd: list[str],
|
|
249
|
+
reload_dirs: list[Path] | None = None,
|
|
250
|
+
is_stdio: bool = False,
|
|
251
|
+
) -> None:
|
|
252
|
+
"""Run a command with file watching and auto-reload.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
cmd: Command to run as subprocess (should include --no-reload)
|
|
256
|
+
reload_dirs: Directories to watch for changes (default: cwd)
|
|
257
|
+
is_stdio: Whether this is stdio transport
|
|
258
|
+
"""
|
|
259
|
+
watch_paths = reload_dirs or [Path.cwd()]
|
|
260
|
+
process: asyncio.subprocess.Process | None = None
|
|
261
|
+
first_run = True
|
|
262
|
+
|
|
263
|
+
if is_stdio:
|
|
264
|
+
logger.info("Reload mode enabled (using stateless sessions)")
|
|
265
|
+
else:
|
|
266
|
+
logger.info(
|
|
267
|
+
"Reload mode enabled (using stateless HTTP). "
|
|
268
|
+
"Some features requiring bidirectional communication "
|
|
269
|
+
"(like elicitation) are not available."
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Handle SIGTERM/SIGINT gracefully with proper asyncio integration
|
|
273
|
+
shutdown_event = asyncio.Event()
|
|
274
|
+
loop = asyncio.get_running_loop()
|
|
275
|
+
|
|
276
|
+
def signal_handler() -> None:
|
|
277
|
+
logger.info("Received shutdown signal, stopping...")
|
|
278
|
+
shutdown_event.set()
|
|
279
|
+
|
|
280
|
+
# Windows doesn't support add_signal_handler
|
|
281
|
+
if sys.platform != "win32":
|
|
282
|
+
loop.add_signal_handler(signal.SIGTERM, signal_handler)
|
|
283
|
+
loop.add_signal_handler(signal.SIGINT, signal_handler)
|
|
284
|
+
|
|
285
|
+
try:
|
|
286
|
+
while not shutdown_event.is_set():
|
|
287
|
+
# Build command - add --no-banner on restarts to reduce noise
|
|
288
|
+
if first_run or "--no-banner" in cmd:
|
|
289
|
+
run_cmd = cmd
|
|
290
|
+
else:
|
|
291
|
+
run_cmd = [*cmd, "--no-banner"]
|
|
292
|
+
first_run = False
|
|
293
|
+
|
|
294
|
+
process = await asyncio.create_subprocess_exec(
|
|
295
|
+
*run_cmd,
|
|
296
|
+
stdin=None,
|
|
297
|
+
stdout=None,
|
|
298
|
+
stderr=None,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# Watch for either: file changes OR process death
|
|
302
|
+
watch_task = asyncio.create_task(
|
|
303
|
+
anext(aiter(awatch(*watch_paths, watch_filter=_python_file_filter)))
|
|
304
|
+
)
|
|
305
|
+
wait_task = asyncio.create_task(process.wait())
|
|
306
|
+
shutdown_task = asyncio.create_task(shutdown_event.wait())
|
|
307
|
+
|
|
308
|
+
done, pending = await asyncio.wait(
|
|
309
|
+
[watch_task, wait_task, shutdown_task],
|
|
310
|
+
return_when=asyncio.FIRST_COMPLETED,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
for task in pending:
|
|
314
|
+
task.cancel()
|
|
315
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
316
|
+
await task
|
|
317
|
+
|
|
318
|
+
if shutdown_task in done:
|
|
319
|
+
# User requested shutdown
|
|
320
|
+
break
|
|
321
|
+
|
|
322
|
+
if wait_task in done:
|
|
323
|
+
# Server died on its own - wait for file change before restart
|
|
324
|
+
code = wait_task.result()
|
|
325
|
+
if code != 0:
|
|
326
|
+
logger.error(
|
|
327
|
+
f"Server exited with code {code}, waiting for file change..."
|
|
328
|
+
)
|
|
329
|
+
else:
|
|
330
|
+
logger.info("Server exited, waiting for file change...")
|
|
331
|
+
|
|
332
|
+
# Wait for file change or shutdown (avoid hot loop on crash)
|
|
333
|
+
watch_task = asyncio.create_task(
|
|
334
|
+
anext(aiter(awatch(*watch_paths, watch_filter=_python_file_filter)))
|
|
335
|
+
)
|
|
336
|
+
shutdown_task = asyncio.create_task(shutdown_event.wait())
|
|
337
|
+
done, pending = await asyncio.wait(
|
|
338
|
+
[watch_task, shutdown_task],
|
|
339
|
+
return_when=asyncio.FIRST_COMPLETED,
|
|
340
|
+
)
|
|
341
|
+
for task in pending:
|
|
342
|
+
task.cancel()
|
|
343
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
344
|
+
await task
|
|
345
|
+
if shutdown_task in done:
|
|
346
|
+
break
|
|
347
|
+
logger.info("Detected changes, restarting...")
|
|
348
|
+
else:
|
|
349
|
+
# File changed - restart server
|
|
350
|
+
changes = watch_task.result()
|
|
351
|
+
logger.info(
|
|
352
|
+
f"Detected changes in {len(changes)} file(s), restarting..."
|
|
353
|
+
)
|
|
354
|
+
await _terminate_process(process)
|
|
355
|
+
|
|
356
|
+
except KeyboardInterrupt:
|
|
357
|
+
# Handle Ctrl+C on Windows (where add_signal_handler isn't available)
|
|
358
|
+
logger.info("Received shutdown signal, stopping...")
|
|
359
|
+
|
|
360
|
+
finally:
|
|
361
|
+
# Clean up signal handlers
|
|
362
|
+
if sys.platform != "win32":
|
|
363
|
+
loop.remove_signal_handler(signal.SIGTERM)
|
|
364
|
+
loop.remove_signal_handler(signal.SIGINT)
|
|
365
|
+
if process and process.returncode is None:
|
|
366
|
+
await _terminate_process(process)
|
fastmcp/client/__init__.py
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
+
from .auth import OAuth, BearerAuth
|
|
1
2
|
from .client import Client
|
|
2
3
|
from .transports import (
|
|
3
4
|
ClientTransport,
|
|
4
|
-
|
|
5
|
-
SSETransport,
|
|
6
|
-
StdioTransport,
|
|
7
|
-
PythonStdioTransport,
|
|
5
|
+
FastMCPTransport,
|
|
8
6
|
NodeStdioTransport,
|
|
9
|
-
UvxStdioTransport,
|
|
10
|
-
UvStdioTransport,
|
|
11
7
|
NpxStdioTransport,
|
|
12
|
-
|
|
8
|
+
PythonStdioTransport,
|
|
9
|
+
SSETransport,
|
|
10
|
+
StdioTransport,
|
|
13
11
|
StreamableHttpTransport,
|
|
12
|
+
UvStdioTransport,
|
|
13
|
+
UvxStdioTransport,
|
|
14
14
|
)
|
|
15
|
-
from .auth import OAuth, BearerAuth
|
|
16
15
|
|
|
17
16
|
__all__ = [
|
|
18
17
|
"BearerAuth",
|
|
@@ -28,5 +27,4 @@ __all__ = [
|
|
|
28
27
|
"StreamableHttpTransport",
|
|
29
28
|
"UvStdioTransport",
|
|
30
29
|
"UvxStdioTransport",
|
|
31
|
-
"WSTransport",
|
|
32
30
|
]
|
fastmcp/client/auth/oauth.py
CHANGED
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import time
|
|
4
4
|
import webbrowser
|
|
5
5
|
from collections.abc import AsyncGenerator
|
|
6
|
+
from contextlib import aclosing
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
8
9
|
import anyio
|
|
@@ -299,15 +300,15 @@ class OAuth(OAuthClientProvider):
|
|
|
299
300
|
"""
|
|
300
301
|
try:
|
|
301
302
|
# First attempt with potentially cached credentials
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
303
|
+
async with aclosing(super().async_auth_flow(request)) as gen:
|
|
304
|
+
response = None
|
|
305
|
+
while True:
|
|
306
|
+
try:
|
|
307
|
+
# First iteration sends None, subsequent iterations send response
|
|
308
|
+
yielded_request = await gen.asend(response) # ty: ignore[invalid-argument-type]
|
|
309
|
+
response = yield yielded_request
|
|
310
|
+
except StopAsyncIteration:
|
|
311
|
+
break
|
|
311
312
|
|
|
312
313
|
except ClientNotFoundError:
|
|
313
314
|
logger.debug(
|
|
@@ -318,11 +319,11 @@ class OAuth(OAuthClientProvider):
|
|
|
318
319
|
await self.token_storage_adapter.clear()
|
|
319
320
|
|
|
320
321
|
# Retry with fresh registration
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
322
|
+
async with aclosing(super().async_auth_flow(request)) as gen:
|
|
323
|
+
response = None
|
|
324
|
+
while True:
|
|
325
|
+
try:
|
|
326
|
+
yielded_request = await gen.asend(response) # ty: ignore[invalid-argument-type]
|
|
327
|
+
response = yield yielded_request
|
|
328
|
+
except StopAsyncIteration:
|
|
329
|
+
break
|