From e177ef18e1fc323e4d89df9955cb678ed6e3af81 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sun, 17 Dec 2023 15:06:44 +0200 Subject: [PATCH] Add and use asyncio fire_and_forget utility function (#164) --- qtoggleserver/core/api/funcs/ports.py | 3 ++- qtoggleserver/core/ports.py | 3 ++- qtoggleserver/core/reverse.py | 1 + qtoggleserver/slaves/devices.py | 8 ++++---- qtoggleserver/utils/asyncio.py | 11 +++++++++-- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/qtoggleserver/core/api/funcs/ports.py b/qtoggleserver/core/api/funcs/ports.py index b635bffe..5f7d91bc 100644 --- a/qtoggleserver/core/api/funcs/ports.py +++ b/qtoggleserver/core/api/funcs/ports.py @@ -23,6 +23,7 @@ ) from qtoggleserver.slaves import devices as slaves_devices from qtoggleserver.slaves import ports as slaves_ports +from qtoggleserver.utils import asyncio as asyncio_utils from qtoggleserver.utils import json as json_utils @@ -132,7 +133,7 @@ async def set_attr(attr_name: str, attr_value: Attribute) -> None: # If value is supplied among attrs, use it to update port value, but in background and ignoring any errors if value is not _none and port.is_enabled(): - asyncio.create_task(port.transform_and_write_value(value)) + asyncio_utils.fire_and_forget(port.transform_and_write_value(value)) await port.save() diff --git a/qtoggleserver/core/ports.py b/qtoggleserver/core/ports.py index 842ec10d..60f8c92f 100644 --- a/qtoggleserver/core/ports.py +++ b/qtoggleserver/core/ports.py @@ -25,6 +25,7 @@ NullablePortValue, PortValue, ) +from qtoggleserver.utils import asyncio as asyncio_utils from qtoggleserver.utils import dynload as dynload_utils from qtoggleserver.utils import json as json_utils from qtoggleserver.utils import logging as logging_utils @@ -885,7 +886,7 @@ async def set_sequence(self, values: list[PortValue], delays: list[int], repeat: self._sequence.start() def _transform_and_write_value_fire_and_forget(self, value: NullablePortValue) -> None: - asyncio.create_task(self.transform_and_write_value(value)) + asyncio_utils.fire_and_forget(self.transform_and_write_value(value)) async def _on_sequence_finish(self) -> None: self.debug('sequence finished') diff --git a/qtoggleserver/core/reverse.py b/qtoggleserver/core/reverse.py index 22626743..c74f0ed8 100644 --- a/qtoggleserver/core/reverse.py +++ b/qtoggleserver/core/reverse.py @@ -104,6 +104,7 @@ def enable(self) -> None: logger.debug('starting wait loop') self._enabled = True + # TODO: properly handle session loop task asyncio.create_task(self._session_loop()) def disable(self) -> None: diff --git a/qtoggleserver/slaves/devices.py b/qtoggleserver/slaves/devices.py index 433ded41..2def0529 100644 --- a/qtoggleserver/slaves/devices.py +++ b/qtoggleserver/slaves/devices.py @@ -195,7 +195,7 @@ async def update_cached_attrs(self, attrs: Attributes, partial: bool = False) -> # Upon renaming, what be basically do is remove the existing device and add it from scratch. # This is being taken care of by the following handle_rename() calls, respectively. await self._handle_rename(name) - asyncio.create_task(_handle_rename(self, name)) + asyncio_utils.fire_and_forget(_handle_rename(self, name)) raise exceptions.DeviceRenamed(self) else: @@ -284,7 +284,7 @@ async def enable(self) -> None: # We can't await for the _load_ports() result, because we expect callers of enable() to generate # slave-device-update events, and we want any port-related events generated by _load_ports() to come after. if self.is_permanently_offline() and self._name: - asyncio.create_task(self._load_ports()) + asyncio_utils.fire_and_forget(self._load_ports()) async def disable(self) -> None: if not self._enabled: @@ -329,7 +329,7 @@ def set_poll_interval(self, poll_interval: Optional[int]) -> None: if self._online: # Take offline self._online = False - asyncio.create_task(self._handle_offline()) + asyncio_utils.fire_and_forget(self._handle_offline()) def get_poll_interval(self) -> Optional[int]: return self._poll_interval @@ -357,7 +357,7 @@ def disable_listen(self) -> None: if self._online: # Take offline self._online = False - asyncio.create_task(self._handle_offline()) + asyncio_utils.fire_and_forget(self._handle_offline()) def is_listen_enabled(self) -> bool: return self._listen_enabled diff --git a/qtoggleserver/utils/asyncio.py b/qtoggleserver/utils/asyncio.py index 163de0a7..029cbfce 100644 --- a/qtoggleserver/utils/asyncio.py +++ b/qtoggleserver/utils/asyncio.py @@ -4,6 +4,7 @@ import queue import sys import threading +import weakref from typing import Any, Awaitable, Callable, Optional, Union @@ -152,6 +153,12 @@ async def stop(self) -> None: await self._stopped_future -async def await_later(delay: float, aw: Awaitable, loop: asyncio.AbstractEventLoop = None) -> None: - await asyncio.sleep(delay, loop=loop) +async def await_later(delay: float, aw: Awaitable) -> None: + await asyncio.sleep(delay) await aw + + +def fire_and_forget(aw: Awaitable) -> None: + task = asyncio.create_task(aw) + coro = task.get_coro() + weakref.finalize(task, coro.close)