Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to signal shutdown by user code? #1151

Open
allefeld opened this issue Oct 2, 2023 · 7 comments
Open

How to signal shutdown by user code? #1151

allefeld opened this issue Oct 2, 2023 · 7 comments

Comments

@allefeld
Copy link

allefeld commented Oct 2, 2023

I'm implementing a kernel by inheriting from ipykernel.kernelbase.Kernel, as described at Making simple Python wrapper kernels.

My do_execute method passes code to an external interpreter implemented in a Python module, and it is possible that this code includes an instruction to shut down the interpreter, comparable to sys.exit(0) in Python. When that happens, the interpreter raises an exception.

Because after that code the interpreter is no longer usable, I want to catch that exception and in response initiate a kernel shutdown. Unfortunately, I wasn't able to figure out how to do so.

Is it possible to initiate a kernel shutdown from within the kernel? If yes, how?

If not, what is the recommended way to deal with code which shuts down the interpreter? Detecting that from the code might be impossible to do correctly. And even if I could do it, I would still have to initiate shutdown from within do_execute.

@allefeld
Copy link
Author

allefeld commented Oct 4, 2023

I found a solution: I was able to detect the execution of an instruction to shut down the interpreter, but only afterwards, because the Python interface to the interpreter throws an Exception.

I still don't know how to trigger a shutdown, and would still be interested how to do so, but for the moment I settled for restarting the interpreter and sending a message to the user.

@jjvraw
Copy link
Contributor

jjvraw commented Dec 2, 2023

If you're extending KernelBase, would do_shutdown with restart=False not satisfy this?

def do_shutdown(self, restart):
"""Override in subclasses to do things when the frontend shuts down the
kernel.
"""
return {"status": "ok", "restart": restart}

@allefeld
Copy link
Author

allefeld commented Dec 4, 2023

@jjvraw, I have implemented do_shutdown. But as the docstring says, this is "to do things when the frontend shuts down the kernel", i.e. cleanup actions.

I'm looking for a way to initiate a shutdown from the kernel, within do_execute. Basically, how the kernel can make the frontend to shut down the kernel, thereby calling do_shutdown?

@martinRenou
Copy link
Contributor

do_shutdown is really the logic that would be called if the user tries to shutdown from the client.

how the kernel can make the frontend to shut down the kernel

The kernel cannot know how many clients there are and knows nothing about the clients actually. So it cannot request anything to any client.

What you can do is kill the process yourself from code though. According to the messaging protocol it's considered a valid shutdown if the user typed "quit" in code. So you can do it under the conditions you want.

The clients will then be notified that the kernel is not responding anymore and they would need to restart it from the UI themselves.

@allefeld
Copy link
Author

allefeld commented Dec 4, 2023

@martinRenou

What you can do is kill the process yourself from code though. According to the messaging protocol it's considered a valid shutdown if the user typed "quit" in code. So you can do it under the conditions you want.

Are you referring to the "Note" under "Kernel shutdown"?

I think I tried that. However, it did not lead to a clean exit in Jupyter Console and Jupyter QtConsole.

What I'm looking for is that: The user sends code which includes "quit", and in response Jupyter Console / Jupyter QtConsole exits cleanly.

@jjvraw
Copy link
Contributor

jjvraw commented Dec 4, 2023

Ah, apologies @allefeld. I tried a less involved approach compared to @martinRenou's suggestion. If one can implement execute_request and catch the exception of the interpreter:

def execute_request(self, stream, ident, parent):
       try:
            return super().execute_request(stream, ident, parent)
        catch SomeException:
            parent['content']['restart'] = False
            return super().shutdown_request(stream, ident, parent)

the kernel will shutdown.

However, as @martinRenou said

The kernel cannot know how many clients there are and knows nothing about the clients actually. So it cannot request anything to any client.

If the front-end is not expecting the kernel to shutdown, there will be some form of rejection/manual handling from the unexpected behavior.

This is my understanding as of now, at least. Best of luck !

@allefeld
Copy link
Author

allefeld commented Dec 4, 2023

@jjvraw, thanks for the tip, I will try that!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants