From e7bb75bc3edcdc481054082dd04661d630b760ab Mon Sep 17 00:00:00 2001 From: f321x Date: Fri, 5 Sep 2025 10:26:58 +0200 Subject: [PATCH] chore: replace calls to asyncio.iscoroutinefunction Replace calls to deprecated asyncio.iscoroutinefunction with calls to inspect.iscoroutinefunction to prevent the following deprecation warnings from showing up if running with Python 3.14: ``` /home/user/code/electrum-fork/electrum/util.py:1225: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine' /home/user/code/electrum-fork/electrum/util.py:507: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead if asyncio.iscoroutinefunction(func): /home/user/code/electrum-fork/electrum/util.py:1246: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine' /home/user/code/electrum-fork/electrum/lnpeer.py:272: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine' /home/user/code/electrum-fork/electrum/util.py:1225: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine' /home/user/code/electrum-fork/electrum/util.py:507: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead if asyncio.iscoroutinefunction(func): ``` --- electrum/commands.py | 2 +- electrum/gui/qt/main_window.py | 3 ++- electrum/lnpeer.py | 5 +++-- electrum/util.py | 11 ++++++----- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/electrum/commands.py b/electrum/commands.py index a886197a4..9d2cf9a86 100644 --- a/electrum/commands.py +++ b/electrum/commands.py @@ -2204,7 +2204,7 @@ def plugin_command(s, plugin_name): name = plugin_name + '_' + func.__name__ if name in known_commands or hasattr(Commands, name): raise Exception(f"Command name {name} already exists. Plugin commands should not overwrite other commands.") - assert asyncio.iscoroutinefunction(func), f"Plugin commands must be a coroutine: {name}" + assert inspect.iscoroutinefunction(func), f"Plugin commands must be a coroutine: {name}" @command(s) @wraps(func) diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index a2aaf6a98..45783aa78 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -36,6 +36,7 @@ import queue import asyncio from typing import Optional, TYPE_CHECKING, Sequence, Union, Dict, Mapping, Callable, List, Set import concurrent.futures +import inspect from PyQt6.QtGui import QPixmap, QKeySequence, QIcon, QCursor, QFont, QFontMetrics, QAction, QShortcut from PyQt6.QtCore import Qt, QRect, QStringListModel, QSize, pyqtSignal, QTimer @@ -2734,7 +2735,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): Registers a callback that will be called when the wallet is closed. If the callback returns a string it will be shown to the user as a warning to prevent them closing the wallet. """ - assert not asyncio.iscoroutinefunction(callback) + assert not inspect.iscoroutinefunction(callback) def warning_callback() -> Optional[str]: try: return callback() diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index bad460b10..ea0d16ec2 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -13,6 +13,7 @@ from typing import Tuple, Dict, TYPE_CHECKING, Optional, Union, Set, Callable, A from datetime import datetime import functools from functools import partial +import inspect import electrum_ecc as ecc from electrum_ecc import ecdsa_sig64_from_r_and_s, ecdsa_der_sig_from_ecdsa_sig64, ECPubkey @@ -256,7 +257,7 @@ class Peer(Logger, EventListener): payload['sender_node_id'] = self.pubkey # note: the message handler might be async or non-async. In either case, by default, # we wait for it to complete before we return, i.e. before the next message is processed. - if asyncio.iscoroutinefunction(f): + if inspect.iscoroutinefunction(f): async with AsyncHangDetector( message=f"message handler still running for {message_type.upper()}", logger=self.logger, @@ -269,7 +270,7 @@ class Peer(Logger, EventListener): """Makes a message handler non-blocking: while processing the message, the message_loop keeps processing subsequent incoming messages asynchronously. """ - assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine' + assert inspect.iscoroutinefunction(func), 'func needs to be a coroutine' @functools.wraps(func) async def wrapper(self: 'Peer', *args, **kwargs): return await self.taskgroup.spawn(func(self, *args, **kwargs)) diff --git a/electrum/util.py b/electrum/util.py index 0bad5f510..748c1c8f5 100644 --- a/electrum/util.py +++ b/electrum/util.py @@ -54,6 +54,7 @@ from abc import abstractmethod, ABC import enum from contextlib import nullcontext import traceback +import inspect import aiohttp from aiohttp_socks import ProxyConnector, ProxyType @@ -504,7 +505,7 @@ def profiler(func=None, *, min_threshold: Union[int, float, None] = None): if min_threshold is None or t > min_threshold: _profiler_logger.debug(f"{func.__qualname__} {t:,.4f} sec") - if asyncio.iscoroutinefunction(func): + if inspect.iscoroutinefunction(func): async def do_profile(*args, **kw_args): timer_start() o = await func(*args, **kw_args) @@ -1222,7 +1223,7 @@ def is_subpath(long_path: str, short_path: str) -> bool: def log_exceptions(func): """Decorator to log AND re-raise exceptions.""" - assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine' + assert inspect.iscoroutinefunction(func), 'func needs to be a coroutine' @functools.wraps(func) async def wrapper(*args, **kwargs): @@ -1243,7 +1244,7 @@ def log_exceptions(func): def ignore_exceptions(func): """Decorator to silently swallow all exceptions.""" - assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine' + assert inspect.iscoroutinefunction(func), 'func needs to be a coroutine' @functools.wraps(func) async def wrapper(*args, **kwargs): @@ -1761,7 +1762,7 @@ def run_sync_function_on_asyncio_thread(func: Callable[[], Any], *, block: bool) For any other thread, we only wait for completion if `block` is True. """ - assert not asyncio.iscoroutinefunction(func), "func must be a non-async function" + assert not inspect.iscoroutinefunction(func), "func must be a non-async function" asyncio_loop = get_asyncio_loop() if get_running_loop() == asyncio_loop: # we are running on the asyncio thread func() @@ -1964,7 +1965,7 @@ class CallbackManager(Logger): with self.callback_lock: callbacks = self.callbacks[event][:] for callback in callbacks: - if asyncio.iscoroutinefunction(callback): # async cb + if inspect.iscoroutinefunction(callback): # async cb fut = asyncio.run_coroutine_threadsafe(callback(*args), loop) def on_done(fut_: concurrent.futures.Future):