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