lib.App: Restore previous event loop in run()
When run() creates an event loop, it installs it with set_event_loop() but never restores the thread's previous loop, so after run() returns, the thread is left with the now-closed loop created by run(). Any code that calls get_event_loop() afterwards gets a closed loop, and on Python 3.13+ a thread that had no loop at all starts emitting or raising deprecation errors that run() caused. Capture the thread's current loop with _get_current_event_loop() before installing a new one, and restore it in the finally block. If there was no previous loop, unset the loop with set_event_loop(None) so that the thread is left without a loop instead of with the closed one. 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:
parent
157fa86fb7
commit
845601ff10
1 changed files with 20 additions and 0 deletions
|
|
@ -4,6 +4,7 @@ import asyncio
|
|||
import cProfile
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace
|
||||
from typing import TYPE_CHECKING, Any, cast, override
|
||||
|
|
@ -31,6 +32,17 @@ if TYPE_CHECKING:
|
|||
from typing import TypeVar
|
||||
T = TypeVar('T')
|
||||
|
||||
def _get_current_event_loop() -> asyncio.AbstractEventLoop | None:
|
||||
"""Return the current event loop of this thread, or None if there is
|
||||
none, without creating one implicitly or emitting a deprecation
|
||||
warning."""
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('error', DeprecationWarning)
|
||||
try:
|
||||
return asyncio.get_event_loop()
|
||||
except (RuntimeError, DeprecationWarning):
|
||||
return None
|
||||
|
||||
class App: # export
|
||||
|
||||
def _add_arguments(self, parser: ArgumentParser) -> None:
|
||||
|
|
@ -341,7 +353,9 @@ class App: # export
|
|||
return self.__parser
|
||||
|
||||
def run(self, argv: list[str] | None = None) -> None:
|
||||
previous_eloop: asyncio.AbstractEventLoop | None = None
|
||||
if self.__eloop is None:
|
||||
previous_eloop = _get_current_event_loop()
|
||||
eloop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(eloop)
|
||||
self.__eloop = eloop
|
||||
|
|
@ -350,6 +364,12 @@ class App: # export
|
|||
ret = self.eloop.run_until_complete(self.__run(argv))
|
||||
finally:
|
||||
self.close()
|
||||
# -- Restore the event loop the thread had before run(), or
|
||||
# unset the loop if there was none.
|
||||
if previous_eloop is not None:
|
||||
asyncio.set_event_loop(previous_eloop)
|
||||
else:
|
||||
asyncio.set_event_loop(None)
|
||||
return ret
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue