Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
View this page
Toggle table of contents sidebar

# Asyncio support

The `asyncio` module built into Python 3.4 and later can be used to write asynchronous code in a single thread. This library supports receiving messages asynchronously in an event loop using the `can.Notifier` class.

There will still be one thread per CAN bus but the user application will execute entirely in the event loop, allowing simpler concurrency without worrying about threading issues. Interfaces that have a valid file descriptor will however be supported natively without a thread.

You can also use the `can.AsyncBufferedReader` listener if you prefer to write coroutine based code instead of using callbacks.

## Example

Here is an example using both callback and coroutine based code:

    #!/usr/bin/env python3

    """
    This example demonstrates how to use async IO with python-can.
    """

    import asyncio
    from typing import List

    import can
    from can.notifier import MessageRecipient

    def print_message(msg: can.Message) -> None:
        """Regular callback function. Can also be a coroutine."""
        print(msg)

    async def main() -> None:
        """The main function that runs in the loop."""

        with can.Bus(
            interface="virtual", channel="my_channel_0", receive_own_messages=True
        ) as bus:
            reader = can.AsyncBufferedReader()
            logger = can.Logger("logfile.asc")

            listeners: List[MessageRecipient] = [
                print_message,  # Callback function
                reader,  # AsyncBufferedReader() listener
                logger,  # Regular Listener object
            ]
            # Create Notifier with an explicit loop to use for scheduling of callbacks
            loop = asyncio.get_running_loop()
            notifier = can.Notifier(bus, listeners, loop=loop)
            # Start sending first message
            bus.send(can.Message(arbitration_id=0))

            print("Bouncing 10 messages...")
            for _ in range(10):
                # Wait for next message from AsyncBufferedReader
                msg = await reader.get_message()
                # Delay response
                await asyncio.sleep(0.5)
                msg.arbitration_id += 1
                bus.send(msg)

            # Wait for last message to arrive
            await reader.get_message()
            print("Done!")

            # Clean-up
            notifier.stop()

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