empicano / aiomqtt

The idiomatic asyncio MQTT client
https://aiomqtt.bo3hm.com
BSD 3-Clause "New" or "Revised" License
427 stars 75 forks source link

Fix: Iterating over received messages multiple times does not yield proper results #312

Closed ryan-summers closed 4 months ago

ryan-summers commented 4 months ago

Because of the iterator construction in __init__, any attempt to iterate over client.messages after the initial iteration will yield no data.

Here's an MVP of the actual defect:

import sys
import os

# Change to the "Selector" event loop if platform is Windows
if sys.platform.lower() == "win32" or os.name.lower() == "nt":
    from asyncio import set_event_loop_policy, WindowsSelectorEventLoopPolicy
    set_event_loop_policy(WindowsSelectorEventLoopPolicy())
import asyncio
import aiomqtt

async def main():
    async with aiomqtt.Client("test.mosquitto.org") as client:
        async def listen():
            async for message in client.messages:
                print(message.payload)

        listen_task = asyncio.create_task(listen())

        await client.subscribe("temperature/#")

        try:
            await asyncio.wait_for(listen_task, timeout=0.5)
        except asyncio.TimeoutError:
            listen_task.cancel()

        # Should continue forever
        await listen()
        raise Exception("Listen returned too early!")

asyncio.run(main())

Running the MVP fails with current main, but works properly after this PR.

empicano commented 4 months ago

LGTM 👍 Thank you for your first contribution to aiomqtt! 🎉