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