hardbyte / hardbyte/python-can

Async Listener

Aperta
#1,626 2 commenti 1 reazione 0 assegnatari Vedi su GitHub
enhancement
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.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.