debugger.py
178 lines
| 6.5 KiB
| text/x-python
|
PythonLexer
Jonathan Slenders
|
r25342 | import asyncio | ||
Matthias Bussonnier
|
r26809 | import os | ||
Thomas Kluyver
|
r23318 | import sys | ||
Thomas Kluyver
|
r22391 | from IPython.core.debugger import Pdb | ||
from IPython.core.completer import IPCompleter | ||||
from .ptutils import IPythonPTCompleter | ||||
Blazej Michalik
|
r26323 | from .shortcuts import create_ipython_shortcuts | ||
Iwan Briquemont
|
r26465 | from . import embed | ||
Thomas Kluyver
|
r22391 | |||
Matthias Bussonnier
|
r26809 | from pathlib import Path | ||
Jonathan Slenders
|
r24376 | from pygments.token import Token | ||
from prompt_toolkit.shortcuts.prompt import PromptSession | ||||
Thomas Kluyver
|
r22391 | from prompt_toolkit.enums import EditingMode | ||
Jonathan Slenders
|
r24376 | from prompt_toolkit.formatted_text import PygmentsTokens | ||
Matthias Bussonnier
|
r26809 | from prompt_toolkit.history import InMemoryHistory, FileHistory | ||
Maor Kleinberger
|
r27108 | from concurrent.futures import ThreadPoolExecutor | ||
tillahoffmann
|
r22874 | |||
Matthias Bussonnier
|
r25279 | from prompt_toolkit import __version__ as ptk_version | ||
PTK3 = ptk_version.startswith('3.') | ||||
Thomas Kluyver
|
r22391 | |||
Matthias Bussonnier
|
r27693 | # we want to avoid ptk as much as possible when using subprocesses | ||
# as it uses cursor positioning requests, deletes color .... | ||||
_use_simple_prompt = "IPY_TEST_SIMPLE_PROMPT" in os.environ | ||||
Thomas Kluyver
|
r22391 | class TerminalPdb(Pdb): | ||
Terry Davis
|
r25174 | """Standalone IPython debugger.""" | ||
Maor Kleinberger
|
r25868 | def __init__(self, *args, pt_session_options=None, **kwargs): | ||
Thomas Kluyver
|
r22391 | Pdb.__init__(self, *args, **kwargs) | ||
self._ptcomp = None | ||||
Maor Kleinberger
|
r25868 | self.pt_init(pt_session_options) | ||
Maor Kleinberger
|
r27108 | self.thread_executor = ThreadPoolExecutor(1) | ||
Maor Kleinberger
|
r25868 | |||
def pt_init(self, pt_session_options=None): | ||||
"""Initialize the prompt session and the prompt loop | ||||
and store them in self.pt_app and self.pt_loop. | ||||
Blazej Michalik
|
r26324 | |||
Maor Kleinberger
|
r25868 | Additional keyword arguments for the PromptSession class | ||
can be specified in pt_session_options. | ||||
""" | ||||
if pt_session_options is None: | ||||
pt_session_options = {} | ||||
Blazej Michalik
|
r26324 | |||
Jonathan Slenders
|
r24376 | def get_prompt_tokens(): | ||
Thomas Kluyver
|
r22391 | return [(Token.Prompt, self.prompt)] | ||
if self._ptcomp is None: | ||||
Blazej Michalik
|
r26328 | compl = IPCompleter( | ||
shell=self.shell, namespace={}, global_namespace={}, parent=self.shell | ||||
) | ||||
Matthias Bussonnier
|
r25839 | # add a completer for all the do_ methods | ||
methods_names = [m[3:] for m in dir(self) if m.startswith("do_")] | ||||
def gen_comp(self, text): | ||||
return [m for m in methods_names if m.startswith(text)] | ||||
import types | ||||
newcomp = types.MethodType(gen_comp, compl) | ||||
compl.custom_matchers.insert(0, newcomp) | ||||
# end add completer. | ||||
Jonathan Slenders
|
r24376 | self._ptcomp = IPythonPTCompleter(compl) | ||
Thomas Kluyver
|
r22391 | |||
Matthias Bussonnier
|
r26809 | # setup history only when we start pdb | ||
if self.shell.debugger_history is None: | ||||
if self.shell.debugger_history_file is not None: | ||||
p = Path(self.shell.debugger_history_file).expanduser() | ||||
if not p.exists(): | ||||
p.touch() | ||||
self.debugger_history = FileHistory(os.path.expanduser(str(p))) | ||||
else: | ||||
self.debugger_history = InMemoryHistory() | ||||
Can Sarigol
|
r27538 | else: | ||
self.debugger_history = self.shell.debugger_history | ||||
Matthias Bussonnier
|
r26809 | |||
Matthias Bussonnier
|
r25279 | options = dict( | ||
message=(lambda: PygmentsTokens(get_prompt_tokens())), | ||||
editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()), | ||||
Daniel Hahler
|
r25494 | key_bindings=create_ipython_shortcuts(self.shell), | ||
Matthias Bussonnier
|
r27137 | history=self.debugger_history, | ||
Matthias Bussonnier
|
r25279 | completer=self._ptcomp, | ||
enable_history_search=True, | ||||
mouse_support=self.shell.mouse_support, | ||||
complete_style=self.shell.pt_complete_style, | ||||
Matthias Bussonnier
|
r26810 | style=getattr(self.shell, "style", None), | ||
Matthias Bussonnier
|
r25279 | color_depth=self.shell.color_depth, | ||
Thomas Kluyver
|
r22391 | ) | ||
Matthias Bussonnier
|
r25279 | if not PTK3: | ||
Matthias Bussonnier
|
r25325 | options['inputhook'] = self.shell.inputhook | ||
Maor Kleinberger
|
r25868 | options.update(pt_session_options) | ||
Matthias Bussonnier
|
r27693 | if not _use_simple_prompt: | ||
self.pt_loop = asyncio.new_event_loop() | ||||
self.pt_app = PromptSession(**options) | ||||
Matthias Bussonnier
|
r25279 | |||
Thomas Kluyver
|
r22391 | def cmdloop(self, intro=None): | ||
"""Repeatedly issue a prompt, accept input, parse an initial prefix | ||||
off the received input, and dispatch to action methods, passing them | ||||
the remainder of the line as argument. | ||||
override the same methods from cmd.Cmd to provide prompt toolkit replacement. | ||||
""" | ||||
if not self.use_rawinput: | ||||
raise ValueError('Sorry ipdb does not support use_rawinput=False') | ||||
Jonathan Slenders
|
r25511 | # In order to make sure that prompt, which uses asyncio doesn't | ||
# interfere with applications in which it's used, we always run the | ||||
# prompt itself in a different thread (we can't start an event loop | ||||
# within an event loop). This new thread won't have any event loop | ||||
# running, and here we run our prompt-loop. | ||||
Thomas Kluyver
|
r22391 | self.preloop() | ||
try: | ||||
if intro is not None: | ||||
self.intro = intro | ||||
if self.intro: | ||||
Maor Kleinberger
|
r27108 | print(self.intro, file=self.stdout) | ||
Thomas Kluyver
|
r22391 | stop = None | ||
while not stop: | ||||
if self.cmdqueue: | ||||
line = self.cmdqueue.pop(0) | ||||
else: | ||||
self._ptcomp.ipy_completer.namespace = self.curframe_locals | ||||
self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals | ||||
Jonathan Slenders
|
r25342 | |||
Jonathan Slenders
|
r25511 | # Run the prompt in a different thread. | ||
Matthias Bussonnier
|
r27693 | if not _use_simple_prompt: | ||
try: | ||||
line = self.thread_executor.submit( | ||||
self.pt_app.prompt | ||||
).result() | ||||
except EOFError: | ||||
line = "EOF" | ||||
else: | ||||
line = input("ipdb> ") | ||||
Maor Kleinberger
|
r27108 | |||
Thomas Kluyver
|
r22391 | line = self.precmd(line) | ||
stop = self.onecmd(line) | ||||
stop = self.postcmd(stop, line) | ||||
self.postloop() | ||||
except Exception: | ||||
raise | ||||
Matthias Bussonnier
|
r22724 | |||
Iwan Briquemont
|
r26465 | def do_interact(self, arg): | ||
ipshell = embed.InteractiveShellEmbed( | ||||
config=self.shell.config, | ||||
banner1="*interactive*", | ||||
Matthias Bussonnier
|
r26466 | exit_msg="*exiting interactive console...*", | ||
Iwan Briquemont
|
r26465 | ) | ||
global_ns = self.curframe.f_globals | ||||
ipshell( | ||||
module=sys.modules.get(global_ns["__name__"], None), | ||||
local_ns=self.curframe_locals, | ||||
) | ||||
tillahoffmann
|
r22874 | |||
def set_trace(frame=None): | ||||
""" | ||||
Start debugging from `frame`. | ||||
If frame is not specified, debugging starts from caller's frame. | ||||
""" | ||||
TerminalPdb().set_trace(frame or sys._getframe().f_back) | ||||
Matthias Bussonnier
|
r22724 | |||
mbyt
|
r22858 | |||
if __name__ == '__main__': | ||||
import pdb | ||||
mbyt
|
r22879 | # IPython.core.debugger.Pdb.trace_dispatch shall not catch | ||
mbyt
|
r22901 | # bdb.BdbQuit. When started through __main__ and an exception | ||
mbyt
|
r22902 | # happened after hitting "c", this is needed in order to | ||
mbyt
|
r22879 | # be able to quit the debugging session (see #9950). | ||
old_trace_dispatch = pdb.Pdb.trace_dispatch | ||||
Matthias Bussonnier
|
r26485 | pdb.Pdb = TerminalPdb # type: ignore | ||
pdb.Pdb.trace_dispatch = old_trace_dispatch # type: ignore | ||||
mbyt
|
r22858 | pdb.main() | ||