list/dict/set comprehensions#

В list/dict/set comprehensions можно использовать await и, при переборе асинхронного итератора - async for.

Пример использования list comprehensions в сопрограмме:

from pprint import pprint
import asyncio
import yaml
from scrapli import AsyncScrapli
from scrapli.exceptions import ScrapliException


async def send_show(device, command):
    print(f">>> connect to {device['host']}")
    try:
        async with AsyncScrapli(**device) as conn:
            result = await conn.send_command(command)
            return result.result
    except ScrapliException as error:
        print(error, device["host"])


async def send_command_to_devices(devices, commands):
    tasks = [asyncio.create_task(send_show(device, commands)) for device in devices]
    result = [await task for task in tasks]
    return result


if __name__ == "__main__":
    with open("devices_async.yaml") as f:
        devices = yaml.safe_load(f)
    result = asyncio.run(send_command_to_devices(devices, "sh clock"))
    pprint(result, width=120)

Тут с помощью обычного list comprehensions (без async/await) создается список задач:

tasks = [asyncio.create_task(send_show(device, commands)) for device in devices]

А в этом используется await чтобы получить результаты каждой задачи:

result = [await task for task in tasks]

Также в list comprehensions может использоваться async for, при переборе асинхронного итератора.

import asyncio                                                                                                                                                                                                                              from pprint import pprint
from datetime import datetime
from scrapli import AsyncScrapli
from scrapli.exceptions import ScrapliException
import yaml


class CheckConnection:
    def __init__(self, device_list):
        self.device_list = device_list
        self._current_device = 0

    async def _scan_device(self, device):
        ip = device["host"]
        try:
            async with AsyncScrapli(**device) as conn:
                prompt = await conn.get_prompt()
            return True, prompt
        except (ScrapliException, asyncio.exceptions.TimeoutError) as error:
            return False, f"{error} {ip}"

    async def __anext__(self):
        if self._current_device >= len(self.device_list):
            raise StopAsyncIteration
        device_params = self.device_list[self._current_device]
        scan_results = await self._scan_device(device_params)
        self._current_device += 1
        return scan_results

    def __aiter__(self):
        return self


async def ssh_scan(devices):
    check = CheckConnection(devices)
    results = [out async for out in check]
    return results


if __name__ == "__main__":
    with open("devices_asyncssh.yaml") as f:
        devices = yaml.safe_load(f)
    result = asyncio.run(ssh_scan(devices))
    pprint(result)
    for status, msg in result:
        if status:
            print(f"{datetime.now()} SSH. Подключение успешно: {msg}")
        else:
            print(f"{datetime.now()} SSH. Не удалось подключиться: {msg}")

В примере выше в list comp используется именно async for потому что выполняется перебор асинхронного итератора:

results = [out async for out in check]