hardbyte / hardbyte/python-can
Async Listener
- Lingua principale
- Python
- Stelle
- 1.6k
- Fork
- 697
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Descrizione
### Issue description
Currently, `Listener` objects are asyncio-ready but are not truely async.
Let me first clarify that `AsyncBufferedReader` is not a truely-async listener, it is a sync listener (sync `on_message_received` and `on_error` methods) that exposes an async buffer through the `get_message` method. It is pretty useful for a lot of cases but this is not the target of this feature request. A truely-async listener would allow for async `on_message_received` and `on_error` definitions.
Disclaimer: I haven't inspected the whole code so I'm not sure if there is anything of the underlying architecture that makes this kind of truely-async listeners an issue.
And what do I mean by "asyncio-ready" in the first sentence? `Notifier._on_message_available` is called when a new message can be received. `Notifier.listeners` can either be `Listener` instances or direct callbacks. Either way they are called, and if they return a coroutine (and the notifier was passed a loop), it will scheduler the corresponding task:
https://github.com/hardbyte/python-can/blob/dc0ae68acb28f01a503e084718cfa2acf39d1e16/can/notifier.py#L134-L143
Async callbacks can be provided to make use of this feature, but `Listener.__call__` never returns a coroutine:
https://github.com/hardbyte/python-can/blob/dc0ae68acb28f01a503e084718cfa2acf39d1e16/can/listener.py#L42-L43
### Solutions
Returning the result of `self.on_message_received` instead of implicitly returning `None` would work for both versions:
```py
def __call__(self, msg: Message):
return self.on_message_received(msg)
```
In the sync version, `self.on_message_receive(msg)` returns `None`, while the async version will return the coroutine, which is exactly the desired behavior. Regaring type-hinting of the result, `Optional[asyncio.coroutine]` works up to Python 3.9, not sure what should be the 3.10 one as `asyncio.coroutine` is no longer a thing.
I see two ways of providing a truely-async Listener:
1. Creating a new `AsyncListener` with the above version of `__call__`, and async signatures for `on_message_received` and `on_error`.
2. Updating the `Listener` class with the above version of `__call__`.
### Alternatives
Meanwhile, the user can always modify the `__call__` method of the subclass of `Listener` to the one provided above and it will work. I still think this should be provided to the user and not expected from him to discover that he needs to change that magic method of the subclass.
Guida per i contributori
Apri la guida per i contributori
Valutazione
Questa issue non è ancora stata valutata.