##// END OF EJS Templates
Fix `help=` is a Constructor arg, not a metadata.
Matthias Bussonnier -
Show More
@@ -1,713 +1,715 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Prefiltering components.
3 Prefiltering components.
4
4
5 Prefilters transform user input before it is exec'd by Python. These
5 Prefilters transform user input before it is exec'd by Python. These
6 transforms are used to implement additional syntax such as !ls and %magic.
6 transforms are used to implement additional syntax such as !ls and %magic.
7
7
8 Authors:
8 Authors:
9
9
10 * Brian Granger
10 * Brian Granger
11 * Fernando Perez
11 * Fernando Perez
12 * Dan Milstein
12 * Dan Milstein
13 * Ville Vainio
13 * Ville Vainio
14 """
14 """
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Copyright (C) 2008-2011 The IPython Development Team
17 # Copyright (C) 2008-2011 The IPython Development Team
18 #
18 #
19 # Distributed under the terms of the BSD License. The full license is in
19 # Distributed under the terms of the BSD License. The full license is in
20 # the file COPYING, distributed as part of this software.
20 # the file COPYING, distributed as part of this software.
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Imports
24 # Imports
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27 from keyword import iskeyword
27 from keyword import iskeyword
28 import re
28 import re
29
29
30 from IPython.core.autocall import IPyAutocall
30 from IPython.core.autocall import IPyAutocall
31 from traitlets.config.configurable import Configurable
31 from traitlets.config.configurable import Configurable
32 from IPython.core.inputsplitter import (
32 from IPython.core.inputsplitter import (
33 ESC_MAGIC,
33 ESC_MAGIC,
34 ESC_QUOTE,
34 ESC_QUOTE,
35 ESC_QUOTE2,
35 ESC_QUOTE2,
36 ESC_PAREN,
36 ESC_PAREN,
37 )
37 )
38 from IPython.core.macro import Macro
38 from IPython.core.macro import Macro
39 from IPython.core.splitinput import LineInfo
39 from IPython.core.splitinput import LineInfo
40
40
41 from traitlets import (
41 from traitlets import (
42 List, Integer, Unicode, CBool, Bool, Instance, CRegExp
42 List, Integer, Unicode, CBool, Bool, Instance, CRegExp
43 )
43 )
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # Global utilities, errors and constants
46 # Global utilities, errors and constants
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49
49
50 class PrefilterError(Exception):
50 class PrefilterError(Exception):
51 pass
51 pass
52
52
53
53
54 # RegExp to identify potential function names
54 # RegExp to identify potential function names
55 re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$')
55 re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$')
56
56
57 # RegExp to exclude strings with this start from autocalling. In
57 # RegExp to exclude strings with this start from autocalling. In
58 # particular, all binary operators should be excluded, so that if foo is
58 # particular, all binary operators should be excluded, so that if foo is
59 # callable, foo OP bar doesn't become foo(OP bar), which is invalid. The
59 # callable, foo OP bar doesn't become foo(OP bar), which is invalid. The
60 # characters '!=()' don't need to be checked for, as the checkPythonChars
60 # characters '!=()' don't need to be checked for, as the checkPythonChars
61 # routine explicitely does so, to catch direct calls and rebindings of
61 # routine explicitely does so, to catch direct calls and rebindings of
62 # existing names.
62 # existing names.
63
63
64 # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
64 # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
65 # it affects the rest of the group in square brackets.
65 # it affects the rest of the group in square brackets.
66 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
66 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
67 r'|^is |^not |^in |^and |^or ')
67 r'|^is |^not |^in |^and |^or ')
68
68
69 # try to catch also methods for stuff in lists/tuples/dicts: off
69 # try to catch also methods for stuff in lists/tuples/dicts: off
70 # (experimental). For this to work, the line_split regexp would need
70 # (experimental). For this to work, the line_split regexp would need
71 # to be modified so it wouldn't break things at '['. That line is
71 # to be modified so it wouldn't break things at '['. That line is
72 # nasty enough that I shouldn't change it until I can test it _well_.
72 # nasty enough that I shouldn't change it until I can test it _well_.
73 #self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$')
73 #self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$')
74
74
75
75
76 # Handler Check Utilities
76 # Handler Check Utilities
77 def is_shadowed(identifier, ip):
77 def is_shadowed(identifier, ip):
78 """Is the given identifier defined in one of the namespaces which shadow
78 """Is the given identifier defined in one of the namespaces which shadow
79 the alias and magic namespaces? Note that an identifier is different
79 the alias and magic namespaces? Note that an identifier is different
80 than ifun, because it can not contain a '.' character."""
80 than ifun, because it can not contain a '.' character."""
81 # This is much safer than calling ofind, which can change state
81 # This is much safer than calling ofind, which can change state
82 return (identifier in ip.user_ns \
82 return (identifier in ip.user_ns \
83 or identifier in ip.user_global_ns \
83 or identifier in ip.user_global_ns \
84 or identifier in ip.ns_table['builtin']\
84 or identifier in ip.ns_table['builtin']\
85 or iskeyword(identifier))
85 or iskeyword(identifier))
86
86
87
87
88 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
89 # Main Prefilter manager
89 # Main Prefilter manager
90 #-----------------------------------------------------------------------------
90 #-----------------------------------------------------------------------------
91
91
92
92
93 class PrefilterManager(Configurable):
93 class PrefilterManager(Configurable):
94 """Main prefilter component.
94 """Main prefilter component.
95
95
96 The IPython prefilter is run on all user input before it is run. The
96 The IPython prefilter is run on all user input before it is run. The
97 prefilter consumes lines of input and produces transformed lines of
97 prefilter consumes lines of input and produces transformed lines of
98 input.
98 input.
99
99
100 The iplementation consists of two phases:
100 The iplementation consists of two phases:
101
101
102 1. Transformers
102 1. Transformers
103 2. Checkers and handlers
103 2. Checkers and handlers
104
104
105 Over time, we plan on deprecating the checkers and handlers and doing
105 Over time, we plan on deprecating the checkers and handlers and doing
106 everything in the transformers.
106 everything in the transformers.
107
107
108 The transformers are instances of :class:`PrefilterTransformer` and have
108 The transformers are instances of :class:`PrefilterTransformer` and have
109 a single method :meth:`transform` that takes a line and returns a
109 a single method :meth:`transform` that takes a line and returns a
110 transformed line. The transformation can be accomplished using any
110 transformed line. The transformation can be accomplished using any
111 tool, but our current ones use regular expressions for speed.
111 tool, but our current ones use regular expressions for speed.
112
112
113 After all the transformers have been run, the line is fed to the checkers,
113 After all the transformers have been run, the line is fed to the checkers,
114 which are instances of :class:`PrefilterChecker`. The line is passed to
114 which are instances of :class:`PrefilterChecker`. The line is passed to
115 the :meth:`check` method, which either returns `None` or a
115 the :meth:`check` method, which either returns `None` or a
116 :class:`PrefilterHandler` instance. If `None` is returned, the other
116 :class:`PrefilterHandler` instance. If `None` is returned, the other
117 checkers are tried. If an :class:`PrefilterHandler` instance is returned,
117 checkers are tried. If an :class:`PrefilterHandler` instance is returned,
118 the line is passed to the :meth:`handle` method of the returned
118 the line is passed to the :meth:`handle` method of the returned
119 handler and no further checkers are tried.
119 handler and no further checkers are tried.
120
120
121 Both transformers and checkers have a `priority` attribute, that determines
121 Both transformers and checkers have a `priority` attribute, that determines
122 the order in which they are called. Smaller priorities are tried first.
122 the order in which they are called. Smaller priorities are tried first.
123
123
124 Both transformers and checkers also have `enabled` attribute, which is
124 Both transformers and checkers also have `enabled` attribute, which is
125 a boolean that determines if the instance is used.
125 a boolean that determines if the instance is used.
126
126
127 Users or developers can change the priority or enabled attribute of
127 Users or developers can change the priority or enabled attribute of
128 transformers or checkers, but they must call the :meth:`sort_checkers`
128 transformers or checkers, but they must call the :meth:`sort_checkers`
129 or :meth:`sort_transformers` method after changing the priority.
129 or :meth:`sort_transformers` method after changing the priority.
130 """
130 """
131
131
132 multi_line_specials = CBool(True).tag(config=True)
132 multi_line_specials = CBool(True).tag(config=True)
133 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
133 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
134
134
135 def __init__(self, shell=None, **kwargs):
135 def __init__(self, shell=None, **kwargs):
136 super(PrefilterManager, self).__init__(shell=shell, **kwargs)
136 super(PrefilterManager, self).__init__(shell=shell, **kwargs)
137 self.shell = shell
137 self.shell = shell
138 self.init_transformers()
138 self.init_transformers()
139 self.init_handlers()
139 self.init_handlers()
140 self.init_checkers()
140 self.init_checkers()
141
141
142 #-------------------------------------------------------------------------
142 #-------------------------------------------------------------------------
143 # API for managing transformers
143 # API for managing transformers
144 #-------------------------------------------------------------------------
144 #-------------------------------------------------------------------------
145
145
146 def init_transformers(self):
146 def init_transformers(self):
147 """Create the default transformers."""
147 """Create the default transformers."""
148 self._transformers = []
148 self._transformers = []
149 for transformer_cls in _default_transformers:
149 for transformer_cls in _default_transformers:
150 transformer_cls(
150 transformer_cls(
151 shell=self.shell, prefilter_manager=self, parent=self
151 shell=self.shell, prefilter_manager=self, parent=self
152 )
152 )
153
153
154 def sort_transformers(self):
154 def sort_transformers(self):
155 """Sort the transformers by priority.
155 """Sort the transformers by priority.
156
156
157 This must be called after the priority of a transformer is changed.
157 This must be called after the priority of a transformer is changed.
158 The :meth:`register_transformer` method calls this automatically.
158 The :meth:`register_transformer` method calls this automatically.
159 """
159 """
160 self._transformers.sort(key=lambda x: x.priority)
160 self._transformers.sort(key=lambda x: x.priority)
161
161
162 @property
162 @property
163 def transformers(self):
163 def transformers(self):
164 """Return a list of checkers, sorted by priority."""
164 """Return a list of checkers, sorted by priority."""
165 return self._transformers
165 return self._transformers
166
166
167 def register_transformer(self, transformer):
167 def register_transformer(self, transformer):
168 """Register a transformer instance."""
168 """Register a transformer instance."""
169 if transformer not in self._transformers:
169 if transformer not in self._transformers:
170 self._transformers.append(transformer)
170 self._transformers.append(transformer)
171 self.sort_transformers()
171 self.sort_transformers()
172
172
173 def unregister_transformer(self, transformer):
173 def unregister_transformer(self, transformer):
174 """Unregister a transformer instance."""
174 """Unregister a transformer instance."""
175 if transformer in self._transformers:
175 if transformer in self._transformers:
176 self._transformers.remove(transformer)
176 self._transformers.remove(transformer)
177
177
178 #-------------------------------------------------------------------------
178 #-------------------------------------------------------------------------
179 # API for managing checkers
179 # API for managing checkers
180 #-------------------------------------------------------------------------
180 #-------------------------------------------------------------------------
181
181
182 def init_checkers(self):
182 def init_checkers(self):
183 """Create the default checkers."""
183 """Create the default checkers."""
184 self._checkers = []
184 self._checkers = []
185 for checker in _default_checkers:
185 for checker in _default_checkers:
186 checker(
186 checker(
187 shell=self.shell, prefilter_manager=self, parent=self
187 shell=self.shell, prefilter_manager=self, parent=self
188 )
188 )
189
189
190 def sort_checkers(self):
190 def sort_checkers(self):
191 """Sort the checkers by priority.
191 """Sort the checkers by priority.
192
192
193 This must be called after the priority of a checker is changed.
193 This must be called after the priority of a checker is changed.
194 The :meth:`register_checker` method calls this automatically.
194 The :meth:`register_checker` method calls this automatically.
195 """
195 """
196 self._checkers.sort(key=lambda x: x.priority)
196 self._checkers.sort(key=lambda x: x.priority)
197
197
198 @property
198 @property
199 def checkers(self):
199 def checkers(self):
200 """Return a list of checkers, sorted by priority."""
200 """Return a list of checkers, sorted by priority."""
201 return self._checkers
201 return self._checkers
202
202
203 def register_checker(self, checker):
203 def register_checker(self, checker):
204 """Register a checker instance."""
204 """Register a checker instance."""
205 if checker not in self._checkers:
205 if checker not in self._checkers:
206 self._checkers.append(checker)
206 self._checkers.append(checker)
207 self.sort_checkers()
207 self.sort_checkers()
208
208
209 def unregister_checker(self, checker):
209 def unregister_checker(self, checker):
210 """Unregister a checker instance."""
210 """Unregister a checker instance."""
211 if checker in self._checkers:
211 if checker in self._checkers:
212 self._checkers.remove(checker)
212 self._checkers.remove(checker)
213
213
214 #-------------------------------------------------------------------------
214 #-------------------------------------------------------------------------
215 # API for managing handlers
215 # API for managing handlers
216 #-------------------------------------------------------------------------
216 #-------------------------------------------------------------------------
217
217
218 def init_handlers(self):
218 def init_handlers(self):
219 """Create the default handlers."""
219 """Create the default handlers."""
220 self._handlers = {}
220 self._handlers = {}
221 self._esc_handlers = {}
221 self._esc_handlers = {}
222 for handler in _default_handlers:
222 for handler in _default_handlers:
223 handler(
223 handler(
224 shell=self.shell, prefilter_manager=self, parent=self
224 shell=self.shell, prefilter_manager=self, parent=self
225 )
225 )
226
226
227 @property
227 @property
228 def handlers(self):
228 def handlers(self):
229 """Return a dict of all the handlers."""
229 """Return a dict of all the handlers."""
230 return self._handlers
230 return self._handlers
231
231
232 def register_handler(self, name, handler, esc_strings):
232 def register_handler(self, name, handler, esc_strings):
233 """Register a handler instance by name with esc_strings."""
233 """Register a handler instance by name with esc_strings."""
234 self._handlers[name] = handler
234 self._handlers[name] = handler
235 for esc_str in esc_strings:
235 for esc_str in esc_strings:
236 self._esc_handlers[esc_str] = handler
236 self._esc_handlers[esc_str] = handler
237
237
238 def unregister_handler(self, name, handler, esc_strings):
238 def unregister_handler(self, name, handler, esc_strings):
239 """Unregister a handler instance by name with esc_strings."""
239 """Unregister a handler instance by name with esc_strings."""
240 try:
240 try:
241 del self._handlers[name]
241 del self._handlers[name]
242 except KeyError:
242 except KeyError:
243 pass
243 pass
244 for esc_str in esc_strings:
244 for esc_str in esc_strings:
245 h = self._esc_handlers.get(esc_str)
245 h = self._esc_handlers.get(esc_str)
246 if h is handler:
246 if h is handler:
247 del self._esc_handlers[esc_str]
247 del self._esc_handlers[esc_str]
248
248
249 def get_handler_by_name(self, name):
249 def get_handler_by_name(self, name):
250 """Get a handler by its name."""
250 """Get a handler by its name."""
251 return self._handlers.get(name)
251 return self._handlers.get(name)
252
252
253 def get_handler_by_esc(self, esc_str):
253 def get_handler_by_esc(self, esc_str):
254 """Get a handler by its escape string."""
254 """Get a handler by its escape string."""
255 return self._esc_handlers.get(esc_str)
255 return self._esc_handlers.get(esc_str)
256
256
257 #-------------------------------------------------------------------------
257 #-------------------------------------------------------------------------
258 # Main prefiltering API
258 # Main prefiltering API
259 #-------------------------------------------------------------------------
259 #-------------------------------------------------------------------------
260
260
261 def prefilter_line_info(self, line_info):
261 def prefilter_line_info(self, line_info):
262 """Prefilter a line that has been converted to a LineInfo object.
262 """Prefilter a line that has been converted to a LineInfo object.
263
263
264 This implements the checker/handler part of the prefilter pipe.
264 This implements the checker/handler part of the prefilter pipe.
265 """
265 """
266 # print "prefilter_line_info: ", line_info
266 # print "prefilter_line_info: ", line_info
267 handler = self.find_handler(line_info)
267 handler = self.find_handler(line_info)
268 return handler.handle(line_info)
268 return handler.handle(line_info)
269
269
270 def find_handler(self, line_info):
270 def find_handler(self, line_info):
271 """Find a handler for the line_info by trying checkers."""
271 """Find a handler for the line_info by trying checkers."""
272 for checker in self.checkers:
272 for checker in self.checkers:
273 if checker.enabled:
273 if checker.enabled:
274 handler = checker.check(line_info)
274 handler = checker.check(line_info)
275 if handler:
275 if handler:
276 return handler
276 return handler
277 return self.get_handler_by_name('normal')
277 return self.get_handler_by_name('normal')
278
278
279 def transform_line(self, line, continue_prompt):
279 def transform_line(self, line, continue_prompt):
280 """Calls the enabled transformers in order of increasing priority."""
280 """Calls the enabled transformers in order of increasing priority."""
281 for transformer in self.transformers:
281 for transformer in self.transformers:
282 if transformer.enabled:
282 if transformer.enabled:
283 line = transformer.transform(line, continue_prompt)
283 line = transformer.transform(line, continue_prompt)
284 return line
284 return line
285
285
286 def prefilter_line(self, line, continue_prompt=False):
286 def prefilter_line(self, line, continue_prompt=False):
287 """Prefilter a single input line as text.
287 """Prefilter a single input line as text.
288
288
289 This method prefilters a single line of text by calling the
289 This method prefilters a single line of text by calling the
290 transformers and then the checkers/handlers.
290 transformers and then the checkers/handlers.
291 """
291 """
292
292
293 # print "prefilter_line: ", line, continue_prompt
293 # print "prefilter_line: ", line, continue_prompt
294 # All handlers *must* return a value, even if it's blank ('').
294 # All handlers *must* return a value, even if it's blank ('').
295
295
296 # save the line away in case we crash, so the post-mortem handler can
296 # save the line away in case we crash, so the post-mortem handler can
297 # record it
297 # record it
298 self.shell._last_input_line = line
298 self.shell._last_input_line = line
299
299
300 if not line:
300 if not line:
301 # Return immediately on purely empty lines, so that if the user
301 # Return immediately on purely empty lines, so that if the user
302 # previously typed some whitespace that started a continuation
302 # previously typed some whitespace that started a continuation
303 # prompt, he can break out of that loop with just an empty line.
303 # prompt, he can break out of that loop with just an empty line.
304 # This is how the default python prompt works.
304 # This is how the default python prompt works.
305 return ''
305 return ''
306
306
307 # At this point, we invoke our transformers.
307 # At this point, we invoke our transformers.
308 if not continue_prompt or (continue_prompt and self.multi_line_specials):
308 if not continue_prompt or (continue_prompt and self.multi_line_specials):
309 line = self.transform_line(line, continue_prompt)
309 line = self.transform_line(line, continue_prompt)
310
310
311 # Now we compute line_info for the checkers and handlers
311 # Now we compute line_info for the checkers and handlers
312 line_info = LineInfo(line, continue_prompt)
312 line_info = LineInfo(line, continue_prompt)
313
313
314 # the input history needs to track even empty lines
314 # the input history needs to track even empty lines
315 stripped = line.strip()
315 stripped = line.strip()
316
316
317 normal_handler = self.get_handler_by_name('normal')
317 normal_handler = self.get_handler_by_name('normal')
318 if not stripped:
318 if not stripped:
319 return normal_handler.handle(line_info)
319 return normal_handler.handle(line_info)
320
320
321 # special handlers are only allowed for single line statements
321 # special handlers are only allowed for single line statements
322 if continue_prompt and not self.multi_line_specials:
322 if continue_prompt and not self.multi_line_specials:
323 return normal_handler.handle(line_info)
323 return normal_handler.handle(line_info)
324
324
325 prefiltered = self.prefilter_line_info(line_info)
325 prefiltered = self.prefilter_line_info(line_info)
326 # print "prefiltered line: %r" % prefiltered
326 # print "prefiltered line: %r" % prefiltered
327 return prefiltered
327 return prefiltered
328
328
329 def prefilter_lines(self, lines, continue_prompt=False):
329 def prefilter_lines(self, lines, continue_prompt=False):
330 """Prefilter multiple input lines of text.
330 """Prefilter multiple input lines of text.
331
331
332 This is the main entry point for prefiltering multiple lines of
332 This is the main entry point for prefiltering multiple lines of
333 input. This simply calls :meth:`prefilter_line` for each line of
333 input. This simply calls :meth:`prefilter_line` for each line of
334 input.
334 input.
335
335
336 This covers cases where there are multiple lines in the user entry,
336 This covers cases where there are multiple lines in the user entry,
337 which is the case when the user goes back to a multiline history
337 which is the case when the user goes back to a multiline history
338 entry and presses enter.
338 entry and presses enter.
339 """
339 """
340 llines = lines.rstrip('\n').split('\n')
340 llines = lines.rstrip('\n').split('\n')
341 # We can get multiple lines in one shot, where multiline input 'blends'
341 # We can get multiple lines in one shot, where multiline input 'blends'
342 # into one line, in cases like recalling from the readline history
342 # into one line, in cases like recalling from the readline history
343 # buffer. We need to make sure that in such cases, we correctly
343 # buffer. We need to make sure that in such cases, we correctly
344 # communicate downstream which line is first and which are continuation
344 # communicate downstream which line is first and which are continuation
345 # ones.
345 # ones.
346 if len(llines) > 1:
346 if len(llines) > 1:
347 out = '\n'.join([self.prefilter_line(line, lnum>0)
347 out = '\n'.join([self.prefilter_line(line, lnum>0)
348 for lnum, line in enumerate(llines) ])
348 for lnum, line in enumerate(llines) ])
349 else:
349 else:
350 out = self.prefilter_line(llines[0], continue_prompt)
350 out = self.prefilter_line(llines[0], continue_prompt)
351
351
352 return out
352 return out
353
353
354 #-----------------------------------------------------------------------------
354 #-----------------------------------------------------------------------------
355 # Prefilter transformers
355 # Prefilter transformers
356 #-----------------------------------------------------------------------------
356 #-----------------------------------------------------------------------------
357
357
358
358
359 class PrefilterTransformer(Configurable):
359 class PrefilterTransformer(Configurable):
360 """Transform a line of user input."""
360 """Transform a line of user input."""
361
361
362 priority = Integer(100).tag(config=True)
362 priority = Integer(100).tag(config=True)
363 # Transformers don't currently use shell or prefilter_manager, but as we
363 # Transformers don't currently use shell or prefilter_manager, but as we
364 # move away from checkers and handlers, they will need them.
364 # move away from checkers and handlers, they will need them.
365 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
365 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
366 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True)
366 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True)
367 enabled = Bool(True).tag(config=True)
367 enabled = Bool(True).tag(config=True)
368
368
369 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
369 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
370 super(PrefilterTransformer, self).__init__(
370 super(PrefilterTransformer, self).__init__(
371 shell=shell, prefilter_manager=prefilter_manager, **kwargs
371 shell=shell, prefilter_manager=prefilter_manager, **kwargs
372 )
372 )
373 self.prefilter_manager.register_transformer(self)
373 self.prefilter_manager.register_transformer(self)
374
374
375 def transform(self, line, continue_prompt):
375 def transform(self, line, continue_prompt):
376 """Transform a line, returning the new one."""
376 """Transform a line, returning the new one."""
377 return None
377 return None
378
378
379 def __repr__(self):
379 def __repr__(self):
380 return "<%s(priority=%r, enabled=%r)>" % (
380 return "<%s(priority=%r, enabled=%r)>" % (
381 self.__class__.__name__, self.priority, self.enabled)
381 self.__class__.__name__, self.priority, self.enabled)
382
382
383
383
384 #-----------------------------------------------------------------------------
384 #-----------------------------------------------------------------------------
385 # Prefilter checkers
385 # Prefilter checkers
386 #-----------------------------------------------------------------------------
386 #-----------------------------------------------------------------------------
387
387
388
388
389 class PrefilterChecker(Configurable):
389 class PrefilterChecker(Configurable):
390 """Inspect an input line and return a handler for that line."""
390 """Inspect an input line and return a handler for that line."""
391
391
392 priority = Integer(100).tag(config=True)
392 priority = Integer(100).tag(config=True)
393 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
393 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
394 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True)
394 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True)
395 enabled = Bool(True).tag(config=True)
395 enabled = Bool(True).tag(config=True)
396
396
397 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
397 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
398 super(PrefilterChecker, self).__init__(
398 super(PrefilterChecker, self).__init__(
399 shell=shell, prefilter_manager=prefilter_manager, **kwargs
399 shell=shell, prefilter_manager=prefilter_manager, **kwargs
400 )
400 )
401 self.prefilter_manager.register_checker(self)
401 self.prefilter_manager.register_checker(self)
402
402
403 def check(self, line_info):
403 def check(self, line_info):
404 """Inspect line_info and return a handler instance or None."""
404 """Inspect line_info and return a handler instance or None."""
405 return None
405 return None
406
406
407 def __repr__(self):
407 def __repr__(self):
408 return "<%s(priority=%r, enabled=%r)>" % (
408 return "<%s(priority=%r, enabled=%r)>" % (
409 self.__class__.__name__, self.priority, self.enabled)
409 self.__class__.__name__, self.priority, self.enabled)
410
410
411
411
412 class EmacsChecker(PrefilterChecker):
412 class EmacsChecker(PrefilterChecker):
413
413
414 priority = Integer(100).tag(config=True)
414 priority = Integer(100).tag(config=True)
415 enabled = Bool(False).tag(config=True)
415 enabled = Bool(False).tag(config=True)
416
416
417 def check(self, line_info):
417 def check(self, line_info):
418 "Emacs ipython-mode tags certain input lines."
418 "Emacs ipython-mode tags certain input lines."
419 if line_info.line.endswith('# PYTHON-MODE'):
419 if line_info.line.endswith('# PYTHON-MODE'):
420 return self.prefilter_manager.get_handler_by_name('emacs')
420 return self.prefilter_manager.get_handler_by_name('emacs')
421 else:
421 else:
422 return None
422 return None
423
423
424
424
425 class MacroChecker(PrefilterChecker):
425 class MacroChecker(PrefilterChecker):
426
426
427 priority = Integer(250).tag(config=True)
427 priority = Integer(250).tag(config=True)
428
428
429 def check(self, line_info):
429 def check(self, line_info):
430 obj = self.shell.user_ns.get(line_info.ifun)
430 obj = self.shell.user_ns.get(line_info.ifun)
431 if isinstance(obj, Macro):
431 if isinstance(obj, Macro):
432 return self.prefilter_manager.get_handler_by_name('macro')
432 return self.prefilter_manager.get_handler_by_name('macro')
433 else:
433 else:
434 return None
434 return None
435
435
436
436
437 class IPyAutocallChecker(PrefilterChecker):
437 class IPyAutocallChecker(PrefilterChecker):
438
438
439 priority = Integer(300).tag(config=True)
439 priority = Integer(300).tag(config=True)
440
440
441 def check(self, line_info):
441 def check(self, line_info):
442 "Instances of IPyAutocall in user_ns get autocalled immediately"
442 "Instances of IPyAutocall in user_ns get autocalled immediately"
443 obj = self.shell.user_ns.get(line_info.ifun, None)
443 obj = self.shell.user_ns.get(line_info.ifun, None)
444 if isinstance(obj, IPyAutocall):
444 if isinstance(obj, IPyAutocall):
445 obj.set_ip(self.shell)
445 obj.set_ip(self.shell)
446 return self.prefilter_manager.get_handler_by_name('auto')
446 return self.prefilter_manager.get_handler_by_name('auto')
447 else:
447 else:
448 return None
448 return None
449
449
450
450
451 class AssignmentChecker(PrefilterChecker):
451 class AssignmentChecker(PrefilterChecker):
452
452
453 priority = Integer(600).tag(config=True)
453 priority = Integer(600).tag(config=True)
454
454
455 def check(self, line_info):
455 def check(self, line_info):
456 """Check to see if user is assigning to a var for the first time, in
456 """Check to see if user is assigning to a var for the first time, in
457 which case we want to avoid any sort of automagic / autocall games.
457 which case we want to avoid any sort of automagic / autocall games.
458
458
459 This allows users to assign to either alias or magic names true python
459 This allows users to assign to either alias or magic names true python
460 variables (the magic/alias systems always take second seat to true
460 variables (the magic/alias systems always take second seat to true
461 python code). E.g. ls='hi', or ls,that=1,2"""
461 python code). E.g. ls='hi', or ls,that=1,2"""
462 if line_info.the_rest:
462 if line_info.the_rest:
463 if line_info.the_rest[0] in '=,':
463 if line_info.the_rest[0] in '=,':
464 return self.prefilter_manager.get_handler_by_name('normal')
464 return self.prefilter_manager.get_handler_by_name('normal')
465 else:
465 else:
466 return None
466 return None
467
467
468
468
469 class AutoMagicChecker(PrefilterChecker):
469 class AutoMagicChecker(PrefilterChecker):
470
470
471 priority = Integer(700).tag(config=True)
471 priority = Integer(700).tag(config=True)
472
472
473 def check(self, line_info):
473 def check(self, line_info):
474 """If the ifun is magic, and automagic is on, run it. Note: normal,
474 """If the ifun is magic, and automagic is on, run it. Note: normal,
475 non-auto magic would already have been triggered via '%' in
475 non-auto magic would already have been triggered via '%' in
476 check_esc_chars. This just checks for automagic. Also, before
476 check_esc_chars. This just checks for automagic. Also, before
477 triggering the magic handler, make sure that there is nothing in the
477 triggering the magic handler, make sure that there is nothing in the
478 user namespace which could shadow it."""
478 user namespace which could shadow it."""
479 if not self.shell.automagic or not self.shell.find_magic(line_info.ifun):
479 if not self.shell.automagic or not self.shell.find_magic(line_info.ifun):
480 return None
480 return None
481
481
482 # We have a likely magic method. Make sure we should actually call it.
482 # We have a likely magic method. Make sure we should actually call it.
483 if line_info.continue_prompt and not self.prefilter_manager.multi_line_specials:
483 if line_info.continue_prompt and not self.prefilter_manager.multi_line_specials:
484 return None
484 return None
485
485
486 head = line_info.ifun.split('.',1)[0]
486 head = line_info.ifun.split('.',1)[0]
487 if is_shadowed(head, self.shell):
487 if is_shadowed(head, self.shell):
488 return None
488 return None
489
489
490 return self.prefilter_manager.get_handler_by_name('magic')
490 return self.prefilter_manager.get_handler_by_name('magic')
491
491
492
492
493 class PythonOpsChecker(PrefilterChecker):
493 class PythonOpsChecker(PrefilterChecker):
494
494
495 priority = Integer(900).tag(config=True)
495 priority = Integer(900).tag(config=True)
496
496
497 def check(self, line_info):
497 def check(self, line_info):
498 """If the 'rest' of the line begins with a function call or pretty much
498 """If the 'rest' of the line begins with a function call or pretty much
499 any python operator, we should simply execute the line (regardless of
499 any python operator, we should simply execute the line (regardless of
500 whether or not there's a possible autocall expansion). This avoids
500 whether or not there's a possible autocall expansion). This avoids
501 spurious (and very confusing) geattr() accesses."""
501 spurious (and very confusing) geattr() accesses."""
502 if line_info.the_rest and line_info.the_rest[0] in '!=()<>,+*/%^&|':
502 if line_info.the_rest and line_info.the_rest[0] in '!=()<>,+*/%^&|':
503 return self.prefilter_manager.get_handler_by_name('normal')
503 return self.prefilter_manager.get_handler_by_name('normal')
504 else:
504 else:
505 return None
505 return None
506
506
507
507
508 class AutocallChecker(PrefilterChecker):
508 class AutocallChecker(PrefilterChecker):
509
509
510 priority = Integer(1000).tag(config=True)
510 priority = Integer(1000).tag(config=True)
511
511
512 function_name_regexp = CRegExp(re_fun_name).tag(config=True,
512 function_name_regexp = CRegExp(re_fun_name,
513 help="RegExp to identify potential function names.")
513 help="RegExp to identify potential function names."
514 exclude_regexp = CRegExp(re_exclude_auto).tag(config=True,
514 ).tag(config=True)
515 help="RegExp to exclude strings with this start from autocalling.")
515 exclude_regexp = CRegExp(re_exclude_auto,
516 help="RegExp to exclude strings with this start from autocalling."
517 ).tag(config=True)
516
518
517 def check(self, line_info):
519 def check(self, line_info):
518 "Check if the initial word/function is callable and autocall is on."
520 "Check if the initial word/function is callable and autocall is on."
519 if not self.shell.autocall:
521 if not self.shell.autocall:
520 return None
522 return None
521
523
522 oinfo = line_info.ofind(self.shell) # This can mutate state via getattr
524 oinfo = line_info.ofind(self.shell) # This can mutate state via getattr
523 if not oinfo['found']:
525 if not oinfo['found']:
524 return None
526 return None
525
527
526 if callable(oinfo['obj']) \
528 if callable(oinfo['obj']) \
527 and (not self.exclude_regexp.match(line_info.the_rest)) \
529 and (not self.exclude_regexp.match(line_info.the_rest)) \
528 and self.function_name_regexp.match(line_info.ifun):
530 and self.function_name_regexp.match(line_info.ifun):
529 return self.prefilter_manager.get_handler_by_name('auto')
531 return self.prefilter_manager.get_handler_by_name('auto')
530 else:
532 else:
531 return None
533 return None
532
534
533
535
534 #-----------------------------------------------------------------------------
536 #-----------------------------------------------------------------------------
535 # Prefilter handlers
537 # Prefilter handlers
536 #-----------------------------------------------------------------------------
538 #-----------------------------------------------------------------------------
537
539
538
540
539 class PrefilterHandler(Configurable):
541 class PrefilterHandler(Configurable):
540
542
541 handler_name = Unicode('normal')
543 handler_name = Unicode('normal')
542 esc_strings = List([])
544 esc_strings = List([])
543 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
545 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
544 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True)
546 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True)
545
547
546 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
548 def __init__(self, shell=None, prefilter_manager=None, **kwargs):
547 super(PrefilterHandler, self).__init__(
549 super(PrefilterHandler, self).__init__(
548 shell=shell, prefilter_manager=prefilter_manager, **kwargs
550 shell=shell, prefilter_manager=prefilter_manager, **kwargs
549 )
551 )
550 self.prefilter_manager.register_handler(
552 self.prefilter_manager.register_handler(
551 self.handler_name,
553 self.handler_name,
552 self,
554 self,
553 self.esc_strings
555 self.esc_strings
554 )
556 )
555
557
556 def handle(self, line_info):
558 def handle(self, line_info):
557 # print "normal: ", line_info
559 # print "normal: ", line_info
558 """Handle normal input lines. Use as a template for handlers."""
560 """Handle normal input lines. Use as a template for handlers."""
559
561
560 # With autoindent on, we need some way to exit the input loop, and I
562 # With autoindent on, we need some way to exit the input loop, and I
561 # don't want to force the user to have to backspace all the way to
563 # don't want to force the user to have to backspace all the way to
562 # clear the line. The rule will be in this case, that either two
564 # clear the line. The rule will be in this case, that either two
563 # lines of pure whitespace in a row, or a line of pure whitespace but
565 # lines of pure whitespace in a row, or a line of pure whitespace but
564 # of a size different to the indent level, will exit the input loop.
566 # of a size different to the indent level, will exit the input loop.
565 line = line_info.line
567 line = line_info.line
566 continue_prompt = line_info.continue_prompt
568 continue_prompt = line_info.continue_prompt
567
569
568 if (continue_prompt and
570 if (continue_prompt and
569 self.shell.autoindent and
571 self.shell.autoindent and
570 line.isspace() and
572 line.isspace() and
571 0 < abs(len(line) - self.shell.indent_current_nsp) <= 2):
573 0 < abs(len(line) - self.shell.indent_current_nsp) <= 2):
572 line = ''
574 line = ''
573
575
574 return line
576 return line
575
577
576 def __str__(self):
578 def __str__(self):
577 return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name)
579 return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name)
578
580
579
581
580 class MacroHandler(PrefilterHandler):
582 class MacroHandler(PrefilterHandler):
581 handler_name = Unicode("macro")
583 handler_name = Unicode("macro")
582
584
583 def handle(self, line_info):
585 def handle(self, line_info):
584 obj = self.shell.user_ns.get(line_info.ifun)
586 obj = self.shell.user_ns.get(line_info.ifun)
585 pre_space = line_info.pre_whitespace
587 pre_space = line_info.pre_whitespace
586 line_sep = "\n" + pre_space
588 line_sep = "\n" + pre_space
587 return pre_space + line_sep.join(obj.value.splitlines())
589 return pre_space + line_sep.join(obj.value.splitlines())
588
590
589
591
590 class MagicHandler(PrefilterHandler):
592 class MagicHandler(PrefilterHandler):
591
593
592 handler_name = Unicode('magic')
594 handler_name = Unicode('magic')
593 esc_strings = List([ESC_MAGIC])
595 esc_strings = List([ESC_MAGIC])
594
596
595 def handle(self, line_info):
597 def handle(self, line_info):
596 """Execute magic functions."""
598 """Execute magic functions."""
597 ifun = line_info.ifun
599 ifun = line_info.ifun
598 the_rest = line_info.the_rest
600 the_rest = line_info.the_rest
599 cmd = '%sget_ipython().magic(%r)' % (line_info.pre_whitespace,
601 cmd = '%sget_ipython().magic(%r)' % (line_info.pre_whitespace,
600 (ifun + " " + the_rest))
602 (ifun + " " + the_rest))
601 return cmd
603 return cmd
602
604
603
605
604 class AutoHandler(PrefilterHandler):
606 class AutoHandler(PrefilterHandler):
605
607
606 handler_name = Unicode('auto')
608 handler_name = Unicode('auto')
607 esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2])
609 esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2])
608
610
609 def handle(self, line_info):
611 def handle(self, line_info):
610 """Handle lines which can be auto-executed, quoting if requested."""
612 """Handle lines which can be auto-executed, quoting if requested."""
611 line = line_info.line
613 line = line_info.line
612 ifun = line_info.ifun
614 ifun = line_info.ifun
613 the_rest = line_info.the_rest
615 the_rest = line_info.the_rest
614 esc = line_info.esc
616 esc = line_info.esc
615 continue_prompt = line_info.continue_prompt
617 continue_prompt = line_info.continue_prompt
616 obj = line_info.ofind(self.shell)['obj']
618 obj = line_info.ofind(self.shell)['obj']
617
619
618 # This should only be active for single-line input!
620 # This should only be active for single-line input!
619 if continue_prompt:
621 if continue_prompt:
620 return line
622 return line
621
623
622 force_auto = isinstance(obj, IPyAutocall)
624 force_auto = isinstance(obj, IPyAutocall)
623
625
624 # User objects sometimes raise exceptions on attribute access other
626 # User objects sometimes raise exceptions on attribute access other
625 # than AttributeError (we've seen it in the past), so it's safest to be
627 # than AttributeError (we've seen it in the past), so it's safest to be
626 # ultra-conservative here and catch all.
628 # ultra-conservative here and catch all.
627 try:
629 try:
628 auto_rewrite = obj.rewrite
630 auto_rewrite = obj.rewrite
629 except Exception:
631 except Exception:
630 auto_rewrite = True
632 auto_rewrite = True
631
633
632 if esc == ESC_QUOTE:
634 if esc == ESC_QUOTE:
633 # Auto-quote splitting on whitespace
635 # Auto-quote splitting on whitespace
634 newcmd = '%s("%s")' % (ifun,'", "'.join(the_rest.split()) )
636 newcmd = '%s("%s")' % (ifun,'", "'.join(the_rest.split()) )
635 elif esc == ESC_QUOTE2:
637 elif esc == ESC_QUOTE2:
636 # Auto-quote whole string
638 # Auto-quote whole string
637 newcmd = '%s("%s")' % (ifun,the_rest)
639 newcmd = '%s("%s")' % (ifun,the_rest)
638 elif esc == ESC_PAREN:
640 elif esc == ESC_PAREN:
639 newcmd = '%s(%s)' % (ifun,",".join(the_rest.split()))
641 newcmd = '%s(%s)' % (ifun,",".join(the_rest.split()))
640 else:
642 else:
641 # Auto-paren.
643 # Auto-paren.
642 if force_auto:
644 if force_auto:
643 # Don't rewrite if it is already a call.
645 # Don't rewrite if it is already a call.
644 do_rewrite = not the_rest.startswith('(')
646 do_rewrite = not the_rest.startswith('(')
645 else:
647 else:
646 if not the_rest:
648 if not the_rest:
647 # We only apply it to argument-less calls if the autocall
649 # We only apply it to argument-less calls if the autocall
648 # parameter is set to 2.
650 # parameter is set to 2.
649 do_rewrite = (self.shell.autocall >= 2)
651 do_rewrite = (self.shell.autocall >= 2)
650 elif the_rest.startswith('[') and hasattr(obj, '__getitem__'):
652 elif the_rest.startswith('[') and hasattr(obj, '__getitem__'):
651 # Don't autocall in this case: item access for an object
653 # Don't autocall in this case: item access for an object
652 # which is BOTH callable and implements __getitem__.
654 # which is BOTH callable and implements __getitem__.
653 do_rewrite = False
655 do_rewrite = False
654 else:
656 else:
655 do_rewrite = True
657 do_rewrite = True
656
658
657 # Figure out the rewritten command
659 # Figure out the rewritten command
658 if do_rewrite:
660 if do_rewrite:
659 if the_rest.endswith(';'):
661 if the_rest.endswith(';'):
660 newcmd = '%s(%s);' % (ifun.rstrip(),the_rest[:-1])
662 newcmd = '%s(%s);' % (ifun.rstrip(),the_rest[:-1])
661 else:
663 else:
662 newcmd = '%s(%s)' % (ifun.rstrip(), the_rest)
664 newcmd = '%s(%s)' % (ifun.rstrip(), the_rest)
663 else:
665 else:
664 normal_handler = self.prefilter_manager.get_handler_by_name('normal')
666 normal_handler = self.prefilter_manager.get_handler_by_name('normal')
665 return normal_handler.handle(line_info)
667 return normal_handler.handle(line_info)
666
668
667 # Display the rewritten call
669 # Display the rewritten call
668 if auto_rewrite:
670 if auto_rewrite:
669 self.shell.auto_rewrite_input(newcmd)
671 self.shell.auto_rewrite_input(newcmd)
670
672
671 return newcmd
673 return newcmd
672
674
673
675
674 class EmacsHandler(PrefilterHandler):
676 class EmacsHandler(PrefilterHandler):
675
677
676 handler_name = Unicode('emacs')
678 handler_name = Unicode('emacs')
677 esc_strings = List([])
679 esc_strings = List([])
678
680
679 def handle(self, line_info):
681 def handle(self, line_info):
680 """Handle input lines marked by python-mode."""
682 """Handle input lines marked by python-mode."""
681
683
682 # Currently, nothing is done. Later more functionality can be added
684 # Currently, nothing is done. Later more functionality can be added
683 # here if needed.
685 # here if needed.
684
686
685 # The input cache shouldn't be updated
687 # The input cache shouldn't be updated
686 return line_info.line
688 return line_info.line
687
689
688
690
689 #-----------------------------------------------------------------------------
691 #-----------------------------------------------------------------------------
690 # Defaults
692 # Defaults
691 #-----------------------------------------------------------------------------
693 #-----------------------------------------------------------------------------
692
694
693
695
694 _default_transformers = [
696 _default_transformers = [
695 ]
697 ]
696
698
697 _default_checkers = [
699 _default_checkers = [
698 EmacsChecker,
700 EmacsChecker,
699 MacroChecker,
701 MacroChecker,
700 IPyAutocallChecker,
702 IPyAutocallChecker,
701 AssignmentChecker,
703 AssignmentChecker,
702 AutoMagicChecker,
704 AutoMagicChecker,
703 PythonOpsChecker,
705 PythonOpsChecker,
704 AutocallChecker
706 AutocallChecker
705 ]
707 ]
706
708
707 _default_handlers = [
709 _default_handlers = [
708 PrefilterHandler,
710 PrefilterHandler,
709 MacroHandler,
711 MacroHandler,
710 MagicHandler,
712 MagicHandler,
711 AutoHandler,
713 AutoHandler,
712 EmacsHandler
714 EmacsHandler
713 ]
715 ]
@@ -1,463 +1,464 b''
1 """IPython terminal interface using prompt_toolkit in place of readline"""
1 """IPython terminal interface using prompt_toolkit in place of readline"""
2 from __future__ import print_function
2 from __future__ import print_function
3
3
4 import os
4 import os
5 import sys
5 import sys
6 import signal
6 import signal
7 import unicodedata
7 import unicodedata
8 from warnings import warn
8 from warnings import warn
9 from wcwidth import wcwidth
9 from wcwidth import wcwidth
10
10
11 from IPython.core.error import TryNext
11 from IPython.core.error import TryNext
12 from IPython.core.interactiveshell import InteractiveShell
12 from IPython.core.interactiveshell import InteractiveShell
13 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
13 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
14 from IPython.utils.terminal import toggle_set_term_title, set_term_title
14 from IPython.utils.terminal import toggle_set_term_title, set_term_title
15 from IPython.utils.process import abbrev_cwd
15 from IPython.utils.process import abbrev_cwd
16 from traitlets import Bool, CBool, Unicode, Dict, Integer, observe
16 from traitlets import Bool, CBool, Unicode, Dict, Integer, observe
17
17
18 from prompt_toolkit.completion import Completer, Completion
18 from prompt_toolkit.completion import Completer, Completion
19 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
19 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
20 from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode
20 from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode
21 from prompt_toolkit.history import InMemoryHistory
21 from prompt_toolkit.history import InMemoryHistory
22 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
22 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
23 from prompt_toolkit.interface import CommandLineInterface
23 from prompt_toolkit.interface import CommandLineInterface
24 from prompt_toolkit.key_binding.manager import KeyBindingManager
24 from prompt_toolkit.key_binding.manager import KeyBindingManager
25 from prompt_toolkit.keys import Keys
25 from prompt_toolkit.keys import Keys
26 from prompt_toolkit.layout.lexers import Lexer
26 from prompt_toolkit.layout.lexers import Lexer
27 from prompt_toolkit.layout.lexers import PygmentsLexer
27 from prompt_toolkit.layout.lexers import PygmentsLexer
28 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
28 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
29
29
30 from pygments.styles import get_style_by_name, get_all_styles
30 from pygments.styles import get_style_by_name, get_all_styles
31 from pygments.lexers import Python3Lexer, BashLexer, PythonLexer
31 from pygments.lexers import Python3Lexer, BashLexer, PythonLexer
32 from pygments.token import Token
32 from pygments.token import Token
33
33
34 from .pt_inputhooks import get_inputhook_func
34 from .pt_inputhooks import get_inputhook_func
35 from .interactiveshell import get_default_editor, TerminalMagics
35 from .interactiveshell import get_default_editor, TerminalMagics
36
36
37
37
38 class IPythonPTCompleter(Completer):
38 class IPythonPTCompleter(Completer):
39 """Adaptor to provide IPython completions to prompt_toolkit"""
39 """Adaptor to provide IPython completions to prompt_toolkit"""
40 def __init__(self, ipy_completer):
40 def __init__(self, ipy_completer):
41 self.ipy_completer = ipy_completer
41 self.ipy_completer = ipy_completer
42
42
43 def get_completions(self, document, complete_event):
43 def get_completions(self, document, complete_event):
44 if not document.current_line.strip():
44 if not document.current_line.strip():
45 return
45 return
46
46
47 used, matches = self.ipy_completer.complete(
47 used, matches = self.ipy_completer.complete(
48 line_buffer=document.current_line,
48 line_buffer=document.current_line,
49 cursor_pos=document.cursor_position_col
49 cursor_pos=document.cursor_position_col
50 )
50 )
51 start_pos = -len(used)
51 start_pos = -len(used)
52 for m in matches:
52 for m in matches:
53 m = unicodedata.normalize('NFC', m)
53 m = unicodedata.normalize('NFC', m)
54
54
55 # When the first character of the completion has a zero length,
55 # When the first character of the completion has a zero length,
56 # then it's probably a decomposed unicode character. E.g. caused by
56 # then it's probably a decomposed unicode character. E.g. caused by
57 # the "\dot" completion. Try to compose again with the previous
57 # the "\dot" completion. Try to compose again with the previous
58 # character.
58 # character.
59 if wcwidth(m[0]) == 0:
59 if wcwidth(m[0]) == 0:
60 if document.cursor_position + start_pos > 0:
60 if document.cursor_position + start_pos > 0:
61 char_before = document.text[document.cursor_position + start_pos - 1]
61 char_before = document.text[document.cursor_position + start_pos - 1]
62 m = unicodedata.normalize('NFC', char_before + m)
62 m = unicodedata.normalize('NFC', char_before + m)
63
63
64 # Yield the modified completion instead, if this worked.
64 # Yield the modified completion instead, if this worked.
65 if wcwidth(m[0:1]) == 1:
65 if wcwidth(m[0:1]) == 1:
66 yield Completion(m, start_position=start_pos - 1)
66 yield Completion(m, start_position=start_pos - 1)
67 continue
67 continue
68
68
69 # TODO: Use Jedi to determine meta_text
69 # TODO: Use Jedi to determine meta_text
70 # (Jedi currently has a bug that results in incorrect information.)
70 # (Jedi currently has a bug that results in incorrect information.)
71 # meta_text = ''
71 # meta_text = ''
72 # yield Completion(m, start_position=start_pos,
72 # yield Completion(m, start_position=start_pos,
73 # display_meta=meta_text)
73 # display_meta=meta_text)
74 yield Completion(m, start_position=start_pos)
74 yield Completion(m, start_position=start_pos)
75
75
76 class IPythonPTLexer(Lexer):
76 class IPythonPTLexer(Lexer):
77 """
77 """
78 Wrapper around PythonLexer and BashLexer.
78 Wrapper around PythonLexer and BashLexer.
79 """
79 """
80 def __init__(self):
80 def __init__(self):
81 self.python_lexer = PygmentsLexer(Python3Lexer if PY3 else PythonLexer)
81 self.python_lexer = PygmentsLexer(Python3Lexer if PY3 else PythonLexer)
82 self.shell_lexer = PygmentsLexer(BashLexer)
82 self.shell_lexer = PygmentsLexer(BashLexer)
83
83
84 def lex_document(self, cli, document):
84 def lex_document(self, cli, document):
85 if document.text.startswith('!'):
85 if document.text.startswith('!'):
86 return self.shell_lexer.lex_document(cli, document)
86 return self.shell_lexer.lex_document(cli, document)
87 else:
87 else:
88 return self.python_lexer.lex_document(cli, document)
88 return self.python_lexer.lex_document(cli, document)
89
89
90
90
91 class TerminalInteractiveShell(InteractiveShell):
91 class TerminalInteractiveShell(InteractiveShell):
92 colors_force = True
92 colors_force = True
93
93
94 space_for_menu = Integer(6).tag(config=True, help='Number of line at the bottom of the screen '
94 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
95 'to reserve for the completion menu')
95 'to reserve for the completion menu'
96 ).tag(config=True)
96
97
97 def _space_for_menu_changed(self, old, new):
98 def _space_for_menu_changed(self, old, new):
98 self._update_layout()
99 self._update_layout()
99
100
100 pt_cli = None
101 pt_cli = None
101
102
102 autoedit_syntax = CBool(False).tag(config=True,
103 autoedit_syntax = CBool(False).tag(config=True,
103 help="auto editing of files with syntax errors.")
104 help="auto editing of files with syntax errors.")
104
105
105 confirm_exit = CBool(True).tag(config=True,
106 confirm_exit = CBool(True).tag(config=True,
106 help="""
107 help="""
107 Set to confirm when you try to exit IPython with an EOF (Control-D
108 Set to confirm when you try to exit IPython with an EOF (Control-D
108 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
109 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
109 you can force a direct exit without any confirmation.""",
110 you can force a direct exit without any confirmation.""",
110 )
111 )
111 editing_mode = Unicode('emacs').tag(config=True,
112 editing_mode = Unicode('emacs',
112 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
113 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
113 )
114 ).tag(config=True)
114
115
115 mouse_support = Bool(False).tag(config=True,
116 mouse_support = Bool(False,
116 help="Enable mouse support in the prompt"
117 help="Enable mouse support in the prompt"
117 )
118 ).tag(config=True)
118
119
119 highlighting_style = Unicode('default').tag(config=True,
120 highlighting_style = Unicode('default',
120 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
121 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
121 )
122 ).tag(config=True)
122
123
123 def _highlighting_style_changed(self, old, new):
124 def _highlighting_style_changed(self, old, new):
124 self._style = self._make_style_from_name(self.highlighting_style)
125 self._style = self._make_style_from_name(self.highlighting_style)
125
126
126 highlighting_style_overrides = Dict().tag(config=True,
127 highlighting_style_overrides = Dict(
127 help="Override highlighting format for specific tokens"
128 help="Override highlighting format for specific tokens"
128 )
129 ).tag(config=True)
129
130
130 editor = Unicode(get_default_editor()).tag(config=True,
131 editor = Unicode(get_default_editor(),
131 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
132 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
132 )
133 ).tag(config=True)
133
134
134 term_title = Bool(True).tag(config=True,
135 term_title = Bool(True,
135 help="Automatically set the terminal title"
136 help="Automatically set the terminal title"
136 )
137 ).tag(config=True)
137
138
138 display_completions_in_columns = Bool(False).tag(config=True,
139 display_completions_in_columns = Bool(False,
139 help="Display a multi column completion menu.",
140 help="Display a multi column completion menu.",
140 )
141 ).tag(config=True)
141
142
142 @observe('term_title')
143 @observe('term_title')
143 def _term_title_changed(self, change):
144 def _term_title_changed(self, change):
144 self.init_term_title()
145 self.init_term_title()
145
146
146 def init_term_title(self):
147 def init_term_title(self):
147 # Enable or disable the terminal title.
148 # Enable or disable the terminal title.
148 if self.term_title:
149 if self.term_title:
149 toggle_set_term_title(True)
150 toggle_set_term_title(True)
150 set_term_title('IPython: ' + abbrev_cwd())
151 set_term_title('IPython: ' + abbrev_cwd())
151 else:
152 else:
152 toggle_set_term_title(False)
153 toggle_set_term_title(False)
153
154
154 def get_prompt_tokens(self, cli):
155 def get_prompt_tokens(self, cli):
155 return [
156 return [
156 (Token.Prompt, 'In ['),
157 (Token.Prompt, 'In ['),
157 (Token.PromptNum, str(self.execution_count)),
158 (Token.PromptNum, str(self.execution_count)),
158 (Token.Prompt, ']: '),
159 (Token.Prompt, ']: '),
159 ]
160 ]
160
161
161 def get_continuation_tokens(self, cli, width):
162 def get_continuation_tokens(self, cli, width):
162 return [
163 return [
163 (Token.Prompt, (' ' * (width - 5)) + '...: '),
164 (Token.Prompt, (' ' * (width - 5)) + '...: '),
164 ]
165 ]
165
166
166 def init_prompt_toolkit_cli(self):
167 def init_prompt_toolkit_cli(self):
167 if ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or not sys.stdin.isatty():
168 if ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or not sys.stdin.isatty():
168 # Fall back to plain non-interactive output for tests.
169 # Fall back to plain non-interactive output for tests.
169 # This is very limited, and only accepts a single line.
170 # This is very limited, and only accepts a single line.
170 def prompt():
171 def prompt():
171 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
172 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
172 self.prompt_for_code = prompt
173 self.prompt_for_code = prompt
173 return
174 return
174
175
175 kbmanager = KeyBindingManager.for_prompt()
176 kbmanager = KeyBindingManager.for_prompt()
176 insert_mode = ViInsertMode() | EmacsInsertMode()
177 insert_mode = ViInsertMode() | EmacsInsertMode()
177 # Ctrl+J == Enter, seemingly
178 # Ctrl+J == Enter, seemingly
178 @kbmanager.registry.add_binding(Keys.ControlJ,
179 @kbmanager.registry.add_binding(Keys.ControlJ,
179 filter=(HasFocus(DEFAULT_BUFFER)
180 filter=(HasFocus(DEFAULT_BUFFER)
180 & ~HasSelection()
181 & ~HasSelection()
181 & insert_mode
182 & insert_mode
182 ))
183 ))
183 def _(event):
184 def _(event):
184 b = event.current_buffer
185 b = event.current_buffer
185 d = b.document
186 d = b.document
186 if not (d.on_last_line or d.cursor_position_row >= d.line_count
187 if not (d.on_last_line or d.cursor_position_row >= d.line_count
187 - d.empty_line_count_at_the_end()):
188 - d.empty_line_count_at_the_end()):
188 b.newline()
189 b.newline()
189 return
190 return
190
191
191 status, indent = self.input_splitter.check_complete(d.text)
192 status, indent = self.input_splitter.check_complete(d.text)
192
193
193 if (status != 'incomplete') and b.accept_action.is_returnable:
194 if (status != 'incomplete') and b.accept_action.is_returnable:
194 b.accept_action.validate_and_handle(event.cli, b)
195 b.accept_action.validate_and_handle(event.cli, b)
195 else:
196 else:
196 b.insert_text('\n' + (' ' * (indent or 0)))
197 b.insert_text('\n' + (' ' * (indent or 0)))
197
198
198 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
199 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
199 def _reset_buffer(event):
200 def _reset_buffer(event):
200 event.current_buffer.reset()
201 event.current_buffer.reset()
201
202
202 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
203 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
203 def _reset_search_buffer(event):
204 def _reset_search_buffer(event):
204 if event.current_buffer.document.text:
205 if event.current_buffer.document.text:
205 event.current_buffer.reset()
206 event.current_buffer.reset()
206 else:
207 else:
207 event.cli.push_focus(DEFAULT_BUFFER)
208 event.cli.push_focus(DEFAULT_BUFFER)
208
209
209 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
210 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
210
211
211 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
212 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
212 def _suspend_to_bg(event):
213 def _suspend_to_bg(event):
213 event.cli.suspend_to_background()
214 event.cli.suspend_to_background()
214
215
215 @Condition
216 @Condition
216 def cursor_in_leading_ws(cli):
217 def cursor_in_leading_ws(cli):
217 before = cli.application.buffer.document.current_line_before_cursor
218 before = cli.application.buffer.document.current_line_before_cursor
218 return (not before) or before.isspace()
219 return (not before) or before.isspace()
219
220
220 # Ctrl+I == Tab
221 # Ctrl+I == Tab
221 @kbmanager.registry.add_binding(Keys.ControlI,
222 @kbmanager.registry.add_binding(Keys.ControlI,
222 filter=(HasFocus(DEFAULT_BUFFER)
223 filter=(HasFocus(DEFAULT_BUFFER)
223 & ~HasSelection()
224 & ~HasSelection()
224 & insert_mode
225 & insert_mode
225 & cursor_in_leading_ws
226 & cursor_in_leading_ws
226 ))
227 ))
227 def _indent_buffer(event):
228 def _indent_buffer(event):
228 event.current_buffer.insert_text(' ' * 4)
229 event.current_buffer.insert_text(' ' * 4)
229
230
230 # Pre-populate history from IPython's history database
231 # Pre-populate history from IPython's history database
231 history = InMemoryHistory()
232 history = InMemoryHistory()
232 last_cell = u""
233 last_cell = u""
233 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
234 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
234 include_latest=True):
235 include_latest=True):
235 # Ignore blank lines and consecutive duplicates
236 # Ignore blank lines and consecutive duplicates
236 cell = cell.rstrip()
237 cell = cell.rstrip()
237 if cell and (cell != last_cell):
238 if cell and (cell != last_cell):
238 history.append(cell)
239 history.append(cell)
239
240
240 self._style = self._make_style_from_name(self.highlighting_style)
241 self._style = self._make_style_from_name(self.highlighting_style)
241 style = DynamicStyle(lambda: self._style)
242 style = DynamicStyle(lambda: self._style)
242
243
243 editing_mode = getattr(EditingMode, self.editing_mode.upper())
244 editing_mode = getattr(EditingMode, self.editing_mode.upper())
244
245
245 self._app = create_prompt_application(
246 self._app = create_prompt_application(
246 editing_mode=editing_mode,
247 editing_mode=editing_mode,
247 key_bindings_registry=kbmanager.registry,
248 key_bindings_registry=kbmanager.registry,
248 history=history,
249 history=history,
249 completer=IPythonPTCompleter(self.Completer),
250 completer=IPythonPTCompleter(self.Completer),
250 enable_history_search=True,
251 enable_history_search=True,
251 style=style,
252 style=style,
252 mouse_support=self.mouse_support,
253 mouse_support=self.mouse_support,
253 **self._layout_options()
254 **self._layout_options()
254 )
255 )
255 self.pt_cli = CommandLineInterface(self._app,
256 self.pt_cli = CommandLineInterface(self._app,
256 eventloop=create_eventloop(self.inputhook))
257 eventloop=create_eventloop(self.inputhook))
257
258
258 def _make_style_from_name(self, name):
259 def _make_style_from_name(self, name):
259 """
260 """
260 Small wrapper that make an IPython compatible style from a style name
261 Small wrapper that make an IPython compatible style from a style name
261
262
262 We need that to add style for prompt ... etc.
263 We need that to add style for prompt ... etc.
263 """
264 """
264 style_cls = get_style_by_name(name)
265 style_cls = get_style_by_name(name)
265 style_overrides = {
266 style_overrides = {
266 Token.Prompt: '#009900',
267 Token.Prompt: '#009900',
267 Token.PromptNum: '#00ff00 bold',
268 Token.PromptNum: '#00ff00 bold',
268 }
269 }
269 if name == 'default':
270 if name == 'default':
270 style_cls = get_style_by_name('default')
271 style_cls = get_style_by_name('default')
271 # The default theme needs to be visible on both a dark background
272 # The default theme needs to be visible on both a dark background
272 # and a light background, because we can't tell what the terminal
273 # and a light background, because we can't tell what the terminal
273 # looks like. These tweaks to the default theme help with that.
274 # looks like. These tweaks to the default theme help with that.
274 style_overrides.update({
275 style_overrides.update({
275 Token.Number: '#007700',
276 Token.Number: '#007700',
276 Token.Operator: 'noinherit',
277 Token.Operator: 'noinherit',
277 Token.String: '#BB6622',
278 Token.String: '#BB6622',
278 Token.Name.Function: '#2080D0',
279 Token.Name.Function: '#2080D0',
279 Token.Name.Class: 'bold #2080D0',
280 Token.Name.Class: 'bold #2080D0',
280 Token.Name.Namespace: 'bold #2080D0',
281 Token.Name.Namespace: 'bold #2080D0',
281 })
282 })
282 style_overrides.update(self.highlighting_style_overrides)
283 style_overrides.update(self.highlighting_style_overrides)
283 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
284 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
284 style_dict=style_overrides)
285 style_dict=style_overrides)
285
286
286 return style
287 return style
287
288
288 def _layout_options(self):
289 def _layout_options(self):
289 """
290 """
290 Return the current layout option for the current Terminal InteractiveShell
291 Return the current layout option for the current Terminal InteractiveShell
291 """
292 """
292 return {
293 return {
293 'lexer':IPythonPTLexer(),
294 'lexer':IPythonPTLexer(),
294 'reserve_space_for_menu':self.space_for_menu,
295 'reserve_space_for_menu':self.space_for_menu,
295 'get_prompt_tokens':self.get_prompt_tokens,
296 'get_prompt_tokens':self.get_prompt_tokens,
296 'get_continuation_tokens':self.get_continuation_tokens,
297 'get_continuation_tokens':self.get_continuation_tokens,
297 'multiline':True,
298 'multiline':True,
298 'display_completions_in_columns': self.display_completions_in_columns,
299 'display_completions_in_columns': self.display_completions_in_columns,
299 }
300 }
300
301
301 def _update_layout(self):
302 def _update_layout(self):
302 """
303 """
303 Ask for a re computation of the application layout, if for example ,
304 Ask for a re computation of the application layout, if for example ,
304 some configuration options have changed.
305 some configuration options have changed.
305 """
306 """
306 self._app.layout = create_prompt_layout(**self._layout_options())
307 self._app.layout = create_prompt_layout(**self._layout_options())
307
308
308 def prompt_for_code(self):
309 def prompt_for_code(self):
309 document = self.pt_cli.run(
310 document = self.pt_cli.run(
310 pre_run=self.pre_prompt, reset_current_buffer=True)
311 pre_run=self.pre_prompt, reset_current_buffer=True)
311 return document.text
312 return document.text
312
313
313 def init_io(self):
314 def init_io(self):
314 if sys.platform not in {'win32', 'cli'}:
315 if sys.platform not in {'win32', 'cli'}:
315 return
316 return
316
317
317 import colorama
318 import colorama
318 colorama.init()
319 colorama.init()
319
320
320 # For some reason we make these wrappers around stdout/stderr.
321 # For some reason we make these wrappers around stdout/stderr.
321 # For now, we need to reset them so all output gets coloured.
322 # For now, we need to reset them so all output gets coloured.
322 # https://github.com/ipython/ipython/issues/8669
323 # https://github.com/ipython/ipython/issues/8669
323 from IPython.utils import io
324 from IPython.utils import io
324 io.stdout = io.IOStream(sys.stdout)
325 io.stdout = io.IOStream(sys.stdout)
325 io.stderr = io.IOStream(sys.stderr)
326 io.stderr = io.IOStream(sys.stderr)
326
327
327 def init_magics(self):
328 def init_magics(self):
328 super(TerminalInteractiveShell, self).init_magics()
329 super(TerminalInteractiveShell, self).init_magics()
329 self.register_magics(TerminalMagics)
330 self.register_magics(TerminalMagics)
330
331
331 def init_alias(self):
332 def init_alias(self):
332 # The parent class defines aliases that can be safely used with any
333 # The parent class defines aliases that can be safely used with any
333 # frontend.
334 # frontend.
334 super(TerminalInteractiveShell, self).init_alias()
335 super(TerminalInteractiveShell, self).init_alias()
335
336
336 # Now define aliases that only make sense on the terminal, because they
337 # Now define aliases that only make sense on the terminal, because they
337 # need direct access to the console in a way that we can't emulate in
338 # need direct access to the console in a way that we can't emulate in
338 # GUI or web frontend
339 # GUI or web frontend
339 if os.name == 'posix':
340 if os.name == 'posix':
340 for cmd in ['clear', 'more', 'less', 'man']:
341 for cmd in ['clear', 'more', 'less', 'man']:
341 self.alias_manager.soft_define_alias(cmd, cmd)
342 self.alias_manager.soft_define_alias(cmd, cmd)
342
343
343
344
344 def __init__(self, *args, **kwargs):
345 def __init__(self, *args, **kwargs):
345 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
346 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
346 self.init_prompt_toolkit_cli()
347 self.init_prompt_toolkit_cli()
347 self.init_term_title()
348 self.init_term_title()
348 self.keep_running = True
349 self.keep_running = True
349
350
350 def ask_exit(self):
351 def ask_exit(self):
351 self.keep_running = False
352 self.keep_running = False
352
353
353 rl_next_input = None
354 rl_next_input = None
354
355
355 def pre_prompt(self):
356 def pre_prompt(self):
356 if self.rl_next_input:
357 if self.rl_next_input:
357 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
358 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
358 self.rl_next_input = None
359 self.rl_next_input = None
359
360
360 def interact(self):
361 def interact(self):
361 while self.keep_running:
362 while self.keep_running:
362 print(self.separate_in, end='')
363 print(self.separate_in, end='')
363
364
364 try:
365 try:
365 code = self.prompt_for_code()
366 code = self.prompt_for_code()
366 except EOFError:
367 except EOFError:
367 if (not self.confirm_exit) \
368 if (not self.confirm_exit) \
368 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
369 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
369 self.ask_exit()
370 self.ask_exit()
370
371
371 else:
372 else:
372 if code:
373 if code:
373 self.run_cell(code, store_history=True)
374 self.run_cell(code, store_history=True)
374 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
375 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
375 self.edit_syntax_error()
376 self.edit_syntax_error()
376
377
377 def mainloop(self):
378 def mainloop(self):
378 # An extra layer of protection in case someone mashing Ctrl-C breaks
379 # An extra layer of protection in case someone mashing Ctrl-C breaks
379 # out of our internal code.
380 # out of our internal code.
380 while True:
381 while True:
381 try:
382 try:
382 self.interact()
383 self.interact()
383 break
384 break
384 except KeyboardInterrupt:
385 except KeyboardInterrupt:
385 print("\nKeyboardInterrupt escaped interact()\n")
386 print("\nKeyboardInterrupt escaped interact()\n")
386
387
387 _inputhook = None
388 _inputhook = None
388 def inputhook(self, context):
389 def inputhook(self, context):
389 if self._inputhook is not None:
390 if self._inputhook is not None:
390 self._inputhook(context)
391 self._inputhook(context)
391
392
392 def enable_gui(self, gui=None):
393 def enable_gui(self, gui=None):
393 if gui:
394 if gui:
394 self._inputhook = get_inputhook_func(gui)
395 self._inputhook = get_inputhook_func(gui)
395 else:
396 else:
396 self._inputhook = None
397 self._inputhook = None
397
398
398 # Methods to support auto-editing of SyntaxErrors:
399 # Methods to support auto-editing of SyntaxErrors:
399
400
400 def edit_syntax_error(self):
401 def edit_syntax_error(self):
401 """The bottom half of the syntax error handler called in the main loop.
402 """The bottom half of the syntax error handler called in the main loop.
402
403
403 Loop until syntax error is fixed or user cancels.
404 Loop until syntax error is fixed or user cancels.
404 """
405 """
405
406
406 while self.SyntaxTB.last_syntax_error:
407 while self.SyntaxTB.last_syntax_error:
407 # copy and clear last_syntax_error
408 # copy and clear last_syntax_error
408 err = self.SyntaxTB.clear_err_state()
409 err = self.SyntaxTB.clear_err_state()
409 if not self._should_recompile(err):
410 if not self._should_recompile(err):
410 return
411 return
411 try:
412 try:
412 # may set last_syntax_error again if a SyntaxError is raised
413 # may set last_syntax_error again if a SyntaxError is raised
413 self.safe_execfile(err.filename, self.user_ns)
414 self.safe_execfile(err.filename, self.user_ns)
414 except:
415 except:
415 self.showtraceback()
416 self.showtraceback()
416 else:
417 else:
417 try:
418 try:
418 with open(err.filename) as f:
419 with open(err.filename) as f:
419 # This should be inside a display_trap block and I
420 # This should be inside a display_trap block and I
420 # think it is.
421 # think it is.
421 sys.displayhook(f.read())
422 sys.displayhook(f.read())
422 except:
423 except:
423 self.showtraceback()
424 self.showtraceback()
424
425
425 def _should_recompile(self, e):
426 def _should_recompile(self, e):
426 """Utility routine for edit_syntax_error"""
427 """Utility routine for edit_syntax_error"""
427
428
428 if e.filename in ('<ipython console>', '<input>', '<string>',
429 if e.filename in ('<ipython console>', '<input>', '<string>',
429 '<console>', '<BackgroundJob compilation>',
430 '<console>', '<BackgroundJob compilation>',
430 None):
431 None):
431 return False
432 return False
432 try:
433 try:
433 if (self.autoedit_syntax and
434 if (self.autoedit_syntax and
434 not self.ask_yes_no(
435 not self.ask_yes_no(
435 'Return to editor to correct syntax error? '
436 'Return to editor to correct syntax error? '
436 '[Y/n] ', 'y')):
437 '[Y/n] ', 'y')):
437 return False
438 return False
438 except EOFError:
439 except EOFError:
439 return False
440 return False
440
441
441 def int0(x):
442 def int0(x):
442 try:
443 try:
443 return int(x)
444 return int(x)
444 except TypeError:
445 except TypeError:
445 return 0
446 return 0
446
447
447 # always pass integer line and offset values to editor hook
448 # always pass integer line and offset values to editor hook
448 try:
449 try:
449 self.hooks.fix_error_editor(e.filename,
450 self.hooks.fix_error_editor(e.filename,
450 int0(e.lineno), int0(e.offset),
451 int0(e.lineno), int0(e.offset),
451 e.msg)
452 e.msg)
452 except TryNext:
453 except TryNext:
453 warn('Could not open editor')
454 warn('Could not open editor')
454 return False
455 return False
455 return True
456 return True
456
457
457 # Run !system commands directly, not through pipes, so terminal programs
458 # Run !system commands directly, not through pipes, so terminal programs
458 # work correctly.
459 # work correctly.
459 system = InteractiveShell.system_raw
460 system = InteractiveShell.system_raw
460
461
461
462
462 if __name__ == '__main__':
463 if __name__ == '__main__':
463 TerminalInteractiveShell.instance().interact()
464 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now