Я написал инструмент командной строки для выполнения git pull
для нескольких репозиториев git с использованием Python Asyncio. Это работает нормально, если все репозитории имеют настройки входа по паролю ssh. Это также работает нормально, если только 1 репо требует ввода пароля. Когда для нескольких репозиториев требуется ввод пароля, кажется, что они зашли в тупик.
Моя реализация очень проста. Основная логика
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()
Полная база кода здесь на github.
Я думаю, что проблема заключается в следующем. В run_async
asyncio.create_subprocess_exec
перенаправление для stdin отсутствует, а системный stdin используется для всех подпроцессов (репо). Когда первый репозиторий запрашивает ввод пароля, планировщик асинхронного режима видит блокирующий ввод и переключается на второй репо, ожидая ввода из командной строки. Но если второе хранилище запрашивает ввод пароля до того, как ввод пароля для первого хранилища завершится, системный ввод будет связан со вторым хранилищем. И первый репо будет ждать ввода навсегда.
Я не уверен, как справиться с этой ситуацией. Нужно ли перенаправлять стандартный ввод для каждого подпроцесса? Что делать, если некоторые репозитории имеют логин без пароля, а некоторые нет?
Некоторые идеи заключаются в следующем
-
определить, когда требуется ввод пароля в
create_subprocess_exec
. Если это так, тогда вызовитеinput()
и передайте его результат вprocess.communicate(input)
. Но как я могу обнаружить это на лету? -
определить, какие репозитории требуют ввода пароля, и исключить их из асинхронного выполнения. Какой лучший способ сделать это?