##// END OF EJS Templates
Merge pull request #9487 from takluyver/ptk-ipdb...
Matthias Bussonnier -
r22396:97429bde merge
parent child Browse files
Show More
@@ -0,0 +1,73 b''
1 from IPython.core.debugger import Pdb
2
3 from IPython.core.completer import IPCompleter
4 from .ptutils import IPythonPTCompleter
5
6 from prompt_toolkit.token import Token
7 from prompt_toolkit.shortcuts import create_prompt_application
8 from prompt_toolkit.interface import CommandLineInterface
9 from prompt_toolkit.enums import EditingMode
10
11 class TerminalPdb(Pdb):
12 def __init__(self, *args, **kwargs):
13 Pdb.__init__(self, *args, **kwargs)
14 self._ptcomp = None
15 self.pt_init()
16
17 def pt_init(self):
18 def get_prompt_tokens(cli):
19 return [(Token.Prompt, self.prompt)]
20
21 if self._ptcomp is None:
22 compl = IPCompleter(shell=self.shell,
23 namespace={},
24 global_namespace={},
25 use_readline=False,
26 parent=self.shell,
27 )
28 self._ptcomp = IPythonPTCompleter(compl)
29
30 self._pt_app = create_prompt_application(
31 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
32 history=self.shell.debugger_history,
33 completer= self._ptcomp,
34 enable_history_search=True,
35 mouse_support=self.shell.mouse_support,
36 get_prompt_tokens=get_prompt_tokens
37 )
38 self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop)
39
40 def cmdloop(self, intro=None):
41 """Repeatedly issue a prompt, accept input, parse an initial prefix
42 off the received input, and dispatch to action methods, passing them
43 the remainder of the line as argument.
44
45 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
46 """
47 if not self.use_rawinput:
48 raise ValueError('Sorry ipdb does not support use_rawinput=False')
49
50 self.preloop()
51
52 try:
53 if intro is not None:
54 self.intro = intro
55 if self.intro:
56 self.stdout.write(str(self.intro)+"\n")
57 stop = None
58 while not stop:
59 if self.cmdqueue:
60 line = self.cmdqueue.pop(0)
61 else:
62 self._ptcomp.ipy_completer.namespace = self.curframe_locals
63 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
64 try:
65 line = self.pt_cli.run(reset_current_buffer=True).text
66 except EOFError:
67 line = 'EOF'
68 line = self.precmd(line)
69 stop = self.onecmd(line)
70 stop = self.postcmd(stop, line)
71 self.postloop()
72 except Exception:
73 raise
@@ -0,0 +1,62 b''
1 import unicodedata
2 from wcwidth import wcwidth
3
4 from IPython.utils.py3compat import PY3
5
6 from prompt_toolkit.completion import Completer, Completion
7 from prompt_toolkit.layout.lexers import Lexer
8 from prompt_toolkit.layout.lexers import PygmentsLexer
9
10 from pygments.lexers import Python3Lexer, BashLexer, PythonLexer
11
12 class IPythonPTCompleter(Completer):
13 """Adaptor to provide IPython completions to prompt_toolkit"""
14 def __init__(self, ipy_completer):
15 self.ipy_completer = ipy_completer
16
17 def get_completions(self, document, complete_event):
18 if not document.current_line.strip():
19 return
20
21 used, matches = self.ipy_completer.complete(
22 line_buffer=document.current_line,
23 cursor_pos=document.cursor_position_col
24 )
25 start_pos = -len(used)
26 for m in matches:
27 m = unicodedata.normalize('NFC', m)
28
29 # When the first character of the completion has a zero length,
30 # then it's probably a decomposed unicode character. E.g. caused by
31 # the "\dot" completion. Try to compose again with the previous
32 # character.
33 if wcwidth(m[0]) == 0:
34 if document.cursor_position + start_pos > 0:
35 char_before = document.text[document.cursor_position + start_pos - 1]
36 m = unicodedata.normalize('NFC', char_before + m)
37
38 # Yield the modified completion instead, if this worked.
39 if wcwidth(m[0:1]) == 1:
40 yield Completion(m, start_position=start_pos - 1)
41 continue
42
43 # TODO: Use Jedi to determine meta_text
44 # (Jedi currently has a bug that results in incorrect information.)
45 # meta_text = ''
46 # yield Completion(m, start_position=start_pos,
47 # display_meta=meta_text)
48 yield Completion(m, start_position=start_pos)
49
50 class IPythonPTLexer(Lexer):
51 """
52 Wrapper around PythonLexer and BashLexer.
53 """
54 def __init__(self):
55 self.python_lexer = PygmentsLexer(Python3Lexer if PY3 else PythonLexer)
56 self.shell_lexer = PygmentsLexer(BashLexer)
57
58 def lex_document(self, cli, document):
59 if document.text.startswith('!'):
60 return self.shell_lexer.lex_document(cli, document)
61 else:
62 return self.python_lexer.lex_document(cli, document)
@@ -652,6 +652,9 b' class IPCompleter(Completer):'
652 self.dict_key_matches,
652 self.dict_key_matches,
653 ]
653 ]
654
654
655 # This is set externally by InteractiveShell
656 self.custom_completers = None
657
655 def all_completions(self, text):
658 def all_completions(self, text):
656 """
659 """
657 Wrapper around the complete method for the benefit of emacs.
660 Wrapper around the complete method for the benefit of emacs.
@@ -1072,6 +1075,9 b' class IPCompleter(Completer):'
1072 return u'', []
1075 return u'', []
1073
1076
1074 def dispatch_custom_completer(self, text):
1077 def dispatch_custom_completer(self, text):
1078 if not self.custom_completers:
1079 return
1080
1075 line = self.line_buffer
1081 line = self.line_buffer
1076 if not line.strip():
1082 if not line.strip():
1077 return None
1083 return None
@@ -37,6 +37,7 b' from IPython.utils import coloransi, py3compat'
37 from IPython.core.excolors import exception_colors
37 from IPython.core.excolors import exception_colors
38 from IPython.testing.skipdoctest import skip_doctest
38 from IPython.testing.skipdoctest import skip_doctest
39
39
40
40 prompt = 'ipdb> '
41 prompt = 'ipdb> '
41
42
42 #We have to check this directly from sys.argv, config struct not yet available
43 #We have to check this directly from sys.argv, config struct not yet available
@@ -120,12 +121,6 b' class Tracer(object):'
120 sys.excepthook = functools.partial(BdbQuit_excepthook,
121 sys.excepthook = functools.partial(BdbQuit_excepthook,
121 excepthook=sys.excepthook)
122 excepthook=sys.excepthook)
122 def_colors = 'NoColor'
123 def_colors = 'NoColor'
123 try:
124 # Limited tab completion support
125 import readline
126 readline.parse_and_bind('tab: complete')
127 except ImportError:
128 pass
129 else:
124 else:
130 # In ipython, we use its custom exception handler mechanism
125 # In ipython, we use its custom exception handler mechanism
131 def_colors = ip.colors
126 def_colors = ip.colors
@@ -205,7 +200,7 b' class Pdb(OldPdb, object):'
205 except (TypeError, ValueError):
200 except (TypeError, ValueError):
206 raise ValueError("Context must be a positive integer")
201 raise ValueError("Context must be a positive integer")
207
202
208 OldPdb.__init__(self,completekey,stdin,stdout)
203 OldPdb.__init__(self, completekey, stdin, stdout)
209
204
210 # IPython changes...
205 # IPython changes...
211 self.shell = get_ipython()
206 self.shell = get_ipython()
@@ -245,31 +240,17 b' class Pdb(OldPdb, object):'
245 self.parser = PyColorize.Parser()
240 self.parser = PyColorize.Parser()
246
241
247 # Set the prompt - the default prompt is '(Pdb)'
242 # Set the prompt - the default prompt is '(Pdb)'
248 Colors = cst.active_colors
243 self.prompt = prompt
249 if color_scheme == 'NoColor':
250 self.prompt = prompt
251 else:
252 # The colour markers are wrapped by bytes 01 and 02 so that readline
253 # can calculate the width.
254 self.prompt = u'\x01%s\x02%s\x01%s\x02' % (Colors.prompt, prompt, Colors.Normal)
255
244
256 def set_colors(self, scheme):
245 def set_colors(self, scheme):
257 """Shorthand access to the color table scheme selector method."""
246 """Shorthand access to the color table scheme selector method."""
258 self.color_scheme_table.set_active_scheme(scheme)
247 self.color_scheme_table.set_active_scheme(scheme)
259
248
260 def interaction(self, frame, traceback):
249 def interaction(self, frame, traceback):
261 self.shell.set_completer_frame(frame)
250 try:
262 while True:
251 OldPdb.interaction(self, frame, traceback)
263 try:
252 except KeyboardInterrupt:
264 OldPdb.interaction(self, frame, traceback)
253 sys.stdout.write('\n' + self.shell.get_exception_only())
265 break
266 except KeyboardInterrupt:
267 self.shell.write('\n' + self.shell.get_exception_only())
268 break
269 finally:
270 # Pdb sets readline delimiters, so set them back to our own
271 if self.shell.readline is not None:
272 self.shell.readline.set_completer_delims(self.shell.readline_delims)
273
254
274 def parseline(self, line):
255 def parseline(self, line):
275 if line.startswith("!!"):
256 if line.startswith("!!"):
@@ -284,18 +265,15 b' class Pdb(OldPdb, object):'
284
265
285 def new_do_up(self, arg):
266 def new_do_up(self, arg):
286 OldPdb.do_up(self, arg)
267 OldPdb.do_up(self, arg)
287 self.shell.set_completer_frame(self.curframe)
288 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
268 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
289
269
290 def new_do_down(self, arg):
270 def new_do_down(self, arg):
291 OldPdb.do_down(self, arg)
271 OldPdb.do_down(self, arg)
292 self.shell.set_completer_frame(self.curframe)
293
272
294 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
273 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
295
274
296 def new_do_frame(self, arg):
275 def new_do_frame(self, arg):
297 OldPdb.do_frame(self, arg)
276 OldPdb.do_frame(self, arg)
298 self.shell.set_completer_frame(self.curframe)
299
277
300 def new_do_quit(self, arg):
278 def new_do_quit(self, arg):
301
279
@@ -312,9 +290,6 b' class Pdb(OldPdb, object):'
312 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
290 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
313 return self.do_quit(arg)
291 return self.do_quit(arg)
314
292
315 def postloop(self):
316 self.shell.set_completer_frame(None)
317
318 def print_stack_trace(self, context=None):
293 def print_stack_trace(self, context=None):
319 if context is None:
294 if context is None:
320 context = self.context
295 context = self.context
@@ -42,6 +42,7 b' from IPython.core.autocall import ExitAutocall'
42 from IPython.core.builtin_trap import BuiltinTrap
42 from IPython.core.builtin_trap import BuiltinTrap
43 from IPython.core.events import EventManager, available_events
43 from IPython.core.events import EventManager, available_events
44 from IPython.core.compilerop import CachingCompiler, check_linecache_ipython
44 from IPython.core.compilerop import CachingCompiler, check_linecache_ipython
45 from IPython.core.debugger import Pdb
45 from IPython.core.display_trap import DisplayTrap
46 from IPython.core.display_trap import DisplayTrap
46 from IPython.core.displayhook import DisplayHook
47 from IPython.core.displayhook import DisplayHook
47 from IPython.core.displaypub import DisplayPublisher
48 from IPython.core.displaypub import DisplayPublisher
@@ -1584,6 +1585,8 b' class InteractiveShell(SingletonConfigurable):'
1584 # Things related to exception handling and tracebacks (not debugging)
1585 # Things related to exception handling and tracebacks (not debugging)
1585 #-------------------------------------------------------------------------
1586 #-------------------------------------------------------------------------
1586
1587
1588 debugger_cls = Pdb
1589
1587 def init_traceback_handlers(self, custom_exceptions):
1590 def init_traceback_handlers(self, custom_exceptions):
1588 # Syntax error handler.
1591 # Syntax error handler.
1589 self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor')
1592 self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor')
@@ -1594,7 +1597,8 b' class InteractiveShell(SingletonConfigurable):'
1594 self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain',
1597 self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain',
1595 color_scheme='NoColor',
1598 color_scheme='NoColor',
1596 tb_offset = 1,
1599 tb_offset = 1,
1597 check_cache=check_linecache_ipython)
1600 check_cache=check_linecache_ipython,
1601 debugger_cls=self.debugger_cls)
1598
1602
1599 # The instance will store a pointer to the system-wide exception hook,
1603 # The instance will store a pointer to the system-wide exception hook,
1600 # so that runtime code (such as magics) can access it. This is because
1604 # so that runtime code (such as magics) can access it. This is because
@@ -809,7 +809,7 b' class VerboseTB(TBTools):'
809
809
810 def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None,
810 def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None,
811 tb_offset=0, long_header=False, include_vars=True,
811 tb_offset=0, long_header=False, include_vars=True,
812 check_cache=None):
812 check_cache=None, debugger_cls = None):
813 """Specify traceback offset, headers and color scheme.
813 """Specify traceback offset, headers and color scheme.
814
814
815 Define how many frames to drop from the tracebacks. Calling it with
815 Define how many frames to drop from the tracebacks. Calling it with
@@ -830,6 +830,8 b' class VerboseTB(TBTools):'
830 check_cache = linecache.checkcache
830 check_cache = linecache.checkcache
831 self.check_cache = check_cache
831 self.check_cache = check_cache
832
832
833 self.debugger_cls = debugger_cls or debugger.Pdb
834
833 def format_records(self, records, last_unique, recursion_repeat):
835 def format_records(self, records, last_unique, recursion_repeat):
834 """Format the stack frames of the traceback"""
836 """Format the stack frames of the traceback"""
835 frames = []
837 frames = []
@@ -1217,7 +1219,7 b' class VerboseTB(TBTools):'
1217
1219
1218 if force or self.call_pdb:
1220 if force or self.call_pdb:
1219 if self.pdb is None:
1221 if self.pdb is None:
1220 self.pdb = debugger.Pdb(
1222 self.pdb = self.debugger_cls(
1221 self.color_scheme_table.active_scheme_name)
1223 self.color_scheme_table.active_scheme_name)
1222 # the system displayhook may have changed, restore the original
1224 # the system displayhook may have changed, restore the original
1223 # for pdb
1225 # for pdb
@@ -1278,7 +1280,7 b' class FormattedTB(VerboseTB, ListTB):'
1278 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1280 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1279 ostream=None,
1281 ostream=None,
1280 tb_offset=0, long_header=False, include_vars=False,
1282 tb_offset=0, long_header=False, include_vars=False,
1281 check_cache=None):
1283 check_cache=None, debugger_cls=None):
1282
1284
1283 # NEVER change the order of this list. Put new modes at the end:
1285 # NEVER change the order of this list. Put new modes at the end:
1284 self.valid_modes = ['Plain', 'Context', 'Verbose']
1286 self.valid_modes = ['Plain', 'Context', 'Verbose']
@@ -1287,7 +1289,7 b' class FormattedTB(VerboseTB, ListTB):'
1287 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1289 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1288 ostream=ostream, tb_offset=tb_offset,
1290 ostream=ostream, tb_offset=tb_offset,
1289 long_header=long_header, include_vars=include_vars,
1291 long_header=long_header, include_vars=include_vars,
1290 check_cache=check_cache)
1292 check_cache=check_cache, debugger_cls=debugger_cls)
1291
1293
1292 # Different types of tracebacks are joined with different separators to
1294 # Different types of tracebacks are joined with different separators to
1293 # form a single string. They are taken from this dict
1295 # form a single string. They are taken from this dict
@@ -4,18 +4,15 b' from __future__ import print_function'
4 import os
4 import os
5 import sys
5 import sys
6 import signal
6 import signal
7 import unicodedata
8 from warnings import warn
7 from warnings import warn
9 from wcwidth import wcwidth
10
8
11 from IPython.core.error import TryNext
9 from IPython.core.error import TryNext
12 from IPython.core.interactiveshell import InteractiveShell
10 from IPython.core.interactiveshell import InteractiveShell
13 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
11 from IPython.utils.py3compat import cast_unicode_py2, input
14 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
15 from IPython.utils.process import abbrev_cwd
13 from IPython.utils.process import abbrev_cwd
16 from traitlets import Bool, Unicode, Dict, Integer, observe
14 from traitlets import Bool, Unicode, Dict, Integer, observe
17
15
18 from prompt_toolkit.completion import Completer, Completion
19 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
20 from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode, IsDone
17 from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode, IsDone
21 from prompt_toolkit.history import InMemoryHistory
18 from prompt_toolkit.history import InMemoryHistory
@@ -23,70 +20,16 b' from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop'
23 from prompt_toolkit.interface import CommandLineInterface
20 from prompt_toolkit.interface import CommandLineInterface
24 from prompt_toolkit.key_binding.manager import KeyBindingManager
21 from prompt_toolkit.key_binding.manager import KeyBindingManager
25 from prompt_toolkit.keys import Keys
22 from prompt_toolkit.keys import Keys
26 from prompt_toolkit.layout.lexers import Lexer
27 from prompt_toolkit.layout.lexers import PygmentsLexer
28 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
29 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
24 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
30
25
31 from pygments.styles import get_style_by_name, get_all_styles
26 from pygments.styles import get_style_by_name, get_all_styles
32 from pygments.lexers import Python3Lexer, BashLexer, PythonLexer
33 from pygments.token import Token
27 from pygments.token import Token
34
28
29 from .debugger import TerminalPdb
35 from .pt_inputhooks import get_inputhook_func
30 from .pt_inputhooks import get_inputhook_func
36 from .interactiveshell import get_default_editor, TerminalMagics
31 from .interactiveshell import get_default_editor, TerminalMagics
37
32 from .ptutils import IPythonPTCompleter, IPythonPTLexer
38
39 class IPythonPTCompleter(Completer):
40 """Adaptor to provide IPython completions to prompt_toolkit"""
41 def __init__(self, ipy_completer):
42 self.ipy_completer = ipy_completer
43
44 def get_completions(self, document, complete_event):
45 if not document.current_line.strip():
46 return
47
48 used, matches = self.ipy_completer.complete(
49 line_buffer=document.current_line,
50 cursor_pos=document.cursor_position_col
51 )
52 start_pos = -len(used)
53 for m in matches:
54 m = unicodedata.normalize('NFC', m)
55
56 # When the first character of the completion has a zero length,
57 # then it's probably a decomposed unicode character. E.g. caused by
58 # the "\dot" completion. Try to compose again with the previous
59 # character.
60 if wcwidth(m[0]) == 0:
61 if document.cursor_position + start_pos > 0:
62 char_before = document.text[document.cursor_position + start_pos - 1]
63 m = unicodedata.normalize('NFC', char_before + m)
64
65 # Yield the modified completion instead, if this worked.
66 if wcwidth(m[0:1]) == 1:
67 yield Completion(m, start_position=start_pos - 1)
68 continue
69
70 # TODO: Use Jedi to determine meta_text
71 # (Jedi currently has a bug that results in incorrect information.)
72 # meta_text = ''
73 # yield Completion(m, start_position=start_pos,
74 # display_meta=meta_text)
75 yield Completion(m, start_position=start_pos)
76
77 class IPythonPTLexer(Lexer):
78 """
79 Wrapper around PythonLexer and BashLexer.
80 """
81 def __init__(self):
82 self.python_lexer = PygmentsLexer(Python3Lexer if PY3 else PythonLexer)
83 self.shell_lexer = PygmentsLexer(BashLexer)
84
85 def lex_document(self, cli, document):
86 if document.text.startswith('!'):
87 return self.shell_lexer.lex_document(cli, document)
88 else:
89 return self.python_lexer.lex_document(cli, document)
90
33
91
34
92 class TerminalInteractiveShell(InteractiveShell):
35 class TerminalInteractiveShell(InteractiveShell):
@@ -100,6 +43,8 b' class TerminalInteractiveShell(InteractiveShell):'
100 self._update_layout()
43 self._update_layout()
101
44
102 pt_cli = None
45 pt_cli = None
46 debugger_history = None
47 debugger_cls = TerminalPdb
103
48
104 autoedit_syntax = Bool(False,
49 autoedit_syntax = Bool(False,
105 help="auto editing of files with syntax errors.",
50 help="auto editing of files with syntax errors.",
@@ -362,6 +307,8 b' class TerminalInteractiveShell(InteractiveShell):'
362 self.init_term_title()
307 self.init_term_title()
363 self.keep_running = True
308 self.keep_running = True
364
309
310 self.debugger_history = InMemoryHistory()
311
365 def ask_exit(self):
312 def ask_exit(self):
366 self.keep_running = False
313 self.keep_running = False
367
314
General Comments 0
You need to be logged in to leave comments. Login now