Can I pass an list to asyncio.gather?

Viewed 1155

I am trying to start a bunch (one or more) aioserial instances using an for and asyncio.gather without success.

# -*- coding: utf-8 -*-

import asyncio
import aioserial
from protocol import contactid, ademco

def main():
    #   Loop  Asyncio
    loop = asyncio.get_event_loop()
    centrals = utils.auth()
    #   List of corotines to be executed in paralel
    lst_coro = []
    #   Unpack centrals
    for central in centrals:
        protocol = central['protocol']
        id = central['id']
        name = central['name']
        port = central['port']
        logger = log.create_logging_system(name)
        #   Protocols
        if protocol == 'contactid':
            central = contactid.process
        elif protocol == 'ademco':
            central = ademco.process
        else:
            print(f'Unknown protocol: {central["protocol"]}')
        #  Serial (port ex: ttyUSB0/2400)
        dev = ''.join(['/dev/', *filter(str.isalnum, port.split('/')[0])])
        bps = int(port.split('/')[-1])
        aioserial_instance = aioserial.AioSerial(port=dev, baudrate=bps)
        lst_coro.append(central(aioserial_instance, id, name, logger))
    asyncio.gather(*lst_coro, loop=loop)

if __name__ == '__main__':
    asyncio.run(main())

I based this on the asyncio documentation example and some answers from stack overflow. But when I try to run it, I just got errors:

Traceback (most recent call last):
  File "/usr/lib/python3.7/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.7/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/opt/Serial/serial.py", line 39, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.7/asyncio/runners.py", line 37, in run
    raise ValueError("a coroutine was expected, got {!r}".format(main))
ValueError: a coroutine was expected, got None

I also tried to use a set instead of a list, but nothing really changed. Is there a better way to start a bunch of parallels corotines when you need to use a loop? Thanks for the attention.

1 Answers

Your problem isn't with how you call gather. It's with how you define main. The clue is with the error message

ValueError: a coroutine was expected, got None

and the last line of code in the traceback before the except is raised

asyncio.run(main())

asyncio.run wants an awaitable. You pass it the return value of main, but main doesn't return anything. Rather than adding a return value, though, the fix is to change how you define main.

async def main():

This will turn main from a regular function to a coroutine that can be awaited.

Edit

Once you've done this, you'll notice that gather doesn't actually seem to do anything. You'll need to await it in order for main to wait for everything in lst_coro to complete.

await asyncio.gather(*lst_coro)

Unrelated to your error: you shouldn't need to use loop inside main at all. gather's loop argument was deprecated in 3.8 and will be removed in 3.10. Unless you're using an older version of Python, you can remove it and your call to asyncio.get_event_loop.

Related