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