From 4e1b4f32e960d1ca25b7ca6a72f62d491c93d2ec Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Thu, 13 Feb 2025 10:16:44 +0100 Subject: [PATCH] Suggest to make implementations of some function always return awaitable. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See discussion in #1272; It is not deprecated, but being able to always know you can (and must) await should be simpler in the long run. Deprecating now is not the point, but I want to cover our bases, so that we are more confident later when and if we want to enforce those await. In particular many of those branches are not covered in our tests – and I don't even know wether they were ever taken; I changed some of the base methods to be async, but I'm happy to move those back to sync. A few other things use the `if awaitable(...):` pattern but are a bit more complicted, and some do not dates from 2021, so those will be dealt with separately. --- ipykernel/kernelbase.py | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/ipykernel/kernelbase.py b/ipykernel/kernelbase.py index d496e0c91..82856e00e 100644 --- a/ipykernel/kernelbase.py +++ b/ipykernel/kernelbase.py @@ -57,6 +57,14 @@ from ._version import kernel_protocol_version from .iostream import OutStream +_AWAITABLE_MESSAGE: str = ( + "For consistency across implementations, it is recommended that `{func_name}`" + " either be a coroutine function (`async def`) or return an awaitable object" + " (like a Future). It might become a requirement in the future." + " Coroutine functions and awaitables have been supported since" + " ipykernel 6.0 (2021)." +) + def _accepts_parameters(meth, param_names): parameters = inspect.signature(meth).parameters @@ -742,6 +750,12 @@ async def execute_request(self, socket, ident, parent): if inspect.isawaitable(reply_content): reply_content = await reply_content + else: + warnings.warn( + _AWAITABLE_MESSAGE.format(func_name="execute_request"), + PendingDeprecationWarning, + stacklevel=1, + ) # Flush output before sending the reply. if sys.stdout is not None: @@ -802,6 +816,12 @@ async def complete_request(self, socket, ident, parent): matches = self.do_complete(code, cursor_pos) if inspect.isawaitable(matches): matches = await matches + else: + warnings.warn( + _AWAITABLE_MESSAGE.format(func_name="do_complete"), + PendingDeprecationWarning, + stacklevel=1, + ) matches = json_clean(matches) self.session.send(socket, "complete_reply", matches, parent, ident) @@ -830,6 +850,12 @@ async def inspect_request(self, socket, ident, parent): ) if inspect.isawaitable(reply_content): reply_content = await reply_content + else: + warnings.warn( + _AWAITABLE_MESSAGE.format(func_name="do_inspect"), + PendingDeprecationWarning, + stacklevel=1, + ) # Before we send this object over, we scrub it for JSON usage reply_content = json_clean(reply_content) @@ -849,6 +875,12 @@ async def history_request(self, socket, ident, parent): reply_content = self.do_history(**content) if inspect.isawaitable(reply_content): reply_content = await reply_content + else: + warnings.warn( + _AWAITABLE_MESSAGE.format(func_name="do_history"), + PendingDeprecationWarning, + stacklevel=1, + ) reply_content = json_clean(reply_content) msg = self.session.send(socket, "history_reply", reply_content, parent, ident) @@ -966,6 +998,12 @@ async def shutdown_request(self, socket, ident, parent): content = self.do_shutdown(parent["content"]["restart"]) if inspect.isawaitable(content): content = await content + else: + warnings.warn( + _AWAITABLE_MESSAGE.format(func_name="do_shutdown"), + PendingDeprecationWarning, + stacklevel=1, + ) self.session.send(socket, "shutdown_reply", content, parent, ident=ident) # same content, but different msg_id for broadcasting on IOPub self._shutdown_message = self.session.msg("shutdown_reply", content, parent) @@ -990,6 +1028,12 @@ async def is_complete_request(self, socket, ident, parent): reply_content = self.do_is_complete(code) if inspect.isawaitable(reply_content): reply_content = await reply_content + else: + warnings.warn( + _AWAITABLE_MESSAGE.format(func_name="do_is_complete"), + PendingDeprecationWarning, + stacklevel=1, + ) reply_content = json_clean(reply_content) reply_msg = self.session.send(socket, "is_complete_reply", reply_content, parent, ident) self.log.debug("%s", reply_msg) @@ -1006,6 +1050,12 @@ async def debug_request(self, socket, ident, parent): reply_content = self.do_debug_request(content) if inspect.isawaitable(reply_content): reply_content = await reply_content + else: + warnings.warn( + _AWAITABLE_MESSAGE.format(func_name="do_debug_request"), + PendingDeprecationWarning, + stacklevel=1, + ) reply_content = json_clean(reply_content) reply_msg = self.session.send(socket, "debug_reply", reply_content, parent, ident) self.log.debug("%s", reply_msg)