gpt4 book ai didi

如果需要多个标准输入,python asyncio 会死锁

转载 作者:行者123 更新时间:2023-12-01 22:09:45 25 4
gpt4 key购买 nike

我写了一个命令行工具来执行git pull对于使用 python asyncio 的多个 git 存储库。如果所有存储库都具有 ssh 无密码登录设置,则它工作正常。如果只有 1 个 repo 需要输入密码,它也可以正常工作。当多个 repos 需要输入密码时,它似乎会陷入僵局。

我的实现非常简单。主要逻辑是

utils.exec_async_tasks(
utils.run_async(path, cmds) for path in repos.values())

在哪里 run_async创建并等待子进程调用, exec_async_tasks运行所有任务。
async def run_async(path: str, cmds: List[str]):
"""
Run `cmds` asynchronously in `path` directory
"""
process = await asyncio.create_subprocess_exec(
*cmds, stdout=asyncio.subprocess.PIPE, cwd=path)
stdout, _ = await process.communicate()
stdout and print(stdout.decode())


def exec_async_tasks(tasks: List[Coroutine]):
"""
Execute tasks asynchronously
"""
# TODO: asyncio API is nicer in python 3.7
if platform.system() == 'Windows':
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)
else:
loop = asyncio.get_event_loop()

try:
loop.run_until_complete(asyncio.gather(*tasks))
finally:
loop.close()

完整的代码库是 here on github .

我认为问题类似于以下内容。在 run_async , asyncio.create_subprocess_exec ,标准输入没有重定向,系统的标准输入用于所有子进程(repos)。当第一个 repo 要求输入密码时,asyncio 调度程序看到一个阻塞输入,并在等待命令行输入时切换到第二个 repo。但是如果第二个 repo 在第一个 repo 的密码输入完成之前要求输入密码,系统的 stdin 将链接到第二个 repo。第一个 repo 将永远等待输入。

我不知道如何处理这种情况。我必须为每个子进程重定向标准输入吗?如果有些 repos 有无密码登录而有些没有怎么办?

一些想法如下
  • 检测 create_subprocess_exec 中何时需要输入密码.如果有,请调用input()并将其结果传递给 process.communicate(input) .但是我怎样才能即时检测到呢?
  • 检测哪些 repos 需要输入密码,并将它们从异步执行中排除。最好的方法是什么?
  • 最佳答案

    我最终使用了@vincent 建议的简单解决方案,即通过设置 GIT_ASKPASS 禁用任何现有的密码机制。环境变量,在所有存储库上运行异步,并同步重新运行失败的存储库。

    主要逻辑变为

    cache = os.environ.get('GIT_ASKPASS')
    os.environ['GIT_ASKPASS'] = 'echo'
    errors = utils.exec_async_tasks(
    utils.run_async(path, cmds) for path in repos.values())
    # Reset context and re-run
    if cache:
    os.environ['GIT_ASKPASS'] = cache
    else:
    del os.environ['GIT_ASKPASS']
    for path in errors:
    if path:
    subprocess.run(cmds, cwd=path)

    run_asyncexec_async_tasks ,我只是重定向错误并返回 repo path如果子流程执行失败。
    async def run_async(path: str, cmds: List[str]) -> Union[None, str]:
    """
    Run `cmds` asynchronously in `path` directory. Return the `path` if
    execution fails.
    """
    process = await asyncio.create_subprocess_exec(
    *cmds,
    stdout=asyncio.subprocess.PIPE,
    stderr=asyncio.subprocess.PIPE,
    cwd=path)
    stdout, stderr = await process.communicate()
    stdout and print(stdout.decode())
    if stderr:
    return path

    可以看 this pull request为彻底改变。

    进一步更新

    上面的 PR 解决了 https 类型 remote 需要输入用户名/密码时的问题,但是当 ssh 需要为多个 repos 输入密码时仍然存在问题。感谢@gdlmx 下面的评论。

    在 0.9.1 版本中,我基本上遵循了@gdlmx 的建议:在异步模式下运行时完全禁用用户输入,失败的 repos 将使用 subprocess 再次运行委托(delegate)命令串行。

    关于如果需要多个标准输入,python asyncio 会死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55155294/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com