Skip to content

Commit

Permalink
Suggest to make implementations of some function always return awaita…
Browse files Browse the repository at this point in the history
…ble.

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.
  • Loading branch information
Carreau committed Nov 13, 2024
1 parent 8c8d7d2 commit bf4f57d
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 4 deletions.
4 changes: 2 additions & 2 deletions ipykernel/ipkernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ async def run(execution: Execution) -> None:

return reply_content

def do_complete(self, code, cursor_pos):
async def do_complete(self, code, cursor_pos):
"""Handle code completion."""
if _use_experimental_60_completion and self.use_experimental_completions:
return self._experimental_do_complete(code, cursor_pos)
Expand Down Expand Up @@ -625,7 +625,7 @@ def do_history(
"history": list(hist),
}

def do_shutdown(self, restart):
async def do_shutdown(self, restart):
"""Handle kernel shutdown."""
if self.shell:
self.shell.exit_now = True
Expand Down
54 changes: 52 additions & 2 deletions ipykernel/kernelbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@
from ._version import kernel_protocol_version
from .iostream import OutStream

_AWAITABLE_MESSAGE = (
"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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -802,11 +816,17 @@ 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)

def do_complete(self, code, cursor_pos):
async def do_complete(self, code, cursor_pos):
"""Override in subclasses to find completions."""
return {
"matches": [],
Expand All @@ -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="inspect_request"),
PendingDeprecationWarning,
stacklevel=1,
)

# Before we send this object over, we scrub it for JSON usage
reply_content = json_clean(reply_content)
Expand All @@ -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="history_request"),
PendingDeprecationWarning,
stacklevel=1,
)

reply_content = json_clean(reply_content)
msg = self.session.send(socket, "history_reply", reply_content, parent, ident)
Expand Down Expand Up @@ -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)
Expand All @@ -974,7 +1012,7 @@ async def shutdown_request(self, socket, ident, parent):

self.stop()

def do_shutdown(self, restart):
async def do_shutdown(self, restart):
"""Override in subclasses to do things when the frontend shuts down the
kernel.
"""
Expand All @@ -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_execute"),
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)
Expand All @@ -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="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)
Expand Down

0 comments on commit bf4f57d

Please sign in to comment.