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