From: John Snow <jsnow@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Hanna Reitz" <hreitz@redhat.com>,
"Cleber Rosa" <crosa@redhat.com>, "John Snow" <jsnow@redhat.com>,
qemu-block@nongnu.org, "Daniel Berrangé" <berrange@redhat.com>,
"Kevin Wolf" <kwolf@redhat.com>,
"Richard W.M. Jones" <rjones@redhat.com>
Subject: [PATCH 12/19] python: backport '*really* remove get_event_loop'
Date: Mon, 1 Sep 2025 16:26:54 -0400 [thread overview]
Message-ID: <20250901202702.2971212-13-jsnow@redhat.com> (raw)
In-Reply-To: <20250901202702.2971212-1-jsnow@redhat.com>
A prior commit, aa1ff990, switched away from using get_event_loop *by
default*, but this is not good enough to avoid deprecation warnings as
`asyncio.get_event_loop_policy().get_event_loop()` is *also*
deprecated. Replace this mechanism with explicit calls to
asyncio.get_new_loop() and revise the cleanup mechanisms in __del__ to
match.
Reported-by: Richard W.M. Jones <rjones@redhat.com>
Reported-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: John Snow <jsnow@redhat.com>
cherry picked from commit 21ce2ee4f2df87efe84a27b9c5112487f4670622
Signed-off-by: John Snow <jsnow@redhat.com>
---
python/qemu/qmp/legacy.py | 47 +++++++++++++++++++++++++++-----------
python/qemu/qmp/qmp_tui.py | 10 ++++++--
2 files changed, 42 insertions(+), 15 deletions(-)
diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py
index 735d42971e9..775b1fdd3b3 100644
--- a/python/qemu/qmp/legacy.py
+++ b/python/qemu/qmp/legacy.py
@@ -86,13 +86,15 @@ def __init__(self,
"server argument should be False when passing a socket")
self._qmp = QMPClient(nickname)
+ self._created_loop = False
try:
self._aloop = asyncio.get_running_loop()
except RuntimeError:
- # No running loop; since this is a sync shim likely to be
- # used in fully sync programs, create one if neccessary.
- self._aloop = asyncio.get_event_loop_policy().get_event_loop()
+ # No running loop; since this is a sync shim likely to be used
+ # in sync programs without any event loop at all, create one.
+ self._aloop = asyncio.new_event_loop()
+ self._created_loop = True
self._address = address
self._timeout: Optional[float] = None
@@ -313,17 +315,36 @@ def send_fd_scm(self, fd: int) -> None:
self._qmp.send_fd_scm(fd)
def __del__(self) -> None:
- if self._qmp.runstate == Runstate.IDLE:
- return
+ if self._qmp.runstate != Runstate.IDLE:
+ self._qmp.logger.warning(
+ "QEMUMonitorProtocol object garbage collected without a prior "
+ "call to close()"
+ )
if not self._aloop.is_running():
- self.close()
- else:
- # Garbage collection ran while the event loop was running.
- # Nothing we can do about it now, but if we don't raise our
- # own error, the user will be treated to a lot of traceback
- # they might not understand.
+ if self._qmp.runstate != Runstate.IDLE:
+ # If the user neglected to close the QMP session and we
+ # are not currently running in an asyncio context, we
+ # have the opportunity to close the QMP session. If we
+ # do not do this, the error messages presented over
+ # dangling async resources may not make any sense to the
+ # user.
+ self.close()
+
+ # If we created our own loop (and we are not running inside
+ # of it), we must close it to avoid warnings and error
+ # messages upon program exit.
+ if self._created_loop:
+ self._aloop.close()
+
+ if self._qmp.runstate != Runstate.IDLE:
+ # If QMP is still not quiesced, it means that the garbage
+ # collector ran from a context within the event loop and we
+ # are simply too late to take any corrective action. Raise
+ # our own error to give meaningful feedback to the user in
+ # order to prevent pages of asyncio stacktrace jargon.
raise QMPError(
- "QEMUMonitorProtocol.close()"
- " was not called before object was garbage collected"
+ "QEMUMonitorProtocol.close() was not called before object was "
+ "garbage collected, and could not be closed due to GC running "
+ "in the event loop"
)
diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py
index 12bdc17c99e..d5526338f22 100644
--- a/python/qemu/qmp/qmp_tui.py
+++ b/python/qemu/qmp/qmp_tui.py
@@ -161,6 +161,7 @@ def __init__(self, address: Union[str, Tuple[str, int]], num_retries: int,
self.retry_delay = retry_delay if retry_delay else 2
self.retry: bool = False
self.exiting: bool = False
+ self._created_loop = False
super().__init__()
def add_to_history(self, msg: str, level: Optional[str] = None) -> None:
@@ -391,8 +392,9 @@ def run(self, debug: bool = False) -> None:
try:
self.aloop = asyncio.get_running_loop()
except RuntimeError:
- # No running asyncio event loop. Create one if necessary.
- self.aloop = asyncio.get_event_loop_policy().get_event_loop()
+ # No running asyncio event loop. Create one.
+ self.aloop = asyncio.new_event_loop()
+ self._created_loop = True
self.aloop.set_debug(debug)
@@ -416,6 +418,10 @@ def run(self, debug: bool = False) -> None:
logging.error('%s\n%s\n', str(err), pretty_traceback())
raise err
+ def __del__(self) -> None:
+ if self._created_loop and self.aloop:
+ self.aloop.close()
+
class StatusBar(urwid.Text):
"""
--
2.50.1
next prev parent reply other threads:[~2025-09-01 20:28 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-01 20:26 [PATCH 00/19] python: 3.14 compatibility and python-qemu-qmp synchronization John Snow
2025-09-01 20:26 ` [PATCH 01/19] python: backport 'Change error classes to have better repr methods' John Snow
2025-09-01 20:26 ` [PATCH 02/19] python: backport 'EventListener: add __repr__ method' John Snow
2025-09-01 20:26 ` [PATCH 03/19] python: backport 'kick event queue on legacy event_pull()' John Snow
2025-09-01 20:26 ` [PATCH 04/19] python: backport 'protocol: adjust logging name when changing client name' John Snow
2025-09-01 20:26 ` [PATCH 05/19] python: backport 'drop Python3.6 workarounds' John Snow
2025-09-01 20:26 ` [PATCH 06/19] python: backport 'qmp-shell: add common_parser()' John Snow
2025-09-01 20:26 ` [PATCH 07/19] python: backport 'feat: allow setting read buffer limit' John Snow
2025-09-01 20:26 ` [PATCH 08/19] python: backport 'make require() preserve async-ness' John Snow
2025-09-01 20:26 ` [PATCH 09/19] python: backport 'qmp-shell-wrap: handle missing binary gracefully' John Snow
2025-09-01 20:26 ` [PATCH 10/19] python: backport 'qmp-tui: Do not crash if optional dependencies are not met' John Snow
2025-09-01 20:26 ` [PATCH 11/19] python: backport 'Remove deprecated get_event_loop calls' John Snow
2025-09-01 20:26 ` John Snow [this message]
2025-09-01 20:26 ` [PATCH 13/19] python: backport 'python: avoid creating additional event loops per thread' John Snow
2025-09-01 20:26 ` [PATCH 14/19] python: synchronize qemu.qmp documentation John Snow
2025-09-01 20:26 ` [PATCH 15/19] iotests: drop compat for old version context manager John Snow
2025-09-01 20:26 ` [PATCH 16/19] python: ensure QEMUQtestProtocol closes its socket John Snow
2025-09-01 20:26 ` [PATCH 17/19] iotests/147: ensure temporary sockets are closed before exiting John Snow
2025-09-01 20:27 ` [PATCH 18/19] iotests/151: ensure subprocesses are cleaned up John Snow
2025-09-01 20:27 ` [PATCH 19/19] iotests/check: always enable all python warnings John Snow
2025-09-02 16:54 ` [PATCH 00/19] python: 3.14 compatibility and python-qemu-qmp synchronization Daniel P. Berrangé
2025-09-02 16:58 ` John Snow
2025-09-02 19:34 ` Daniel P. Berrangé
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250901202702.2971212-13-jsnow@redhat.com \
--to=jsnow@redhat.com \
--cc=berrange@redhat.com \
--cc=crosa@redhat.com \
--cc=hreitz@redhat.com \
--cc=kwolf@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=rjones@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).