From 4e1b4f32e960d1ca25b7ca6a72f62d491c93d2ec Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Thu, 13 Feb 2025 10:16:44 +0100 Subject: [PATCH 1/5] 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) From de11cd1a5b30604455207d7f45b146a5650d8e7c Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Fri, 14 Feb 2025 08:34:15 +0100 Subject: [PATCH 2/5] Update ipykernel/kernelbase.py Co-authored-by: David Brochart --- ipykernel/kernelbase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipykernel/kernelbase.py b/ipykernel/kernelbase.py index 82856e00e..ca2c189fb 100644 --- a/ipykernel/kernelbase.py +++ b/ipykernel/kernelbase.py @@ -60,7 +60,7 @@ _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." + " (like an `asyncio.Future`). It might become a requirement in the future." " Coroutine functions and awaitables have been supported since" " ipykernel 6.0 (2021)." ) From 2e45aa5b313c3e18c7fce0d353fedd0b048a46f5 Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Mon, 17 Feb 2025 15:52:11 +0100 Subject: [PATCH 3/5] add ignore to not crash tests --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 4de39428b..c27070308 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -190,6 +190,10 @@ filterwarnings= [ # ignore unclosed sqlite in traits "ignore:unclosed database in Date: Mon, 17 Feb 2025 16:49:08 +0100 Subject: [PATCH 4/5] more info in message --- ipykernel/kernelbase.py | 30 ++++++++++++++++++++++-------- pyproject.toml | 2 +- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/ipykernel/kernelbase.py b/ipykernel/kernelbase.py index ca2c189fb..cfc39ed31 100644 --- a/ipykernel/kernelbase.py +++ b/ipykernel/kernelbase.py @@ -62,7 +62,7 @@ " either be a coroutine function (`async def`) or return an awaitable object" " (like an `asyncio.Future`). It might become a requirement in the future." " Coroutine functions and awaitables have been supported since" - " ipykernel 6.0 (2021)." + " ipykernel 6.0 (2021). {target} does not seem to return an awaitable" ) @@ -752,7 +752,9 @@ async def execute_request(self, socket, ident, parent): reply_content = await reply_content else: warnings.warn( - _AWAITABLE_MESSAGE.format(func_name="execute_request"), + _AWAITABLE_MESSAGE.format( + func_name="do_execute", target=self.do_execute + ), PendingDeprecationWarning, stacklevel=1, ) @@ -818,7 +820,9 @@ async def complete_request(self, socket, ident, parent): matches = await matches else: warnings.warn( - _AWAITABLE_MESSAGE.format(func_name="do_complete"), + _AWAITABLE_MESSAGE.format( + func_name="do_complete", target=self.do_complete + ), PendingDeprecationWarning, stacklevel=1, ) @@ -852,7 +856,9 @@ async def inspect_request(self, socket, ident, parent): reply_content = await reply_content else: warnings.warn( - _AWAITABLE_MESSAGE.format(func_name="do_inspect"), + _AWAITABLE_MESSAGE.format( + func_name="do_inspect", target=self.do_inspect + ), PendingDeprecationWarning, stacklevel=1, ) @@ -877,7 +883,9 @@ async def history_request(self, socket, ident, parent): reply_content = await reply_content else: warnings.warn( - _AWAITABLE_MESSAGE.format(func_name="do_history"), + _AWAITABLE_MESSAGE.format( + func_name="do_history", target=self.do_history + ), PendingDeprecationWarning, stacklevel=1, ) @@ -1000,7 +1008,9 @@ async def shutdown_request(self, socket, ident, parent): content = await content else: warnings.warn( - _AWAITABLE_MESSAGE.format(func_name="do_shutdown"), + _AWAITABLE_MESSAGE.format( + func_name="do_shutdown", target=self.do_shutdown + ), PendingDeprecationWarning, stacklevel=1, ) @@ -1030,7 +1040,9 @@ async def is_complete_request(self, socket, ident, parent): reply_content = await reply_content else: warnings.warn( - _AWAITABLE_MESSAGE.format(func_name="do_is_complete"), + _AWAITABLE_MESSAGE.format( + func_name="do_is_complete", target=self.do_is_complete + ), PendingDeprecationWarning, stacklevel=1, ) @@ -1052,7 +1064,9 @@ async def debug_request(self, socket, ident, parent): reply_content = await reply_content else: warnings.warn( - _AWAITABLE_MESSAGE.format(func_name="do_debug_request"), + _AWAITABLE_MESSAGE.format( + func_name="do_debug_request", target=self.do_debug_request + ), PendingDeprecationWarning, stacklevel=1, ) diff --git a/pyproject.toml b/pyproject.toml index c27070308..6103d2da8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -192,7 +192,7 @@ filterwarnings= [ "ignore:unclosed database in Date: Mon, 17 Feb 2025 15:49:26 +0000 Subject: [PATCH 5/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ipykernel/kernelbase.py | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/ipykernel/kernelbase.py b/ipykernel/kernelbase.py index cfc39ed31..c44ff58dc 100644 --- a/ipykernel/kernelbase.py +++ b/ipykernel/kernelbase.py @@ -752,9 +752,7 @@ async def execute_request(self, socket, ident, parent): reply_content = await reply_content else: warnings.warn( - _AWAITABLE_MESSAGE.format( - func_name="do_execute", target=self.do_execute - ), + _AWAITABLE_MESSAGE.format(func_name="do_execute", target=self.do_execute), PendingDeprecationWarning, stacklevel=1, ) @@ -820,9 +818,7 @@ async def complete_request(self, socket, ident, parent): matches = await matches else: warnings.warn( - _AWAITABLE_MESSAGE.format( - func_name="do_complete", target=self.do_complete - ), + _AWAITABLE_MESSAGE.format(func_name="do_complete", target=self.do_complete), PendingDeprecationWarning, stacklevel=1, ) @@ -856,9 +852,7 @@ async def inspect_request(self, socket, ident, parent): reply_content = await reply_content else: warnings.warn( - _AWAITABLE_MESSAGE.format( - func_name="do_inspect", target=self.do_inspect - ), + _AWAITABLE_MESSAGE.format(func_name="do_inspect", target=self.do_inspect), PendingDeprecationWarning, stacklevel=1, ) @@ -883,9 +877,7 @@ async def history_request(self, socket, ident, parent): reply_content = await reply_content else: warnings.warn( - _AWAITABLE_MESSAGE.format( - func_name="do_history", target=self.do_history - ), + _AWAITABLE_MESSAGE.format(func_name="do_history", target=self.do_history), PendingDeprecationWarning, stacklevel=1, ) @@ -1008,9 +1000,7 @@ async def shutdown_request(self, socket, ident, parent): content = await content else: warnings.warn( - _AWAITABLE_MESSAGE.format( - func_name="do_shutdown", target=self.do_shutdown - ), + _AWAITABLE_MESSAGE.format(func_name="do_shutdown", target=self.do_shutdown), PendingDeprecationWarning, stacklevel=1, ) @@ -1040,9 +1030,7 @@ async def is_complete_request(self, socket, ident, parent): reply_content = await reply_content else: warnings.warn( - _AWAITABLE_MESSAGE.format( - func_name="do_is_complete", target=self.do_is_complete - ), + _AWAITABLE_MESSAGE.format(func_name="do_is_complete", target=self.do_is_complete), PendingDeprecationWarning, stacklevel=1, )