##// END OF EJS Templates
Start embedded IPython shell for ipdb interact, #12913
Iwan Briquemont -
Show More
@@ -0,0 +1,4 b''
1 IPython shell for ipdb interact
2 -------------------------------
3
4 The ipdb ``interact`` starts an IPython shell instead of Python's built-in ``code.interact()``.
@@ -1,153 +1,166 b''
1 import asyncio
1 import asyncio
2 import sys
2 import sys
3 import threading
3 import threading
4
4
5 from IPython.core.debugger import Pdb
5 from IPython.core.debugger import Pdb
6
6
7 from IPython.core.completer import IPCompleter
7 from IPython.core.completer import IPCompleter
8 from .ptutils import IPythonPTCompleter
8 from .ptutils import IPythonPTCompleter
9 from .shortcuts import create_ipython_shortcuts
9 from .shortcuts import create_ipython_shortcuts
10 from . import embed
10
11
11 from pygments.token import Token
12 from pygments.token import Token
12 from prompt_toolkit.shortcuts.prompt import PromptSession
13 from prompt_toolkit.shortcuts.prompt import PromptSession
13 from prompt_toolkit.enums import EditingMode
14 from prompt_toolkit.enums import EditingMode
14 from prompt_toolkit.formatted_text import PygmentsTokens
15 from prompt_toolkit.formatted_text import PygmentsTokens
15
16
16 from prompt_toolkit import __version__ as ptk_version
17 from prompt_toolkit import __version__ as ptk_version
17 PTK3 = ptk_version.startswith('3.')
18 PTK3 = ptk_version.startswith('3.')
18
19
19
20
20 class TerminalPdb(Pdb):
21 class TerminalPdb(Pdb):
21 """Standalone IPython debugger."""
22 """Standalone IPython debugger."""
22
23
23 def __init__(self, *args, pt_session_options=None, **kwargs):
24 def __init__(self, *args, pt_session_options=None, **kwargs):
24 Pdb.__init__(self, *args, **kwargs)
25 Pdb.__init__(self, *args, **kwargs)
25 self._ptcomp = None
26 self._ptcomp = None
26 self.pt_init(pt_session_options)
27 self.pt_init(pt_session_options)
27
28
28 def pt_init(self, pt_session_options=None):
29 def pt_init(self, pt_session_options=None):
29 """Initialize the prompt session and the prompt loop
30 """Initialize the prompt session and the prompt loop
30 and store them in self.pt_app and self.pt_loop.
31 and store them in self.pt_app and self.pt_loop.
31
32
32 Additional keyword arguments for the PromptSession class
33 Additional keyword arguments for the PromptSession class
33 can be specified in pt_session_options.
34 can be specified in pt_session_options.
34 """
35 """
35 if pt_session_options is None:
36 if pt_session_options is None:
36 pt_session_options = {}
37 pt_session_options = {}
37
38
38 def get_prompt_tokens():
39 def get_prompt_tokens():
39 return [(Token.Prompt, self.prompt)]
40 return [(Token.Prompt, self.prompt)]
40
41
41 if self._ptcomp is None:
42 if self._ptcomp is None:
42 compl = IPCompleter(
43 compl = IPCompleter(
43 shell=self.shell, namespace={}, global_namespace={}, parent=self.shell
44 shell=self.shell, namespace={}, global_namespace={}, parent=self.shell
44 )
45 )
45 # add a completer for all the do_ methods
46 # add a completer for all the do_ methods
46 methods_names = [m[3:] for m in dir(self) if m.startswith("do_")]
47 methods_names = [m[3:] for m in dir(self) if m.startswith("do_")]
47
48
48 def gen_comp(self, text):
49 def gen_comp(self, text):
49 return [m for m in methods_names if m.startswith(text)]
50 return [m for m in methods_names if m.startswith(text)]
50 import types
51 import types
51 newcomp = types.MethodType(gen_comp, compl)
52 newcomp = types.MethodType(gen_comp, compl)
52 compl.custom_matchers.insert(0, newcomp)
53 compl.custom_matchers.insert(0, newcomp)
53 # end add completer.
54 # end add completer.
54
55
55 self._ptcomp = IPythonPTCompleter(compl)
56 self._ptcomp = IPythonPTCompleter(compl)
56
57
57 options = dict(
58 options = dict(
58 message=(lambda: PygmentsTokens(get_prompt_tokens())),
59 message=(lambda: PygmentsTokens(get_prompt_tokens())),
59 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
60 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
60 key_bindings=create_ipython_shortcuts(self.shell),
61 key_bindings=create_ipython_shortcuts(self.shell),
61 history=self.shell.debugger_history,
62 history=self.shell.debugger_history,
62 completer=self._ptcomp,
63 completer=self._ptcomp,
63 enable_history_search=True,
64 enable_history_search=True,
64 mouse_support=self.shell.mouse_support,
65 mouse_support=self.shell.mouse_support,
65 complete_style=self.shell.pt_complete_style,
66 complete_style=self.shell.pt_complete_style,
66 style=self.shell.style,
67 style=self.shell.style,
67 color_depth=self.shell.color_depth,
68 color_depth=self.shell.color_depth,
68 )
69 )
69
70
70 if not PTK3:
71 if not PTK3:
71 options['inputhook'] = self.shell.inputhook
72 options['inputhook'] = self.shell.inputhook
72 options.update(pt_session_options)
73 options.update(pt_session_options)
73 self.pt_loop = asyncio.new_event_loop()
74 self.pt_loop = asyncio.new_event_loop()
74 self.pt_app = PromptSession(**options)
75 self.pt_app = PromptSession(**options)
75
76
76 def cmdloop(self, intro=None):
77 def cmdloop(self, intro=None):
77 """Repeatedly issue a prompt, accept input, parse an initial prefix
78 """Repeatedly issue a prompt, accept input, parse an initial prefix
78 off the received input, and dispatch to action methods, passing them
79 off the received input, and dispatch to action methods, passing them
79 the remainder of the line as argument.
80 the remainder of the line as argument.
80
81
81 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
82 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
82 """
83 """
83 if not self.use_rawinput:
84 if not self.use_rawinput:
84 raise ValueError('Sorry ipdb does not support use_rawinput=False')
85 raise ValueError('Sorry ipdb does not support use_rawinput=False')
85
86
86 # In order to make sure that prompt, which uses asyncio doesn't
87 # In order to make sure that prompt, which uses asyncio doesn't
87 # interfere with applications in which it's used, we always run the
88 # interfere with applications in which it's used, we always run the
88 # prompt itself in a different thread (we can't start an event loop
89 # prompt itself in a different thread (we can't start an event loop
89 # within an event loop). This new thread won't have any event loop
90 # within an event loop). This new thread won't have any event loop
90 # running, and here we run our prompt-loop.
91 # running, and here we run our prompt-loop.
91
92
92 self.preloop()
93 self.preloop()
93
94
94 try:
95 try:
95 if intro is not None:
96 if intro is not None:
96 self.intro = intro
97 self.intro = intro
97 if self.intro:
98 if self.intro:
98 self.stdout.write(str(self.intro)+"\n")
99 self.stdout.write(str(self.intro)+"\n")
99 stop = None
100 stop = None
100 while not stop:
101 while not stop:
101 if self.cmdqueue:
102 if self.cmdqueue:
102 line = self.cmdqueue.pop(0)
103 line = self.cmdqueue.pop(0)
103 else:
104 else:
104 self._ptcomp.ipy_completer.namespace = self.curframe_locals
105 self._ptcomp.ipy_completer.namespace = self.curframe_locals
105 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
106 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
106
107
107 # Run the prompt in a different thread.
108 # Run the prompt in a different thread.
108 line = ''
109 line = ''
109 keyboard_interrupt = False
110 keyboard_interrupt = False
110
111
111 def in_thread():
112 def in_thread():
112 nonlocal line, keyboard_interrupt
113 nonlocal line, keyboard_interrupt
113 try:
114 try:
114 line = self.pt_app.prompt()
115 line = self.pt_app.prompt()
115 except EOFError:
116 except EOFError:
116 line = 'EOF'
117 line = 'EOF'
117 except KeyboardInterrupt:
118 except KeyboardInterrupt:
118 keyboard_interrupt = True
119 keyboard_interrupt = True
119
120
120 th = threading.Thread(target=in_thread)
121 th = threading.Thread(target=in_thread)
121 th.start()
122 th.start()
122 th.join()
123 th.join()
123
124
124 if keyboard_interrupt:
125 if keyboard_interrupt:
125 raise KeyboardInterrupt
126 raise KeyboardInterrupt
126
127
127 line = self.precmd(line)
128 line = self.precmd(line)
128 stop = self.onecmd(line)
129 stop = self.onecmd(line)
129 stop = self.postcmd(stop, line)
130 stop = self.postcmd(stop, line)
130 self.postloop()
131 self.postloop()
131 except Exception:
132 except Exception:
132 raise
133 raise
133
134
135 def do_interact(self, arg):
136 ipshell = embed.InteractiveShellEmbed(
137 config=self.shell.config,
138 banner1="*interactive*",
139 exit_msg="now exiting InteractiveConsole...",
140 )
141 global_ns = self.curframe.f_globals
142 ipshell(
143 module=sys.modules.get(global_ns["__name__"], None),
144 local_ns=self.curframe_locals,
145 )
146
134
147
135 def set_trace(frame=None):
148 def set_trace(frame=None):
136 """
149 """
137 Start debugging from `frame`.
150 Start debugging from `frame`.
138
151
139 If frame is not specified, debugging starts from caller's frame.
152 If frame is not specified, debugging starts from caller's frame.
140 """
153 """
141 TerminalPdb().set_trace(frame or sys._getframe().f_back)
154 TerminalPdb().set_trace(frame or sys._getframe().f_back)
142
155
143
156
144 if __name__ == '__main__':
157 if __name__ == '__main__':
145 import pdb
158 import pdb
146 # IPython.core.debugger.Pdb.trace_dispatch shall not catch
159 # IPython.core.debugger.Pdb.trace_dispatch shall not catch
147 # bdb.BdbQuit. When started through __main__ and an exception
160 # bdb.BdbQuit. When started through __main__ and an exception
148 # happened after hitting "c", this is needed in order to
161 # happened after hitting "c", this is needed in order to
149 # be able to quit the debugging session (see #9950).
162 # be able to quit the debugging session (see #9950).
150 old_trace_dispatch = pdb.Pdb.trace_dispatch
163 old_trace_dispatch = pdb.Pdb.trace_dispatch
151 pdb.Pdb = TerminalPdb
164 pdb.Pdb = TerminalPdb
152 pdb.Pdb.trace_dispatch = old_trace_dispatch
165 pdb.Pdb.trace_dispatch = old_trace_dispatch
153 pdb.main()
166 pdb.main()
General Comments 0
You need to be logged in to leave comments. Login now