mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 15:19:10 +02:00
Compare commits
1 commit
main
...
codex/stdi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bce711df5 |
2 changed files with 60 additions and 0 deletions
|
|
@ -83,6 +83,14 @@ class StdioTransport(ClientTransport):
|
||||||
async def connect(
|
async def connect(
|
||||||
self, **session_kwargs: Unpack[SessionKwargs]
|
self, **session_kwargs: Unpack[SessionKwargs]
|
||||||
) -> ClientSession | None:
|
) -> ClientSession | None:
|
||||||
|
current_loop = asyncio.get_running_loop()
|
||||||
|
if (
|
||||||
|
self._connect_task is not None
|
||||||
|
and self._connect_task.get_loop() is not current_loop
|
||||||
|
):
|
||||||
|
logger.debug("Stdio transport changed event loops; reconnecting")
|
||||||
|
await self.disconnect()
|
||||||
|
|
||||||
# If the connect task completed or the session's streams are dead,
|
# If the connect task completed or the session's streams are dead,
|
||||||
# the subprocess has exited. Tear down so we can start fresh.
|
# the subprocess has exited. Tear down so we can start fresh.
|
||||||
if self._connect_task is not None and (
|
if self._connect_task is not None and (
|
||||||
|
|
@ -127,6 +135,19 @@ class StdioTransport(ClientTransport):
|
||||||
if self._connect_task is None:
|
if self._connect_task is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
owner_loop = self._connect_task.get_loop()
|
||||||
|
current_loop = asyncio.get_running_loop()
|
||||||
|
if (
|
||||||
|
owner_loop is not current_loop
|
||||||
|
and not self._connect_task.done()
|
||||||
|
and owner_loop.is_running()
|
||||||
|
):
|
||||||
|
disconnect_future = asyncio.run_coroutine_threadsafe(
|
||||||
|
self.disconnect(), owner_loop
|
||||||
|
)
|
||||||
|
await asyncio.wrap_future(disconnect_future)
|
||||||
|
return
|
||||||
|
|
||||||
# signal the connection task to stop
|
# signal the connection task to stop
|
||||||
self._stop_event.set()
|
self._stop_event.set()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import asyncio
|
||||||
import gc
|
import gc
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
|
import threading
|
||||||
import weakref
|
import weakref
|
||||||
|
|
||||||
import psutil
|
import psutil
|
||||||
|
|
@ -237,6 +238,44 @@ class TestKeepAlive:
|
||||||
|
|
||||||
assert pid1 == pid2 == pid3
|
assert pid1 == pid2 == pid3
|
||||||
|
|
||||||
|
def test_keep_alive_reconnects_on_different_running_loop(self, stdio_script):
|
||||||
|
transport = PythonStdioTransport(script_path=stdio_script)
|
||||||
|
client = Client(transport=transport, init_timeout=2)
|
||||||
|
first_loop = asyncio.new_event_loop()
|
||||||
|
ready = threading.Event()
|
||||||
|
|
||||||
|
def run_first_loop():
|
||||||
|
asyncio.set_event_loop(first_loop)
|
||||||
|
ready.set()
|
||||||
|
first_loop.run_forever()
|
||||||
|
|
||||||
|
async def get_pid(*, close: bool = False) -> int:
|
||||||
|
async with client:
|
||||||
|
result = await client.call_tool("pid")
|
||||||
|
if close:
|
||||||
|
await client.close()
|
||||||
|
return result.data
|
||||||
|
|
||||||
|
thread = threading.Thread(target=run_first_loop)
|
||||||
|
thread.start()
|
||||||
|
try:
|
||||||
|
assert ready.wait(timeout=2)
|
||||||
|
pid1 = asyncio.run_coroutine_threadsafe(get_pid(), first_loop).result(
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
pid2 = asyncio.run(asyncio.wait_for(get_pid(close=True), timeout=5))
|
||||||
|
|
||||||
|
assert pid1 != pid2
|
||||||
|
finally:
|
||||||
|
if transport._connect_task is not None:
|
||||||
|
owner_loop = transport._connect_task.get_loop()
|
||||||
|
asyncio.run_coroutine_threadsafe(transport.close(), owner_loop).result(
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
first_loop.call_soon_threadsafe(first_loop.stop)
|
||||||
|
thread.join(timeout=2)
|
||||||
|
first_loop.close()
|
||||||
|
|
||||||
async def test_close_session_and_try_to_use_client_raises_error(self, stdio_script):
|
async def test_close_session_and_try_to_use_client_raises_error(self, stdio_script):
|
||||||
client = Client(transport=PythonStdioTransport(script_path=stdio_script))
|
client = Client(transport=PythonStdioTransport(script_path=stdio_script))
|
||||||
assert client.transport.keep_alive is True
|
assert client.transport.keep_alive is True
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue