hardbyte / hardbyte/python-can

Async Listener

Abierto
#1,626 2 comentarios 1 reacción 0 asignados Ver en GitHub
enhancement
Lenguaje dominante
Python
Estrellas
1.6k
Forks
697
Métricas de merge de PR
Sin PR fusionados en 30 d

Descripción

### 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.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.