lib.App: Release async runner in close()

close() closes the application's own event loop, but the AsyncRunner
is only released in the finally block of run(). An application that
creates a runner through call_async() and then calls close(), for
instance through the async context manager, therefore leaks the
runner, and close() does not fulfill its contract of releasing all
resources.

Move the AsyncRunner cleanup from the finally block of run() into
close() and reset the own-loop flag when the loop is closed, so that
close() releases everything and run() only has to call it.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-08-14 23:56:33 +02:00
commit 157fa86fb7
Signed by: Jan Lindemann
GPG key ID: 3750640C9E25DD61

View file

@ -216,11 +216,16 @@ class App: # export
)
def close(self) -> None:
"""Close the application and release all resources"""
if self.__async_runner is not None:
self.__async_runner.close()
self.__async_runner = None
if self.__own_eloop:
if self.__eloop is not None:
if not self.__eloop.is_closed():
self.__eloop.close()
self.__eloop = None
self.__own_eloop = False
async def __aenter__(self) -> App:
return self
@ -344,9 +349,6 @@ class App: # export
try:
ret = self.eloop.run_until_complete(self.__run(argv))
finally:
if self.__async_runner:
self.__async_runner.close()
self.__async_runner = None
self.close()
return ret