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