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 call InteractiveShellEmbed-based custom excepthook via jupyter-console --existing #14376

Open
cknoll opened this issue Mar 28, 2024 · 0 comments

Comments

@cknoll
Copy link

cknoll commented Mar 28, 2024

Main question: How can I run an embedded IPython shell with a full-featured prompt (simple_prompt=False) in the namespace of a frame where an exception occurred in the context of a running IPython kernel of a Jupyter notebook?

Explanation:

I have the following usecase (which is almost solved, up to one major flaw):

In Jupyter notebook (python kernel) I run some nested function call failing_function(). Deep down in the call stack an exception happens.
I want to debug the situation on the commandline with my custom excepthook (ipydex.ips_excepthook; it opens an emebedded IPython shell right in the frame where the exception occurs and offers the possibility to move up in the frame stack, which I find more useful than the classical IPython debugger).

I can achieve this with the following steps: Within an ordinary python script:

import sys
from jupyter_console.app import ZMQTerminalIPythonApp

# mimic jupyter-console --existing: attach shell to running kernel
sys.argv = [sys.argv[0], "--existing"]
app = ZMQTerminalIPythonApp.instance()
app.initialize(None)
super(ZMQTerminalIPythonApp, app).start()

code = """
import ipydex
import traceback
try:
    failing_function()
except Exception as ex:
    value, tb = traceback._parse_value_tb(ex, traceback._sentinel, traceback._sentinel)
    ipydex.ips_excepthook(type(ex), ex, tb)
"""

# run this code in the context of the running kernel
app.shell.run_cell(code)

Problem:

This works, but with one flaw: It only offers the simple_prompt-mode of the embedded IPython shell. Background: My custom excepthook internally creates an instance of from IPython.terminal.embed.InteractiveShellEmbed and calls its mainloop (as intended). This mainloop has two different prompt-modes: default and simple. The default mode has many desired features (colored output, TAB-completion, ...), which I desire but the simple mode does not offer. The mode is determined by this line IPython/terminal/interactiveshell.py#L134.

I tried to force the class variable InteractiveShellEmbed.simple_prompt to False but then I get RuntimeError: This event loop is already running (would have been too easy).

I see three possible ways to achieve the desired behavior:

  • a) Let InteractiveShellEmbed run in default (i.e. not simple) mode inside the running ZMQTerminalIPythonApp.
  • b) Run ZMQTerminalIPythonApp.shell.mainloop() directly in the namespace of the frame where the exception occurs (and offer the possibility to go up and down again in the frame stack).
  • c) Temporarily overwrite sys.excepthook with my custom ipydex.ips_excepthook and just execute failing_function() without try ... except.

For a): The whole story with the custom excepthook is just my motivation. The problem boils down to achieving that app.shell.run_cell('IPython.embed(colors="neutral")') runs in default (not simple) prompt mode.
For b): I dont know whether that is possible because InteractiveShellEmbed.mainloop accepts key word args local_ns and module (global name space), whereas ZMQTerminalIPythonApp.shell.mainloop does not.
For c): This works perfectly for normal scripts but not when "injecting" code in a running jupyter kernel, because overwriting sys.excepthook does not seem to have an effect.

Derived questions:

  1. Which of the directions (a, b, c) is the most promising?
  2. What would be a good next step?
  3. What other possibilities do I have?
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

1 participant