You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用asyncio.subprocess调用外部Python文件时遇ConnectionResetError求助

问题:使用asyncio.subprocess运行外部命令时抛出ConnectionResetError

我在尝试用asyncio.subprocess模块运行外部Python文件时遇到了ConnectionResetError异常,以下是我的代码:

import asyncio
async def external():
    writing_process=await asyncio.create_subprocess_shell("pwd", stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, cwd="/home/user1/folder1")
    print(await writing_process.stdout.read())
    writing_process.stdin.writelines([b"python test.py"])
    await writing_process.stdin.drain()
    print(await writing_process.stdout.read())
asyncio.run(run_outer_py_file())

运行后输出如下:

b'/home/bykov/nra_banks\n'
---------------------------------------------------------------------------
ConnectionResetError Traceback (most recent call last)
<ipython-input-9-67bbc190049c> in async-def-wrapper()
<ipython-input-9-67bbc190049c> in external()
     11     print(await writing_process.stdout.read())
     12     await external()
~/anaconda3/lib/python3.7/asyncio/streams.py in drain(self)
    346         # would not see an error when the socket is closed.
    347         await sleep(0, loop=self._loop)
--> 348         await self._protocol._drain_helper()
    349 
    350 
~/anaconda3/lib/python3.7/asyncio/streams.py in _drain_helper(self)
    200     async def _drain_helper(self):
    201         if self._connection_lost:
--> 202             raise ConnectionResetError('Connection lost')
    203         if not self._paused:
    204             return
ConnectionResetError: Connection lost

请问有人能指出我哪里出错了吗?


解决思路

嘿,我来帮你捋清楚问题出在哪!你这里的核心问题是你启动的pwd命令是一次性执行的,输出结果后立刻就退出进程了,当你尝试往它的stdin写数据时,这个子进程已经不存在,对应的IO连接也已经关闭,自然就抛出了ConnectionResetError

具体来说:

  • pwd是个短生命周期命令,完成目录输出后会马上终止,此时它的标准输入、输出流都会被系统关闭。
  • 你后续还想往这个已经终止的进程的stdin写入python test.py,这相当于对着一个已经断开的连接发数据,肯定会报错。

另外还要提个小细节:你的代码最后调用asyncio.run(run_outer_py_file()),但定义的函数是external,这里名称不匹配,实际运行时会先报错找不到run_outer_py_file,得改成asyncio.run(external())才行。

下面给你两种可行的修改方案:

方案1:在同一个shell中执行多个命令

如果想先执行pwd再执行python test.py,可以把两个命令用&&串联,放在同一个create_subprocess_shell调用里:

import asyncio
async def external():
    # 串联两个命令,在同一个shell进程中执行
    writing_process=await asyncio.create_subprocess_shell(
        "pwd && python test.py", 
        stdin=asyncio.subprocess.PIPE, 
        stdout=asyncio.subprocess.PIPE, 
        stderr=asyncio.subprocess.PIPE, 
        cwd="/home/user1/folder1"
    )
    # 读取所有输出(包含pwd和test.py的输出)
    output = await writing_process.stdout.read()
    print(output)
    # 读取错误输出并处理
    stderr_content = await writing_process.stderr.read()
    if stderr_content:
        print("错误信息:", stderr_content)
    # 等待进程执行完成
    await writing_process.wait()

asyncio.run(external())

方案2:创建两个独立的子进程

如果需要分开执行两个命令(先跑pwd,再跑python脚本),应该分别创建两个子进程实例:

import asyncio
async def external():
    # 第一个子进程:执行pwd
    pwd_process=await asyncio.create_subprocess_shell(
        "pwd", 
        stdout=asyncio.subprocess.PIPE, 
        stderr=asyncio.subprocess.PIPE, 
        cwd="/home/user1/folder1"
    )
    print(await pwd_process.stdout.read())
    # 等待pwd进程结束
    await pwd_process.wait()
    
    # 第二个子进程:执行python test.py
    py_process=await asyncio.create_subprocess_shell(
        "python test.py", 
        stdin=asyncio.subprocess.PIPE, 
        stdout=asyncio.subprocess.PIPE, 
        stderr=asyncio.subprocess.PIPE, 
        cwd="/home/user1/folder1"
    )
    # 如果test.py需要输入数据,再往stdin写入
    # py_process.stdin.writelines([b"需要传入的输入内容"])
    # await py_process.stdin.drain()
    print(await py_process.stdout.read())
    await py_process.wait()

asyncio.run(external())

内容的提问来源于stack exchange,提问作者A. Bykov

火山引擎 最新活动