0
votes

I'm looking to define a new connection as shown here: https://udsoncan.readthedocs.io/en/latest/udsoncan/connection.html#defining-a-new-connection

However, I want to call async code from there (i.e. make the send/wait asynchronous). I can't seem to get it working.

Consider the following example as what I am trying to achieve:

import asyncio

async def some_task():
    await asyncio.sleep(1)  # Async task
    print("Done")
    
def sync_method():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.sleep(5)) # Some async event
    print("sure")

async def main():
    sync_method()
    await asyncio.gather(some_task(), some_task(), some_task())
    

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

But this throws an error (sleep was never awaited; but if I await it then I get error of await called from non async function). But I read (see here) that this was how to call an async function from a sync function.

So, essentially my code is asynchronous and main is run in the event loop. Now, I want to call the synchronous function but essentially want to make it non-blocking by using async methods.

What am I doing wrong?

Edit - traceback as requested...

Traceback (most recent call last):
  File ".\tmp_sync_async.py", line 18, in <module>
    asyncio.run(main())
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1520.0_x64__qbz5n2kfra8p0\lib\asyncio\runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1520.0_x64__qbz5n2kfra8p0\lib\asyncio\base_events.py", line 616, in run_until_complete
    return future.result()
  File ".\tmp_sync_async.py", line 13, in main
    sync_method()
  File ".\tmp_sync_async.py", line 9, in sync_method
    loop.run_until_complete(asyncio.sleep(3))
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1520.0_x64__qbz5n2kfra8p0\lib\asyncio\base_events.py", line 592, in run_until_complete
    self._check_running()
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1520.0_x64__qbz5n2kfra8p0\lib\asyncio\base_events.py", line 552, in _check_running
    raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
sys:1: RuntimeWarning: coroutine 'sleep' was never awaited

Update: From what I found, the run_until_complete way is fine if starting with sync code and then trying to run async code (i.e. if main() in my case was sync code).

1
Show the full traceback as properly formatted text in the question. - Michael Butscher
You are attempting to use a non-async framework (at least judging by its usage examples) in an async program. That won't work - for asyncio to work, all parts of the program must use it. - user4815162342

1 Answers

0
votes

You can use a generator to return the coroutine:

import asyncio


async def some_task():
    await asyncio.sleep(1)  # Async task
    print("Done")

    
def sync_method():
    # A yield statement converts a function into a generator
    # The yield statement effectively freezes the function and no code below a yield is executed until the next item is requested. The earlier asyncio implementations were actually done using yield statements.
    yield asyncio.sleep(5) # Some async event
    print("sure")


async def run_promises(future_generator):
    # Fetch the items from the generator one by one
    for future in future_generator:
        # Wait for the future result, the next yield is not requested until this is done
        await future


async def main():
    # To run in the background:
    asyncio.create_task(run_promises(sync_method()))

    # To wait:
    await run_promises(sync_method())

    await asyncio.gather(some_task(), some_task(), some_task())
    

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