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