asyncssh icon indicating copy to clipboard operation
asyncssh copied to clipboard

RuntimeError: cannot reuse already awaited coroutine

Open starflows opened this issue 1 year ago • 1 comments

The code below aborts with RuntimeError: cannot reuse already awaited coroutine as soon as the process ends.

To reproduce:

  1. run the code below
  2. in the "remote" terminal type exit
import os
import sys
import asyncio
import asyncssh

async def main():
    async with asyncssh.connect('localhost') as conn:
        async with conn.create_process(
            stdin=os.dup(sys.stdin.fileno()),
            stdout=os.dup(sys.stdout.fileno()),
            stderr=os.dup(sys.stderr.fileno()),
            term_type=os.environ.get('TERM'),
        ) as process:
            await process.wait()

asyncio.run(main())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.12/asyncio/runners.py", line 194, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/asyncio/base_events.py", line 687, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "<stdin>", line 3, in main
  File "/home/test/.venv/lib/python3.12/site-packages/asyncssh/misc.py", line 308, in __aexit__
    exit_result = await self._coro_result.__aexit__(
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/test/.venv/lib/python3.12/site-packages/asyncssh/process.py", line 831, in __aexit__
    await self.wait_closed()
  File "/home/test/.venv/lib/python3.12/site-packages/asyncssh/process.py", line 1212, in wait_closed
    await task
RuntimeError: cannot reuse already awaited coroutine

The code does not raise a RuntimeError with asyncssh version 2.14.2 It does raise the error with asyncssh version 2.17.0 as well as with the develop and master branch.

Maybe there is another way to connect your local terminal to a remote process?

Environment: asyncssh 2.17.0 python 3.12.6

starflows avatar Oct 17 '24 06:10 starflows

Thanks for the report!

I'm having trouble reliably reproducing this with 2.17.0, but I am seeing it in the develop branch, so I'll take a closer look at it there. I reverted some changes related to channel cleanup which went into 2.15.0 and took a different approach in the "develop" branch that I thought had fixed it, but this seems to be a slightly different issue which is more closely tied to the process class, not the channel class.

As a potential workaround, I'd recommend not doing a process.wait() from inside an async context handler. Instead, try something like:

    async with asyncssh.connect('localhost') as conn:
        process =  conn.create_process(
            stdin=os.dup(sys.stdin.fileno()),
            stdout=os.dup(sys.stdout.fileno()),
            stderr=os.dup(sys.stderr.fileno()),
            term_type=os.environ.get('TERM'),
        )

        await process.wait()

That way, it's not attempting to run the aexit handler after the process has already been waited on. That SHOULD have been harmless, but it looks like I didn't clear out the _cleanup_tasks list after they are run and that may be what's causing the error you're seeing.

I haven't thoroughly tested this yet, but you might also try the following patch:

diff --git a/asyncssh/process.py b/asyncssh/process.py
index f2dbb3d..4d5d750 100644
--- a/asyncssh/process.py
+++ b/asyncssh/process.py
@@ -1210,6 +1210,8 @@ class SSHProcess(SSHStreamSession, Generic[AnyStr]):
         for task in self._cleanup_tasks:
             await task

+        self._cleanup_tasks = []
+

 class SSHClientProcess(SSHProcess[AnyStr], SSHClientStreamSession[AnyStr]):
     """SSH client process handler"""

ronf avatar Oct 17 '24 13:10 ronf

Thank you for your reply @ronf !

The workaround and the patch both work and solved my issue.

starflows avatar Oct 21 '24 05:10 starflows

Thanks for the confirmation -- this is now in the "develop" branch as commit bfa04aa.

ronf avatar Oct 21 '24 06:10 ronf

This fix is now available in AsyncSSH 2.18.0.

ronf avatar Oct 26 '24 18:10 ronf