ttu / ruuvitag-sensor

Python package for communicating with RuuviTag BLE Sensor and for decoding sensor data from broadcasted data
https://ttu.github.io/ruuvitag-sensor/
MIT License
196 stars 78 forks source link

BleCommunicationBleak doesn't work with asyncio.run() #186

Open ttu opened 2 years ago

ttu commented 2 years ago

Platform: Windows, Linux?, macOS?

Error:

Task <Task pending name='Task-1' coro=<main() running at c:\xxx\ruuvitag-sensor\examples\get_async_bleak.py:12> cb=[_run_until_complete_cb() at C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\asyncio\base_events.py:184]> got Future attached to a different loop

When import statement in e.g. example is moved to inside run()-function, it works normally:

import asyncio
import os
os.environ['RUUVI_BLE_ADAPTER'] = 'bleak'

import ruuvitag_sensor.log

ruuvitag_sensor.log.enable_console()

async def main():
    from ruuvitag_sensor.ruuvi import RuuviTagSensor

    async for data in RuuviTagSensor.get_data_async():
        print(data)

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

Possible same situation: https://stackoverflow.com/questions/53724665/using-queues-results-in-asyncio-exception-got-future-future-pending-attached

terop commented 2 years ago

How should this problem be approached? I see multiple options: 1) try to figure out the reason for the exception 2) change the code as is known to work even though the cause for the exception is not known 3) leave the code as is and close this issue

For me investigating is difficult as I cannot reproduce the problem on Linux and I don't want to start to mess around with Python on Windows.

ttu commented 2 years ago

I think we should go with option number 1, but that would then require some one with a Windows. I would assume that the problem is that queue must be created inside the async loop, as if import is inside main, everything works normally.

Uular commented 9 months ago

This issue reproduces on Python 3.9 on MacOS and Linux, and probably earlier versions. The root cause is just as suggested, the asyncio.Queue object is bound to the event loop in which is it created, and run() creates a new loop.

The curious thing here is that it does work on Python 3.10 and later, even though nothing in the changelogs suggest a change in this behaviour. I was able to track it down to a side-effect of the removal of the loop argument in the constructor – the binding of the queue to the event loop was moved from the constructor to the first call of put()or get().

I might look to fix this sometime soon, it shouldn't be too hard.