summaryrefslogtreecommitdiff
path: root/kvmd/aiotools.py
diff options
context:
space:
mode:
authorMaxim Devaev <[email protected]>2022-08-07 18:42:00 +0300
committerMaxim Devaev <[email protected]>2022-08-07 18:42:00 +0300
commitaa630988cc09f31d412a62c5480d4bec1a7c626e (patch)
tree7e61031827ddcbfc56d049ffa336ac664d3c5c8b /kvmd/aiotools.py
parentd995349b6311bf06b77ac0e8ccc4a424112feeb0 (diff)
aiotools.shield_fg()
Diffstat (limited to 'kvmd/aiotools.py')
-rw-r--r--kvmd/aiotools.py41
1 files changed, 41 insertions, 0 deletions
diff --git a/kvmd/aiotools.py b/kvmd/aiotools.py
index 6895421c..2a798e2a 100644
--- a/kvmd/aiotools.py
+++ b/kvmd/aiotools.py
@@ -84,6 +84,47 @@ _RetvalT = TypeVar("_RetvalT")
# =====
+class _ArmoredFuture(asyncio.Future):
+ def cancel(self, *_, **__) -> bool: # type: ignore
+ # FIXME: Выяснить, почему это работает
+ return False
+
+ def forced_cancel(self) -> bool:
+ return super().cancel()
+
+
+def shield_fg(aw: Awaitable): # type: ignore
+ # XXX: Копия asyncio.shield() с небольшими изменениями
+
+ inner = asyncio.ensure_future(aw)
+ if inner.done():
+ return inner
+ outer = _ArmoredFuture(loop=inner.get_loop())
+
+ def inner_done(_) -> None: # type: ignore
+ if outer.cancelled():
+ if not inner.cancelled():
+ inner.exception() # Mark inner's result as retrieved
+ return
+
+ if inner.cancelled():
+ outer.forced_cancel()
+ else:
+ err = inner.exception()
+ if err is not None:
+ outer.set_exception(err)
+ else:
+ outer.set_result(inner.result())
+
+ def outer_done(_) -> None: # type: ignore
+ if not inner.done():
+ inner.remove_done_callback(inner_done)
+
+ inner.add_done_callback(inner_done)
+ outer.add_done_callback(outer_done)
+ return outer
+
+
def atomic(func: _FunctionT) -> _FunctionT:
@functools.wraps(func)
async def wrapper(*args: Any, **kwargs: Any) -> Any: