lib.App.call_async(): Add method

Use the AsyncRunner class introduced in the previous commit to add a
call_async() method, allowing to run async functions from sync
functions by spawning an extra event loop.

Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-03-18 17:09:20 +01:00
commit df40af9fc3

View file

@ -1,12 +1,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import Any from __future__ import annotations
from typing import Any, TYPE_CHECKING
import os, sys, argparse, re, asyncio, cProfile import os, sys, argparse, re, asyncio, cProfile
from .AsyncRunner import AsyncRunner
from .log import * from .log import *
from .Types import LoadTypes from .Types import LoadTypes
if TYPE_CHECKING:
from typing import TypeVar
from collections.abc import Awaitable
T = TypeVar("T")
class App: # export class App: # export
def _add_arguments(self, parser): def _add_arguments(self, parser):
@ -66,6 +74,7 @@ class App: # export
if eloop is None: if eloop is None:
self.__eloop = asyncio.get_event_loop() self.__eloop = asyncio.get_event_loop()
self.__own_eloop = True self.__own_eloop = True
self.__async_runner: AsyncRunner|None = None
self.__parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, self.__parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description=description, add_help=False) description=description, add_help=False)
@ -138,10 +147,19 @@ class App: # export
async def _run(self, args: argparse.Namespace) -> None: async def _run(self, args: argparse.Namespace) -> None:
return await self.args.func(args) return await self.args.func(args)
def call_async(self, awaitable: Awaitable[T], timeout: float | None = None) -> T:
return self.async_runner.call(awaitable, timeout)
@property @property
def eloop(self) -> asyncio.AbstractEventLoop: def eloop(self) -> asyncio.AbstractEventLoop:
return self.__eloop return self.__eloop
@property
def async_runner(self) -> AsyncRunner:
if self.__async_runner is None:
self.__async_runner = AsyncRunner()
return self.__async_runner
@property @property
def cmdline(self) -> str: def cmdline(self) -> str:
if self.__cmdline is None: if self.__cmdline is None:
@ -159,7 +177,13 @@ class App: # export
return self.__parser return self.__parser
def run(self, argv=None) -> None: def run(self, argv=None) -> None:
return self.__eloop.run_until_complete(self.__run(argv)) # type: ignore try:
ret = self.__eloop.run_until_complete(self.__run(argv)) # type: ignore
finally:
if self.__async_runner:
self.__async_runner.close()
self.__async_runner = None
return ret
def run_sub_commands(description = '', name_filter = '^Cmd.*', modules=None, argv=None): # export def run_sub_commands(description = '', name_filter = '^Cmd.*', modules=None, argv=None): # export
app = App(description, name_filter, modules) app = App(description, name_filter, modules)