##// END OF EJS Templates
Simplify InputSplitter by stripping out input_mode distinction
Thomas Kluyver -
Show More
@@ -1,721 +1,661 b''
1 """Analysis of text input into executable blocks.
1 """Analysis of text input into executable blocks.
2
2
3 The main class in this module, :class:`InputSplitter`, is designed to break
3 The main class in this module, :class:`InputSplitter`, is designed to break
4 input from either interactive, line-by-line environments or block-based ones,
4 input from either interactive, line-by-line environments or block-based ones,
5 into standalone blocks that can be executed by Python as 'single' statements
5 into standalone blocks that can be executed by Python as 'single' statements
6 (thus triggering sys.displayhook).
6 (thus triggering sys.displayhook).
7
7
8 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
8 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
9 with full support for the extended IPython syntax (magics, system calls, etc).
9 with full support for the extended IPython syntax (magics, system calls, etc).
10
10
11 For more details, see the class docstring below.
11 For more details, see the class docstring below.
12
12
13 Syntax Transformations
13 Syntax Transformations
14 ----------------------
14 ----------------------
15
15
16 One of the main jobs of the code in this file is to apply all syntax
16 One of the main jobs of the code in this file is to apply all syntax
17 transformations that make up 'the IPython language', i.e. magics, shell
17 transformations that make up 'the IPython language', i.e. magics, shell
18 escapes, etc. All transformations should be implemented as *fully stateless*
18 escapes, etc. All transformations should be implemented as *fully stateless*
19 entities, that simply take one line as their input and return a line.
19 entities, that simply take one line as their input and return a line.
20 Internally for implementation purposes they may be a normal function or a
20 Internally for implementation purposes they may be a normal function or a
21 callable object, but the only input they receive will be a single line and they
21 callable object, but the only input they receive will be a single line and they
22 should only return a line, without holding any data-dependent state between
22 should only return a line, without holding any data-dependent state between
23 calls.
23 calls.
24
24
25 As an example, the EscapedTransformer is a class so we can more clearly group
25 As an example, the EscapedTransformer is a class so we can more clearly group
26 together the functionality of dispatching to individual functions based on the
26 together the functionality of dispatching to individual functions based on the
27 starting escape character, but the only method for public use is its call
27 starting escape character, but the only method for public use is its call
28 method.
28 method.
29
29
30
30
31 ToDo
31 ToDo
32 ----
32 ----
33
33
34 - Should we make push() actually raise an exception once push_accepts_more()
34 - Should we make push() actually raise an exception once push_accepts_more()
35 returns False?
35 returns False?
36
36
37 - Naming cleanups. The tr_* names aren't the most elegant, though now they are
37 - Naming cleanups. The tr_* names aren't the most elegant, though now they are
38 at least just attributes of a class so not really very exposed.
38 at least just attributes of a class so not really very exposed.
39
39
40 - Think about the best way to support dynamic things: automagic, autocall,
40 - Think about the best way to support dynamic things: automagic, autocall,
41 macros, etc.
41 macros, etc.
42
42
43 - Think of a better heuristic for the application of the transforms in
43 - Think of a better heuristic for the application of the transforms in
44 IPythonInputSplitter.push() than looking at the buffer ending in ':'. Idea:
44 IPythonInputSplitter.push() than looking at the buffer ending in ':'. Idea:
45 track indentation change events (indent, dedent, nothing) and apply them only
45 track indentation change events (indent, dedent, nothing) and apply them only
46 if the indentation went up, but not otherwise.
46 if the indentation went up, but not otherwise.
47
47
48 - Think of the cleanest way for supporting user-specified transformations (the
48 - Think of the cleanest way for supporting user-specified transformations (the
49 user prefilters we had before).
49 user prefilters we had before).
50
50
51 Authors
51 Authors
52 -------
52 -------
53
53
54 * Fernando Perez
54 * Fernando Perez
55 * Brian Granger
55 * Brian Granger
56 """
56 """
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58 # Copyright (C) 2010 The IPython Development Team
58 # Copyright (C) 2010 The IPython Development Team
59 #
59 #
60 # Distributed under the terms of the BSD License. The full license is in
60 # Distributed under the terms of the BSD License. The full license is in
61 # the file COPYING, distributed as part of this software.
61 # the file COPYING, distributed as part of this software.
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63
63
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65 # Imports
65 # Imports
66 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
67 # stdlib
67 # stdlib
68 import ast
68 import ast
69 import codeop
69 import codeop
70 import re
70 import re
71 import sys
71 import sys
72
72
73 # IPython modules
73 # IPython modules
74 from IPython.core.splitinput import split_user_input, LineInfo
74 from IPython.core.splitinput import split_user_input, LineInfo
75 from IPython.utils.py3compat import cast_unicode
75 from IPython.utils.py3compat import cast_unicode
76 from IPython.core.inputtransformer import (leading_indent,
76 from IPython.core.inputtransformer import (leading_indent,
77 classic_prompt,
77 classic_prompt,
78 ipy_prompt,
78 ipy_prompt,
79 cellmagic,
79 cellmagic,
80 assemble_logical_lines,
80 assemble_logical_lines,
81 help_end,
81 help_end,
82 escaped_commands,
82 escaped_commands,
83 assign_from_magic,
83 assign_from_magic,
84 assign_from_system,
84 assign_from_system,
85 assemble_python_lines,
85 assemble_python_lines,
86 )
86 )
87
87
88 # Temporary!
88 # Temporary!
89 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
89 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
90 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
90 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
91 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
91 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
92
92
93 #-----------------------------------------------------------------------------
93 #-----------------------------------------------------------------------------
94 # Utilities
94 # Utilities
95 #-----------------------------------------------------------------------------
95 #-----------------------------------------------------------------------------
96
96
97 # FIXME: These are general-purpose utilities that later can be moved to the
97 # FIXME: These are general-purpose utilities that later can be moved to the
98 # general ward. Kept here for now because we're being very strict about test
98 # general ward. Kept here for now because we're being very strict about test
99 # coverage with this code, and this lets us ensure that we keep 100% coverage
99 # coverage with this code, and this lets us ensure that we keep 100% coverage
100 # while developing.
100 # while developing.
101
101
102 # compiled regexps for autoindent management
102 # compiled regexps for autoindent management
103 dedent_re = re.compile('|'.join([
103 dedent_re = re.compile('|'.join([
104 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
104 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
105 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
105 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
106 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
106 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
107 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
107 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
108 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
108 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
109 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
109 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
110 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
110 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
111 ]))
111 ]))
112 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
112 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
113
113
114 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
114 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
115 # before pure comments
115 # before pure comments
116 comment_line_re = re.compile('^\s*\#')
116 comment_line_re = re.compile('^\s*\#')
117
117
118
118
119 def num_ini_spaces(s):
119 def num_ini_spaces(s):
120 """Return the number of initial spaces in a string.
120 """Return the number of initial spaces in a string.
121
121
122 Note that tabs are counted as a single space. For now, we do *not* support
122 Note that tabs are counted as a single space. For now, we do *not* support
123 mixing of tabs and spaces in the user's input.
123 mixing of tabs and spaces in the user's input.
124
124
125 Parameters
125 Parameters
126 ----------
126 ----------
127 s : string
127 s : string
128
128
129 Returns
129 Returns
130 -------
130 -------
131 n : int
131 n : int
132 """
132 """
133
133
134 ini_spaces = ini_spaces_re.match(s)
134 ini_spaces = ini_spaces_re.match(s)
135 if ini_spaces:
135 if ini_spaces:
136 return ini_spaces.end()
136 return ini_spaces.end()
137 else:
137 else:
138 return 0
138 return 0
139
139
140 def last_blank(src):
140 def last_blank(src):
141 """Determine if the input source ends in a blank.
141 """Determine if the input source ends in a blank.
142
142
143 A blank is either a newline or a line consisting of whitespace.
143 A blank is either a newline or a line consisting of whitespace.
144
144
145 Parameters
145 Parameters
146 ----------
146 ----------
147 src : string
147 src : string
148 A single or multiline string.
148 A single or multiline string.
149 """
149 """
150 if not src: return False
150 if not src: return False
151 ll = src.splitlines()[-1]
151 ll = src.splitlines()[-1]
152 return (ll == '') or ll.isspace()
152 return (ll == '') or ll.isspace()
153
153
154
154
155 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
155 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
156 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
156 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
157
157
158 def last_two_blanks(src):
158 def last_two_blanks(src):
159 """Determine if the input source ends in two blanks.
159 """Determine if the input source ends in two blanks.
160
160
161 A blank is either a newline or a line consisting of whitespace.
161 A blank is either a newline or a line consisting of whitespace.
162
162
163 Parameters
163 Parameters
164 ----------
164 ----------
165 src : string
165 src : string
166 A single or multiline string.
166 A single or multiline string.
167 """
167 """
168 if not src: return False
168 if not src: return False
169 # The logic here is tricky: I couldn't get a regexp to work and pass all
169 # The logic here is tricky: I couldn't get a regexp to work and pass all
170 # the tests, so I took a different approach: split the source by lines,
170 # the tests, so I took a different approach: split the source by lines,
171 # grab the last two and prepend '###\n' as a stand-in for whatever was in
171 # grab the last two and prepend '###\n' as a stand-in for whatever was in
172 # the body before the last two lines. Then, with that structure, it's
172 # the body before the last two lines. Then, with that structure, it's
173 # possible to analyze with two regexps. Not the most elegant solution, but
173 # possible to analyze with two regexps. Not the most elegant solution, but
174 # it works. If anyone tries to change this logic, make sure to validate
174 # it works. If anyone tries to change this logic, make sure to validate
175 # the whole test suite first!
175 # the whole test suite first!
176 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
176 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
177 return (bool(last_two_blanks_re.match(new_src)) or
177 return (bool(last_two_blanks_re.match(new_src)) or
178 bool(last_two_blanks_re2.match(new_src)) )
178 bool(last_two_blanks_re2.match(new_src)) )
179
179
180
180
181 def remove_comments(src):
181 def remove_comments(src):
182 """Remove all comments from input source.
182 """Remove all comments from input source.
183
183
184 Note: comments are NOT recognized inside of strings!
184 Note: comments are NOT recognized inside of strings!
185
185
186 Parameters
186 Parameters
187 ----------
187 ----------
188 src : string
188 src : string
189 A single or multiline input string.
189 A single or multiline input string.
190
190
191 Returns
191 Returns
192 -------
192 -------
193 String with all Python comments removed.
193 String with all Python comments removed.
194 """
194 """
195
195
196 return re.sub('#.*', '', src)
196 return re.sub('#.*', '', src)
197
197
198
198
199 def get_input_encoding():
199 def get_input_encoding():
200 """Return the default standard input encoding.
200 """Return the default standard input encoding.
201
201
202 If sys.stdin has no encoding, 'ascii' is returned."""
202 If sys.stdin has no encoding, 'ascii' is returned."""
203 # There are strange environments for which sys.stdin.encoding is None. We
203 # There are strange environments for which sys.stdin.encoding is None. We
204 # ensure that a valid encoding is returned.
204 # ensure that a valid encoding is returned.
205 encoding = getattr(sys.stdin, 'encoding', None)
205 encoding = getattr(sys.stdin, 'encoding', None)
206 if encoding is None:
206 if encoding is None:
207 encoding = 'ascii'
207 encoding = 'ascii'
208 return encoding
208 return encoding
209
209
210 #-----------------------------------------------------------------------------
210 #-----------------------------------------------------------------------------
211 # Classes and functions for normal Python syntax handling
211 # Classes and functions for normal Python syntax handling
212 #-----------------------------------------------------------------------------
212 #-----------------------------------------------------------------------------
213
213
214 class InputSplitter(object):
214 class InputSplitter(object):
215 """An object that can accumulate lines of Python source before execution.
215 """An object that can accumulate lines of Python source before execution.
216
216
217 This object is designed to be fed python source line-by-line, using
217 This object is designed to be fed python source line-by-line, using
218 :meth:`push`. It will return on each push whether the currently pushed
218 :meth:`push`. It will return on each push whether the currently pushed
219 code could be executed already. In addition, it provides a method called
219 code could be executed already. In addition, it provides a method called
220 :meth:`push_accepts_more` that can be used to query whether more input
220 :meth:`push_accepts_more` that can be used to query whether more input
221 can be pushed into a single interactive block.
221 can be pushed into a single interactive block.
222
222
223 This is a simple example of how an interactive terminal-based client can use
223 This is a simple example of how an interactive terminal-based client can use
224 this tool::
224 this tool::
225
225
226 isp = InputSplitter()
226 isp = InputSplitter()
227 while isp.push_accepts_more():
227 while isp.push_accepts_more():
228 indent = ' '*isp.indent_spaces
228 indent = ' '*isp.indent_spaces
229 prompt = '>>> ' + indent
229 prompt = '>>> ' + indent
230 line = indent + raw_input(prompt)
230 line = indent + raw_input(prompt)
231 isp.push(line)
231 isp.push(line)
232 print 'Input source was:\n', isp.source_reset(),
232 print 'Input source was:\n', isp.source_reset(),
233 """
233 """
234 # Number of spaces of indentation computed from input that has been pushed
234 # Number of spaces of indentation computed from input that has been pushed
235 # so far. This is the attributes callers should query to get the current
235 # so far. This is the attributes callers should query to get the current
236 # indentation level, in order to provide auto-indent facilities.
236 # indentation level, in order to provide auto-indent facilities.
237 indent_spaces = 0
237 indent_spaces = 0
238 # String, indicating the default input encoding. It is computed by default
238 # String, indicating the default input encoding. It is computed by default
239 # at initialization time via get_input_encoding(), but it can be reset by a
239 # at initialization time via get_input_encoding(), but it can be reset by a
240 # client with specific knowledge of the encoding.
240 # client with specific knowledge of the encoding.
241 encoding = ''
241 encoding = ''
242 # String where the current full source input is stored, properly encoded.
242 # String where the current full source input is stored, properly encoded.
243 # Reading this attribute is the normal way of querying the currently pushed
243 # Reading this attribute is the normal way of querying the currently pushed
244 # source code, that has been properly encoded.
244 # source code, that has been properly encoded.
245 source = ''
245 source = ''
246 # Code object corresponding to the current source. It is automatically
246 # Code object corresponding to the current source. It is automatically
247 # synced to the source, so it can be queried at any time to obtain the code
247 # synced to the source, so it can be queried at any time to obtain the code
248 # object; it will be None if the source doesn't compile to valid Python.
248 # object; it will be None if the source doesn't compile to valid Python.
249 code = None
249 code = None
250 # Input mode
251 input_mode = 'line'
252
250
253 # Private attributes
251 # Private attributes
254
252
255 # List with lines of input accumulated so far
253 # List with lines of input accumulated so far
256 _buffer = None
254 _buffer = None
257 # Command compiler
255 # Command compiler
258 _compile = None
256 _compile = None
259 # Mark when input has changed indentation all the way back to flush-left
257 # Mark when input has changed indentation all the way back to flush-left
260 _full_dedent = False
258 _full_dedent = False
261 # Boolean indicating whether the current block is complete
259 # Boolean indicating whether the current block is complete
262 _is_complete = None
260 _is_complete = None
263
261
264 def __init__(self, input_mode=None):
262 def __init__(self):
265 """Create a new InputSplitter instance.
263 """Create a new InputSplitter instance.
266
267 Parameters
268 ----------
269 input_mode : str
270
271 One of ['line', 'cell']; default is 'line'.
272
273 The input_mode parameter controls how new inputs are used when fed via
274 the :meth:`push` method:
275
276 - 'line': meant for line-oriented clients, inputs are appended one at a
277 time to the internal buffer and the whole buffer is compiled.
278
279 - 'cell': meant for clients that can edit multi-line 'cells' of text at
280 a time. A cell can contain one or more blocks that can be compile in
281 'single' mode by Python. In this mode, each new input new input
282 completely replaces all prior inputs. Cell mode is thus equivalent
283 to prepending a full reset() to every push() call.
284 """
264 """
285 self._buffer = []
265 self._buffer = []
286 self._compile = codeop.CommandCompiler()
266 self._compile = codeop.CommandCompiler()
287 self.encoding = get_input_encoding()
267 self.encoding = get_input_encoding()
288 self.input_mode = InputSplitter.input_mode if input_mode is None \
289 else input_mode
290
268
291 def reset(self):
269 def reset(self):
292 """Reset the input buffer and associated state."""
270 """Reset the input buffer and associated state."""
293 self.indent_spaces = 0
271 self.indent_spaces = 0
294 self._buffer[:] = []
272 self._buffer[:] = []
295 self.source = ''
273 self.source = ''
296 self.code = None
274 self.code = None
297 self._is_complete = False
275 self._is_complete = False
298 self._full_dedent = False
276 self._full_dedent = False
299
277
300 def source_reset(self):
278 def source_reset(self):
301 """Return the input source and perform a full reset.
279 """Return the input source and perform a full reset.
302 """
280 """
303 out = self.source
281 out = self.source
304 self.reset()
282 self.reset()
305 return out
283 return out
306
284
307 def push(self, lines):
285 def push(self, lines):
308 """Push one or more lines of input.
286 """Push one or more lines of input.
309
287
310 This stores the given lines and returns a status code indicating
288 This stores the given lines and returns a status code indicating
311 whether the code forms a complete Python block or not.
289 whether the code forms a complete Python block or not.
312
290
313 Any exceptions generated in compilation are swallowed, but if an
291 Any exceptions generated in compilation are swallowed, but if an
314 exception was produced, the method returns True.
292 exception was produced, the method returns True.
315
293
316 Parameters
294 Parameters
317 ----------
295 ----------
318 lines : string
296 lines : string
319 One or more lines of Python input.
297 One or more lines of Python input.
320
298
321 Returns
299 Returns
322 -------
300 -------
323 is_complete : boolean
301 is_complete : boolean
324 True if the current input source (the result of the current input
302 True if the current input source (the result of the current input
325 plus prior inputs) forms a complete Python execution block. Note that
303 plus prior inputs) forms a complete Python execution block. Note that
326 this value is also stored as a private attribute (``_is_complete``), so it
304 this value is also stored as a private attribute (``_is_complete``), so it
327 can be queried at any time.
305 can be queried at any time.
328 """
306 """
329 if self.input_mode == 'cell':
330 self.reset()
331
332 self._store(lines)
307 self._store(lines)
333 source = self.source
308 source = self.source
334
309
335 # Before calling _compile(), reset the code object to None so that if an
310 # Before calling _compile(), reset the code object to None so that if an
336 # exception is raised in compilation, we don't mislead by having
311 # exception is raised in compilation, we don't mislead by having
337 # inconsistent code/source attributes.
312 # inconsistent code/source attributes.
338 self.code, self._is_complete = None, None
313 self.code, self._is_complete = None, None
339
314
340 # Honor termination lines properly
315 # Honor termination lines properly
341 if source.endswith('\\\n'):
316 if source.endswith('\\\n'):
342 return False
317 return False
343
318
344 self._update_indent(lines)
319 self._update_indent(lines)
345 try:
320 try:
346 self.code = self._compile(source, symbol="exec")
321 self.code = self._compile(source, symbol="exec")
347 # Invalid syntax can produce any of a number of different errors from
322 # Invalid syntax can produce any of a number of different errors from
348 # inside the compiler, so we have to catch them all. Syntax errors
323 # inside the compiler, so we have to catch them all. Syntax errors
349 # immediately produce a 'ready' block, so the invalid Python can be
324 # immediately produce a 'ready' block, so the invalid Python can be
350 # sent to the kernel for evaluation with possible ipython
325 # sent to the kernel for evaluation with possible ipython
351 # special-syntax conversion.
326 # special-syntax conversion.
352 except (SyntaxError, OverflowError, ValueError, TypeError,
327 except (SyntaxError, OverflowError, ValueError, TypeError,
353 MemoryError):
328 MemoryError):
354 self._is_complete = True
329 self._is_complete = True
355 else:
330 else:
356 # Compilation didn't produce any exceptions (though it may not have
331 # Compilation didn't produce any exceptions (though it may not have
357 # given a complete code object)
332 # given a complete code object)
358 self._is_complete = self.code is not None
333 self._is_complete = self.code is not None
359
334
360 return self._is_complete
335 return self._is_complete
361
336
362 def push_accepts_more(self):
337 def push_accepts_more(self):
363 """Return whether a block of interactive input can accept more input.
338 """Return whether a block of interactive input can accept more input.
364
339
365 This method is meant to be used by line-oriented frontends, who need to
340 This method is meant to be used by line-oriented frontends, who need to
366 guess whether a block is complete or not based solely on prior and
341 guess whether a block is complete or not based solely on prior and
367 current input lines. The InputSplitter considers it has a complete
342 current input lines. The InputSplitter considers it has a complete
368 interactive block and will not accept more input only when either a
343 interactive block and will not accept more input only when either a
369 SyntaxError is raised, or *all* of the following are true:
344 SyntaxError is raised, or *all* of the following are true:
370
345
371 1. The input compiles to a complete statement.
346 1. The input compiles to a complete statement.
372
347
373 2. The indentation level is flush-left (because if we are indented,
348 2. The indentation level is flush-left (because if we are indented,
374 like inside a function definition or for loop, we need to keep
349 like inside a function definition or for loop, we need to keep
375 reading new input).
350 reading new input).
376
351
377 3. There is one extra line consisting only of whitespace.
352 3. There is one extra line consisting only of whitespace.
378
353
379 Because of condition #3, this method should be used only by
354 Because of condition #3, this method should be used only by
380 *line-oriented* frontends, since it means that intermediate blank lines
355 *line-oriented* frontends, since it means that intermediate blank lines
381 are not allowed in function definitions (or any other indented block).
356 are not allowed in function definitions (or any other indented block).
382
357
383 If the current input produces a syntax error, this method immediately
358 If the current input produces a syntax error, this method immediately
384 returns False but does *not* raise the syntax error exception, as
359 returns False but does *not* raise the syntax error exception, as
385 typically clients will want to send invalid syntax to an execution
360 typically clients will want to send invalid syntax to an execution
386 backend which might convert the invalid syntax into valid Python via
361 backend which might convert the invalid syntax into valid Python via
387 one of the dynamic IPython mechanisms.
362 one of the dynamic IPython mechanisms.
388 """
363 """
389
364
390 # With incomplete input, unconditionally accept more
365 # With incomplete input, unconditionally accept more
366 # A syntax error also sets _is_complete to True - see push()
391 if not self._is_complete:
367 if not self._is_complete:
368 #print("Not complete") # debug
392 return True
369 return True
393
370
394 # If we already have complete input and we're flush left, the answer
371 # The user can make any (complete) input execute by leaving a blank line
395 # depends. In line mode, if there hasn't been any indentation,
372 last_line = self.source.splitlines()[-1]
396 # that's it. If we've come back from some indentation, we need
373 if (not last_line) or last_line.isspace():
397 # the blank final line to finish.
374 #print("Blank line") # debug
398 # In cell mode, we need to check how many blocks the input so far
375 return False
399 # compiles into, because if there's already more than one full
376
400 # independent block of input, then the client has entered full
377 # If there's just a single AST node, and we're flush left, as is the
401 # 'cell' mode and is feeding lines that each is complete. In this
378 # case after a simple statement such as 'a=1', we want to execute it
402 # case we should then keep accepting. The Qt terminal-like console
379 # straight away.
403 # does precisely this, to provide the convenience of terminal-like
404 # input of single expressions, but allowing the user (with a
405 # separate keystroke) to switch to 'cell' mode and type multiple
406 # expressions in one shot.
407 if self.indent_spaces==0:
380 if self.indent_spaces==0:
408 if self.input_mode=='line':
381 try:
409 if not self._full_dedent:
382 code_ast = ast.parse(u''.join(self._buffer))
410 return False
383 except Exception:
384 #print("Can't parse AST") # debug
385 return False
411 else:
386 else:
412 try:
387 if len(code_ast.body) == 1 and \
413 code_ast = ast.parse(u''.join(self._buffer))
388 not hasattr(code_ast.body[0], 'body'):
414 except Exception:
389 #print("Simple statement") # debug
415 return False
390 return False
416 else:
417 if len(code_ast.body) == 1:
418 return False
419
391
420 # When input is complete, then termination is marked by an extra blank
392 # General fallback - accept more code
421 # line at the end.
393 return True
422 last_line = self.source.splitlines()[-1]
423 return bool(last_line and not last_line.isspace())
424
394
425 #------------------------------------------------------------------------
395 #------------------------------------------------------------------------
426 # Private interface
396 # Private interface
427 #------------------------------------------------------------------------
397 #------------------------------------------------------------------------
428
398
429 def _find_indent(self, line):
399 def _find_indent(self, line):
430 """Compute the new indentation level for a single line.
400 """Compute the new indentation level for a single line.
431
401
432 Parameters
402 Parameters
433 ----------
403 ----------
434 line : str
404 line : str
435 A single new line of non-whitespace, non-comment Python input.
405 A single new line of non-whitespace, non-comment Python input.
436
406
437 Returns
407 Returns
438 -------
408 -------
439 indent_spaces : int
409 indent_spaces : int
440 New value for the indent level (it may be equal to self.indent_spaces
410 New value for the indent level (it may be equal to self.indent_spaces
441 if indentation doesn't change.
411 if indentation doesn't change.
442
412
443 full_dedent : boolean
413 full_dedent : boolean
444 Whether the new line causes a full flush-left dedent.
414 Whether the new line causes a full flush-left dedent.
445 """
415 """
446 indent_spaces = self.indent_spaces
416 indent_spaces = self.indent_spaces
447 full_dedent = self._full_dedent
417 full_dedent = self._full_dedent
448
418
449 inisp = num_ini_spaces(line)
419 inisp = num_ini_spaces(line)
450 if inisp < indent_spaces:
420 if inisp < indent_spaces:
451 indent_spaces = inisp
421 indent_spaces = inisp
452 if indent_spaces <= 0:
422 if indent_spaces <= 0:
453 #print 'Full dedent in text',self.source # dbg
423 #print 'Full dedent in text',self.source # dbg
454 full_dedent = True
424 full_dedent = True
455
425
456 if line.rstrip()[-1] == ':':
426 if line.rstrip()[-1] == ':':
457 indent_spaces += 4
427 indent_spaces += 4
458 elif dedent_re.match(line):
428 elif dedent_re.match(line):
459 indent_spaces -= 4
429 indent_spaces -= 4
460 if indent_spaces <= 0:
430 if indent_spaces <= 0:
461 full_dedent = True
431 full_dedent = True
462
432
463 # Safety
433 # Safety
464 if indent_spaces < 0:
434 if indent_spaces < 0:
465 indent_spaces = 0
435 indent_spaces = 0
466 #print 'safety' # dbg
436 #print 'safety' # dbg
467
437
468 return indent_spaces, full_dedent
438 return indent_spaces, full_dedent
469
439
470 def _update_indent(self, lines):
440 def _update_indent(self, lines):
471 for line in remove_comments(lines).splitlines():
441 for line in remove_comments(lines).splitlines():
472 if line and not line.isspace():
442 if line and not line.isspace():
473 self.indent_spaces, self._full_dedent = self._find_indent(line)
443 self.indent_spaces, self._full_dedent = self._find_indent(line)
474
444
475 def _store(self, lines, buffer=None, store='source'):
445 def _store(self, lines, buffer=None, store='source'):
476 """Store one or more lines of input.
446 """Store one or more lines of input.
477
447
478 If input lines are not newline-terminated, a newline is automatically
448 If input lines are not newline-terminated, a newline is automatically
479 appended."""
449 appended."""
480
450
481 if buffer is None:
451 if buffer is None:
482 buffer = self._buffer
452 buffer = self._buffer
483
453
484 if lines.endswith('\n'):
454 if lines.endswith('\n'):
485 buffer.append(lines)
455 buffer.append(lines)
486 else:
456 else:
487 buffer.append(lines+'\n')
457 buffer.append(lines+'\n')
488 setattr(self, store, self._set_source(buffer))
458 setattr(self, store, self._set_source(buffer))
489
459
490 def _set_source(self, buffer):
460 def _set_source(self, buffer):
491 return u''.join(buffer)
461 return u''.join(buffer)
492
462
493
463
494 class IPythonInputSplitter(InputSplitter):
464 class IPythonInputSplitter(InputSplitter):
495 """An input splitter that recognizes all of IPython's special syntax."""
465 """An input splitter that recognizes all of IPython's special syntax."""
496
466
497 # String with raw, untransformed input.
467 # String with raw, untransformed input.
498 source_raw = ''
468 source_raw = ''
499
469
500 # Flag to track when a transformer has stored input that it hasn't given
470 # Flag to track when a transformer has stored input that it hasn't given
501 # back yet.
471 # back yet.
502 transformer_accumulating = False
472 transformer_accumulating = False
503
473
504 # Flag to track when assemble_python_lines has stored input that it hasn't
474 # Flag to track when assemble_python_lines has stored input that it hasn't
505 # given back yet.
475 # given back yet.
506 within_python_line = False
476 within_python_line = False
507
477
508 # Private attributes
478 # Private attributes
509
479
510 # List with lines of raw input accumulated so far.
480 # List with lines of raw input accumulated so far.
511 _buffer_raw = None
481 _buffer_raw = None
512
482
513 def __init__(self, input_mode=None, physical_line_transforms=None,
483 def __init__(self, physical_line_transforms=None,
514 logical_line_transforms=None, python_line_transforms=None):
484 logical_line_transforms=None, python_line_transforms=None):
515 super(IPythonInputSplitter, self).__init__(input_mode)
485 super(IPythonInputSplitter, self).__init__()
516 self._buffer_raw = []
486 self._buffer_raw = []
517 self._validate = True
487 self._validate = True
518
488
519 if physical_line_transforms is not None:
489 if physical_line_transforms is not None:
520 self.physical_line_transforms = physical_line_transforms
490 self.physical_line_transforms = physical_line_transforms
521 else:
491 else:
522 self.physical_line_transforms = [leading_indent(),
492 self.physical_line_transforms = [leading_indent(),
523 classic_prompt(),
493 classic_prompt(),
524 ipy_prompt(),
494 ipy_prompt(),
525 ]
495 ]
526
496
527 self.assemble_logical_lines = assemble_logical_lines()
497 self.assemble_logical_lines = assemble_logical_lines()
528 if logical_line_transforms is not None:
498 if logical_line_transforms is not None:
529 self.logical_line_transforms = logical_line_transforms
499 self.logical_line_transforms = logical_line_transforms
530 else:
500 else:
531 self.logical_line_transforms = [cellmagic(),
501 self.logical_line_transforms = [cellmagic(),
532 help_end(),
502 help_end(),
533 escaped_commands(),
503 escaped_commands(),
534 assign_from_magic(),
504 assign_from_magic(),
535 assign_from_system(),
505 assign_from_system(),
536 ]
506 ]
537
507
538 self.assemble_python_lines = assemble_python_lines()
508 self.assemble_python_lines = assemble_python_lines()
539 if python_line_transforms is not None:
509 if python_line_transforms is not None:
540 self.python_line_transforms = python_line_transforms
510 self.python_line_transforms = python_line_transforms
541 else:
511 else:
542 # We don't use any of these at present
512 # We don't use any of these at present
543 self.python_line_transforms = []
513 self.python_line_transforms = []
544
514
545 @property
515 @property
546 def transforms(self):
516 def transforms(self):
547 "Quick access to all transformers."
517 "Quick access to all transformers."
548 return self.physical_line_transforms + \
518 return self.physical_line_transforms + \
549 [self.assemble_logical_lines] + self.logical_line_transforms + \
519 [self.assemble_logical_lines] + self.logical_line_transforms + \
550 [self.assemble_python_lines] + self.python_line_transforms
520 [self.assemble_python_lines] + self.python_line_transforms
551
521
552 @property
522 @property
553 def transforms_in_use(self):
523 def transforms_in_use(self):
554 """Transformers, excluding logical line transformers if we're in a
524 """Transformers, excluding logical line transformers if we're in a
555 Python line."""
525 Python line."""
556 t = self.physical_line_transforms[:]
526 t = self.physical_line_transforms[:]
557 if not self.within_python_line:
527 if not self.within_python_line:
558 t += [self.assemble_logical_lines] + self.logical_line_transforms
528 t += [self.assemble_logical_lines] + self.logical_line_transforms
559 return t + [self.assemble_python_lines] + self.python_line_transforms
529 return t + [self.assemble_python_lines] + self.python_line_transforms
560
530
561 def reset(self):
531 def reset(self):
562 """Reset the input buffer and associated state."""
532 """Reset the input buffer and associated state."""
563 super(IPythonInputSplitter, self).reset()
533 super(IPythonInputSplitter, self).reset()
564 self._buffer_raw[:] = []
534 self._buffer_raw[:] = []
565 self.source_raw = ''
535 self.source_raw = ''
566 self.transformer_accumulating = False
536 self.transformer_accumulating = False
567 self.within_python_line = False
537 self.within_python_line = False
568 for t in self.transforms:
538 for t in self.transforms:
569 t.reset()
539 t.reset()
570
540
571 def flush_transformers(self):
541 def flush_transformers(self):
572 def _flush(transform, out):
542 def _flush(transform, out):
573 if out is not None:
543 if out is not None:
574 tmp = transform.push(out)
544 tmp = transform.push(out)
575 return tmp or transform.reset() or None
545 return tmp or transform.reset() or None
576 else:
546 else:
577 return transform.reset() or None
547 return transform.reset() or None
578
548
579 out = None
549 out = None
580 for t in self.transforms_in_use:
550 for t in self.transforms_in_use:
581 out = _flush(t, out)
551 out = _flush(t, out)
582
552
583 if out is not None:
553 if out is not None:
584 self._store(out)
554 self._store(out)
585
555
586 def source_raw_reset(self):
556 def source_raw_reset(self):
587 """Return input and raw source and perform a full reset.
557 """Return input and raw source and perform a full reset.
588 """
558 """
589 self.flush_transformers()
559 self.flush_transformers()
590 out = self.source
560 out = self.source
591 out_r = self.source_raw
561 out_r = self.source_raw
592 self.reset()
562 self.reset()
593 return out, out_r
563 return out, out_r
594
564
595 def source_reset(self):
565 def source_reset(self):
596 self.flush_transformers()
566 self.flush_transformers()
597 return super(IPythonInputSplitter, self).source_reset()
567 return super(IPythonInputSplitter, self).source_reset()
598
568
599 def push_accepts_more(self):
569 def push_accepts_more(self):
600 if self.transformer_accumulating:
570 if self.transformer_accumulating:
601 return True
571 return True
602 else:
572 else:
603 return super(IPythonInputSplitter, self).push_accepts_more()
573 return super(IPythonInputSplitter, self).push_accepts_more()
604
574
605 def transform_cell(self, cell):
575 def transform_cell(self, cell):
606 """Process and translate a cell of input.
576 """Process and translate a cell of input.
607 """
577 """
608 self.reset()
578 self.reset()
609 self.push(cell)
579 self.push(cell)
610 return self.source_reset()
580 return self.source_reset()
611
581
612 def push(self, lines):
582 def push(self, lines):
613 """Push one or more lines of IPython input.
583 """Push one or more lines of IPython input.
614
584
615 This stores the given lines and returns a status code indicating
585 This stores the given lines and returns a status code indicating
616 whether the code forms a complete Python block or not, after processing
586 whether the code forms a complete Python block or not, after processing
617 all input lines for special IPython syntax.
587 all input lines for special IPython syntax.
618
588
619 Any exceptions generated in compilation are swallowed, but if an
589 Any exceptions generated in compilation are swallowed, but if an
620 exception was produced, the method returns True.
590 exception was produced, the method returns True.
621
591
622 Parameters
592 Parameters
623 ----------
593 ----------
624 lines : string
594 lines : string
625 One or more lines of Python input.
595 One or more lines of Python input.
626
596
627 Returns
597 Returns
628 -------
598 -------
629 is_complete : boolean
599 is_complete : boolean
630 True if the current input source (the result of the current input
600 True if the current input source (the result of the current input
631 plus prior inputs) forms a complete Python execution block. Note that
601 plus prior inputs) forms a complete Python execution block. Note that
632 this value is also stored as a private attribute (_is_complete), so it
602 this value is also stored as a private attribute (_is_complete), so it
633 can be queried at any time.
603 can be queried at any time.
634 """
604 """
635
605
636 # We must ensure all input is pure unicode
606 # We must ensure all input is pure unicode
637 lines = cast_unicode(lines, self.encoding)
607 lines = cast_unicode(lines, self.encoding)
638
608
639 # ''.splitlines() --> [], but we need to push the empty line to transformers
609 # ''.splitlines() --> [], but we need to push the empty line to transformers
640 lines_list = lines.splitlines()
610 lines_list = lines.splitlines()
641 if not lines_list:
611 if not lines_list:
642 lines_list = ['']
612 lines_list = ['']
643
613
644 # Transform logic
645 #
646 # We only apply the line transformers to the input if we have either no
647 # input yet, or complete input, or if the last line of the buffer ends
648 # with ':' (opening an indented block). This prevents the accidental
649 # transformation of escapes inside multiline expressions like
650 # triple-quoted strings or parenthesized expressions.
651 #
652 # The last heuristic, while ugly, ensures that the first line of an
653 # indented block is correctly transformed.
654 #
655 # FIXME: try to find a cleaner approach for this last bit.
656
657 # If we were in 'block' mode, since we're going to pump the parent
658 # class by hand line by line, we need to temporarily switch out to
659 # 'line' mode, do a single manual reset and then feed the lines one
660 # by one. Note that this only matters if the input has more than one
661 # line.
662 changed_input_mode = False
663
664 if self.input_mode == 'cell':
665 self.reset()
666 changed_input_mode = True
667 saved_input_mode = 'cell'
668 self.input_mode = 'line'
669
670 # Store raw source before applying any transformations to it. Note
614 # Store raw source before applying any transformations to it. Note
671 # that this must be done *after* the reset() call that would otherwise
615 # that this must be done *after* the reset() call that would otherwise
672 # flush the buffer.
616 # flush the buffer.
673 self._store(lines, self._buffer_raw, 'source_raw')
617 self._store(lines, self._buffer_raw, 'source_raw')
674
618
675 try:
619 for line in lines_list:
676 for line in lines_list:
620 out = self.push_line(line)
677 out = self.push_line(line)
621
678 finally:
679 if changed_input_mode:
680 self.input_mode = saved_input_mode
681
682 return out
622 return out
683
623
684 def push_line(self, line):
624 def push_line(self, line):
685 buf = self._buffer
625 buf = self._buffer
686
626
687 def _accumulating(dbg):
627 def _accumulating(dbg):
688 #print(dbg)
628 #print(dbg)
689 self.transformer_accumulating = True
629 self.transformer_accumulating = True
690 return False
630 return False
691
631
692 for transformer in self.physical_line_transforms:
632 for transformer in self.physical_line_transforms:
693 line = transformer.push(line)
633 line = transformer.push(line)
694 if line is None:
634 if line is None:
695 return _accumulating(transformer)
635 return _accumulating(transformer)
696
636
697 if not self.within_python_line:
637 if not self.within_python_line:
698 line = self.assemble_logical_lines.push(line)
638 line = self.assemble_logical_lines.push(line)
699 if line is None:
639 if line is None:
700 return _accumulating('acc logical line')
640 return _accumulating('acc logical line')
701
641
702 for transformer in self.logical_line_transforms:
642 for transformer in self.logical_line_transforms:
703 line = transformer.push(line)
643 line = transformer.push(line)
704 if line is None:
644 if line is None:
705 return _accumulating(transformer)
645 return _accumulating(transformer)
706
646
707 line = self.assemble_python_lines.push(line)
647 line = self.assemble_python_lines.push(line)
708 if line is None:
648 if line is None:
709 self.within_python_line = True
649 self.within_python_line = True
710 return _accumulating('acc python line')
650 return _accumulating('acc python line')
711 else:
651 else:
712 self.within_python_line = False
652 self.within_python_line = False
713
653
714 for transformer in self.python_line_transforms:
654 for transformer in self.python_line_transforms:
715 line = transformer.push(line)
655 line = transformer.push(line)
716 if line is None:
656 if line is None:
717 return _accumulating(transformer)
657 return _accumulating(transformer)
718
658
719 #print("transformers clear") #debug
659 #print("transformers clear") #debug
720 self.transformer_accumulating = False
660 self.transformer_accumulating = False
721 return super(IPythonInputSplitter, self).push(line)
661 return super(IPythonInputSplitter, self).push(line)
@@ -1,626 +1,576 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the inputsplitter module.
2 """Tests for the inputsplitter module.
3
3
4 Authors
4 Authors
5 -------
5 -------
6 * Fernando Perez
6 * Fernando Perez
7 * Robert Kern
7 * Robert Kern
8 """
8 """
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2010-2011 The IPython Development Team
10 # Copyright (C) 2010-2011 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # stdlib
19 # stdlib
20 import unittest
20 import unittest
21 import sys
21 import sys
22
22
23 # Third party
23 # Third party
24 import nose.tools as nt
24 import nose.tools as nt
25
25
26 # Our own
26 # Our own
27 from IPython.core import inputsplitter as isp
27 from IPython.core import inputsplitter as isp
28 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
28 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
29 from IPython.testing import tools as tt
29 from IPython.testing import tools as tt
30 from IPython.utils import py3compat
30 from IPython.utils import py3compat
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Semi-complete examples (also used as tests)
33 # Semi-complete examples (also used as tests)
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 # Note: at the bottom, there's a slightly more complete version of this that
36 # Note: at the bottom, there's a slightly more complete version of this that
37 # can be useful during development of code here.
37 # can be useful during development of code here.
38
38
39 def mini_interactive_loop(input_func):
39 def mini_interactive_loop(input_func):
40 """Minimal example of the logic of an interactive interpreter loop.
40 """Minimal example of the logic of an interactive interpreter loop.
41
41
42 This serves as an example, and it is used by the test system with a fake
42 This serves as an example, and it is used by the test system with a fake
43 raw_input that simulates interactive input."""
43 raw_input that simulates interactive input."""
44
44
45 from IPython.core.inputsplitter import InputSplitter
45 from IPython.core.inputsplitter import InputSplitter
46
46
47 isp = InputSplitter()
47 isp = InputSplitter()
48 # In practice, this input loop would be wrapped in an outside loop to read
48 # In practice, this input loop would be wrapped in an outside loop to read
49 # input indefinitely, until some exit/quit command was issued. Here we
49 # input indefinitely, until some exit/quit command was issued. Here we
50 # only illustrate the basic inner loop.
50 # only illustrate the basic inner loop.
51 while isp.push_accepts_more():
51 while isp.push_accepts_more():
52 indent = ' '*isp.indent_spaces
52 indent = ' '*isp.indent_spaces
53 prompt = '>>> ' + indent
53 prompt = '>>> ' + indent
54 line = indent + input_func(prompt)
54 line = indent + input_func(prompt)
55 isp.push(line)
55 isp.push(line)
56
56
57 # Here we just return input so we can use it in a test suite, but a real
57 # Here we just return input so we can use it in a test suite, but a real
58 # interpreter would instead send it for execution somewhere.
58 # interpreter would instead send it for execution somewhere.
59 src = isp.source_reset()
59 src = isp.source_reset()
60 #print 'Input source was:\n', src # dbg
60 #print 'Input source was:\n', src # dbg
61 return src
61 return src
62
62
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64 # Test utilities, just for local use
64 # Test utilities, just for local use
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66
66
67 def assemble(block):
67 def assemble(block):
68 """Assemble a block into multi-line sub-blocks."""
68 """Assemble a block into multi-line sub-blocks."""
69 return ['\n'.join(sub_block)+'\n' for sub_block in block]
69 return ['\n'.join(sub_block)+'\n' for sub_block in block]
70
70
71
71
72 def pseudo_input(lines):
72 def pseudo_input(lines):
73 """Return a function that acts like raw_input but feeds the input list."""
73 """Return a function that acts like raw_input but feeds the input list."""
74 ilines = iter(lines)
74 ilines = iter(lines)
75 def raw_in(prompt):
75 def raw_in(prompt):
76 try:
76 try:
77 return next(ilines)
77 return next(ilines)
78 except StopIteration:
78 except StopIteration:
79 return ''
79 return ''
80 return raw_in
80 return raw_in
81
81
82 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
83 # Tests
83 # Tests
84 #-----------------------------------------------------------------------------
84 #-----------------------------------------------------------------------------
85 def test_spaces():
85 def test_spaces():
86 tests = [('', 0),
86 tests = [('', 0),
87 (' ', 1),
87 (' ', 1),
88 ('\n', 0),
88 ('\n', 0),
89 (' \n', 1),
89 (' \n', 1),
90 ('x', 0),
90 ('x', 0),
91 (' x', 1),
91 (' x', 1),
92 (' x',2),
92 (' x',2),
93 (' x',4),
93 (' x',4),
94 # Note: tabs are counted as a single whitespace!
94 # Note: tabs are counted as a single whitespace!
95 ('\tx', 1),
95 ('\tx', 1),
96 ('\t x', 2),
96 ('\t x', 2),
97 ]
97 ]
98 tt.check_pairs(isp.num_ini_spaces, tests)
98 tt.check_pairs(isp.num_ini_spaces, tests)
99
99
100
100
101 def test_remove_comments():
101 def test_remove_comments():
102 tests = [('text', 'text'),
102 tests = [('text', 'text'),
103 ('text # comment', 'text '),
103 ('text # comment', 'text '),
104 ('text # comment\n', 'text \n'),
104 ('text # comment\n', 'text \n'),
105 ('text # comment \n', 'text \n'),
105 ('text # comment \n', 'text \n'),
106 ('line # c \nline\n','line \nline\n'),
106 ('line # c \nline\n','line \nline\n'),
107 ('line # c \nline#c2 \nline\nline #c\n\n',
107 ('line # c \nline#c2 \nline\nline #c\n\n',
108 'line \nline\nline\nline \n\n'),
108 'line \nline\nline\nline \n\n'),
109 ]
109 ]
110 tt.check_pairs(isp.remove_comments, tests)
110 tt.check_pairs(isp.remove_comments, tests)
111
111
112
112
113 def test_get_input_encoding():
113 def test_get_input_encoding():
114 encoding = isp.get_input_encoding()
114 encoding = isp.get_input_encoding()
115 nt.assert_true(isinstance(encoding, basestring))
115 nt.assert_true(isinstance(encoding, basestring))
116 # simple-minded check that at least encoding a simple string works with the
116 # simple-minded check that at least encoding a simple string works with the
117 # encoding we got.
117 # encoding we got.
118 nt.assert_equal(u'test'.encode(encoding), b'test')
118 nt.assert_equal(u'test'.encode(encoding), b'test')
119
119
120
120
121 class NoInputEncodingTestCase(unittest.TestCase):
121 class NoInputEncodingTestCase(unittest.TestCase):
122 def setUp(self):
122 def setUp(self):
123 self.old_stdin = sys.stdin
123 self.old_stdin = sys.stdin
124 class X: pass
124 class X: pass
125 fake_stdin = X()
125 fake_stdin = X()
126 sys.stdin = fake_stdin
126 sys.stdin = fake_stdin
127
127
128 def test(self):
128 def test(self):
129 # Verify that if sys.stdin has no 'encoding' attribute we do the right
129 # Verify that if sys.stdin has no 'encoding' attribute we do the right
130 # thing
130 # thing
131 enc = isp.get_input_encoding()
131 enc = isp.get_input_encoding()
132 self.assertEqual(enc, 'ascii')
132 self.assertEqual(enc, 'ascii')
133
133
134 def tearDown(self):
134 def tearDown(self):
135 sys.stdin = self.old_stdin
135 sys.stdin = self.old_stdin
136
136
137
137
138 class InputSplitterTestCase(unittest.TestCase):
138 class InputSplitterTestCase(unittest.TestCase):
139 def setUp(self):
139 def setUp(self):
140 self.isp = isp.InputSplitter()
140 self.isp = isp.InputSplitter()
141
141
142 def test_reset(self):
142 def test_reset(self):
143 isp = self.isp
143 isp = self.isp
144 isp.push('x=1')
144 isp.push('x=1')
145 isp.reset()
145 isp.reset()
146 self.assertEqual(isp._buffer, [])
146 self.assertEqual(isp._buffer, [])
147 self.assertEqual(isp.indent_spaces, 0)
147 self.assertEqual(isp.indent_spaces, 0)
148 self.assertEqual(isp.source, '')
148 self.assertEqual(isp.source, '')
149 self.assertEqual(isp.code, None)
149 self.assertEqual(isp.code, None)
150 self.assertEqual(isp._is_complete, False)
150 self.assertEqual(isp._is_complete, False)
151
151
152 def test_source(self):
152 def test_source(self):
153 self.isp._store('1')
153 self.isp._store('1')
154 self.isp._store('2')
154 self.isp._store('2')
155 self.assertEqual(self.isp.source, '1\n2\n')
155 self.assertEqual(self.isp.source, '1\n2\n')
156 self.assertTrue(len(self.isp._buffer)>0)
156 self.assertTrue(len(self.isp._buffer)>0)
157 self.assertEqual(self.isp.source_reset(), '1\n2\n')
157 self.assertEqual(self.isp.source_reset(), '1\n2\n')
158 self.assertEqual(self.isp._buffer, [])
158 self.assertEqual(self.isp._buffer, [])
159 self.assertEqual(self.isp.source, '')
159 self.assertEqual(self.isp.source, '')
160
160
161 def test_indent(self):
161 def test_indent(self):
162 isp = self.isp # shorthand
162 isp = self.isp # shorthand
163 isp.push('x=1')
163 isp.push('x=1')
164 self.assertEqual(isp.indent_spaces, 0)
164 self.assertEqual(isp.indent_spaces, 0)
165 isp.push('if 1:\n x=1')
165 isp.push('if 1:\n x=1')
166 self.assertEqual(isp.indent_spaces, 4)
166 self.assertEqual(isp.indent_spaces, 4)
167 isp.push('y=2\n')
167 isp.push('y=2\n')
168 self.assertEqual(isp.indent_spaces, 0)
168 self.assertEqual(isp.indent_spaces, 0)
169
169
170 def test_indent2(self):
170 def test_indent2(self):
171 # In cell mode, inputs must be fed in whole blocks, so skip this test
172 if self.isp.input_mode == 'cell': return
173
174 isp = self.isp
171 isp = self.isp
175 isp.push('if 1:')
172 isp.push('if 1:')
176 self.assertEqual(isp.indent_spaces, 4)
173 self.assertEqual(isp.indent_spaces, 4)
177 isp.push(' x=1')
174 isp.push(' x=1')
178 self.assertEqual(isp.indent_spaces, 4)
175 self.assertEqual(isp.indent_spaces, 4)
179 # Blank lines shouldn't change the indent level
176 # Blank lines shouldn't change the indent level
180 isp.push(' '*2)
177 isp.push(' '*2)
181 self.assertEqual(isp.indent_spaces, 4)
178 self.assertEqual(isp.indent_spaces, 4)
182
179
183 def test_indent3(self):
180 def test_indent3(self):
184 # In cell mode, inputs must be fed in whole blocks, so skip this test
185 if self.isp.input_mode == 'cell': return
186
187 isp = self.isp
181 isp = self.isp
188 # When a multiline statement contains parens or multiline strings, we
182 # When a multiline statement contains parens or multiline strings, we
189 # shouldn't get confused.
183 # shouldn't get confused.
190 isp.push("if 1:")
184 isp.push("if 1:")
191 isp.push(" x = (1+\n 2)")
185 isp.push(" x = (1+\n 2)")
192 self.assertEqual(isp.indent_spaces, 4)
186 self.assertEqual(isp.indent_spaces, 4)
193
187
194 def test_indent4(self):
188 def test_indent4(self):
195 # In cell mode, inputs must be fed in whole blocks, so skip this test
196 if self.isp.input_mode == 'cell': return
197
198 isp = self.isp
189 isp = self.isp
199 # whitespace after ':' should not screw up indent level
190 # whitespace after ':' should not screw up indent level
200 isp.push('if 1: \n x=1')
191 isp.push('if 1: \n x=1')
201 self.assertEqual(isp.indent_spaces, 4)
192 self.assertEqual(isp.indent_spaces, 4)
202 isp.push('y=2\n')
193 isp.push('y=2\n')
203 self.assertEqual(isp.indent_spaces, 0)
194 self.assertEqual(isp.indent_spaces, 0)
204 isp.push('if 1:\t\n x=1')
195 isp.push('if 1:\t\n x=1')
205 self.assertEqual(isp.indent_spaces, 4)
196 self.assertEqual(isp.indent_spaces, 4)
206 isp.push('y=2\n')
197 isp.push('y=2\n')
207 self.assertEqual(isp.indent_spaces, 0)
198 self.assertEqual(isp.indent_spaces, 0)
208
199
209 def test_dedent_pass(self):
200 def test_dedent_pass(self):
210 isp = self.isp # shorthand
201 isp = self.isp # shorthand
211 # should NOT cause dedent
202 # should NOT cause dedent
212 isp.push('if 1:\n passes = 5')
203 isp.push('if 1:\n passes = 5')
213 self.assertEqual(isp.indent_spaces, 4)
204 self.assertEqual(isp.indent_spaces, 4)
214 isp.push('if 1:\n pass')
205 isp.push('if 1:\n pass')
215 self.assertEqual(isp.indent_spaces, 0)
206 self.assertEqual(isp.indent_spaces, 0)
216 isp.push('if 1:\n pass ')
207 isp.push('if 1:\n pass ')
217 self.assertEqual(isp.indent_spaces, 0)
208 self.assertEqual(isp.indent_spaces, 0)
218
209
219 def test_dedent_break(self):
210 def test_dedent_break(self):
220 isp = self.isp # shorthand
211 isp = self.isp # shorthand
221 # should NOT cause dedent
212 # should NOT cause dedent
222 isp.push('while 1:\n breaks = 5')
213 isp.push('while 1:\n breaks = 5')
223 self.assertEqual(isp.indent_spaces, 4)
214 self.assertEqual(isp.indent_spaces, 4)
224 isp.push('while 1:\n break')
215 isp.push('while 1:\n break')
225 self.assertEqual(isp.indent_spaces, 0)
216 self.assertEqual(isp.indent_spaces, 0)
226 isp.push('while 1:\n break ')
217 isp.push('while 1:\n break ')
227 self.assertEqual(isp.indent_spaces, 0)
218 self.assertEqual(isp.indent_spaces, 0)
228
219
229 def test_dedent_continue(self):
220 def test_dedent_continue(self):
230 isp = self.isp # shorthand
221 isp = self.isp # shorthand
231 # should NOT cause dedent
222 # should NOT cause dedent
232 isp.push('while 1:\n continues = 5')
223 isp.push('while 1:\n continues = 5')
233 self.assertEqual(isp.indent_spaces, 4)
224 self.assertEqual(isp.indent_spaces, 4)
234 isp.push('while 1:\n continue')
225 isp.push('while 1:\n continue')
235 self.assertEqual(isp.indent_spaces, 0)
226 self.assertEqual(isp.indent_spaces, 0)
236 isp.push('while 1:\n continue ')
227 isp.push('while 1:\n continue ')
237 self.assertEqual(isp.indent_spaces, 0)
228 self.assertEqual(isp.indent_spaces, 0)
238
229
239 def test_dedent_raise(self):
230 def test_dedent_raise(self):
240 isp = self.isp # shorthand
231 isp = self.isp # shorthand
241 # should NOT cause dedent
232 # should NOT cause dedent
242 isp.push('if 1:\n raised = 4')
233 isp.push('if 1:\n raised = 4')
243 self.assertEqual(isp.indent_spaces, 4)
234 self.assertEqual(isp.indent_spaces, 4)
244 isp.push('if 1:\n raise TypeError()')
235 isp.push('if 1:\n raise TypeError()')
245 self.assertEqual(isp.indent_spaces, 0)
236 self.assertEqual(isp.indent_spaces, 0)
246 isp.push('if 1:\n raise')
237 isp.push('if 1:\n raise')
247 self.assertEqual(isp.indent_spaces, 0)
238 self.assertEqual(isp.indent_spaces, 0)
248 isp.push('if 1:\n raise ')
239 isp.push('if 1:\n raise ')
249 self.assertEqual(isp.indent_spaces, 0)
240 self.assertEqual(isp.indent_spaces, 0)
250
241
251 def test_dedent_return(self):
242 def test_dedent_return(self):
252 isp = self.isp # shorthand
243 isp = self.isp # shorthand
253 # should NOT cause dedent
244 # should NOT cause dedent
254 isp.push('if 1:\n returning = 4')
245 isp.push('if 1:\n returning = 4')
255 self.assertEqual(isp.indent_spaces, 4)
246 self.assertEqual(isp.indent_spaces, 4)
256 isp.push('if 1:\n return 5 + 493')
247 isp.push('if 1:\n return 5 + 493')
257 self.assertEqual(isp.indent_spaces, 0)
248 self.assertEqual(isp.indent_spaces, 0)
258 isp.push('if 1:\n return')
249 isp.push('if 1:\n return')
259 self.assertEqual(isp.indent_spaces, 0)
250 self.assertEqual(isp.indent_spaces, 0)
260 isp.push('if 1:\n return ')
251 isp.push('if 1:\n return ')
261 self.assertEqual(isp.indent_spaces, 0)
252 self.assertEqual(isp.indent_spaces, 0)
262 isp.push('if 1:\n return(0)')
253 isp.push('if 1:\n return(0)')
263 self.assertEqual(isp.indent_spaces, 0)
254 self.assertEqual(isp.indent_spaces, 0)
264
255
265 def test_push(self):
256 def test_push(self):
266 isp = self.isp
257 isp = self.isp
267 self.assertTrue(isp.push('x=1'))
258 self.assertTrue(isp.push('x=1'))
268
259
269 def test_push2(self):
260 def test_push2(self):
270 isp = self.isp
261 isp = self.isp
271 self.assertFalse(isp.push('if 1:'))
262 self.assertFalse(isp.push('if 1:'))
272 for line in [' x=1', '# a comment', ' y=2']:
263 for line in [' x=1', '# a comment', ' y=2']:
273 print(line)
264 print(line)
274 self.assertTrue(isp.push(line))
265 self.assertTrue(isp.push(line))
275
266
276 def test_push3(self):
267 def test_push3(self):
277 isp = self.isp
268 isp = self.isp
278 isp.push('if True:')
269 isp.push('if True:')
279 isp.push(' a = 1')
270 isp.push(' a = 1')
280 self.assertFalse(isp.push('b = [1,'))
271 self.assertFalse(isp.push('b = [1,'))
281
272
282 def test_replace_mode(self):
283 isp = self.isp
284 isp.input_mode = 'cell'
285 isp.push('x=1')
286 self.assertEqual(isp.source, 'x=1\n')
287 isp.push('x=2')
288 self.assertEqual(isp.source, 'x=2\n')
289
290 def test_push_accepts_more(self):
273 def test_push_accepts_more(self):
291 isp = self.isp
274 isp = self.isp
292 isp.push('x=1')
275 isp.push('x=1')
293 self.assertFalse(isp.push_accepts_more())
276 self.assertFalse(isp.push_accepts_more())
294
277
295 def test_push_accepts_more2(self):
278 def test_push_accepts_more2(self):
296 # In cell mode, inputs must be fed in whole blocks, so skip this test
297 if self.isp.input_mode == 'cell': return
298
299 isp = self.isp
279 isp = self.isp
300 isp.push('if 1:')
280 isp.push('if 1:')
301 self.assertTrue(isp.push_accepts_more())
281 self.assertTrue(isp.push_accepts_more())
302 isp.push(' x=1')
282 isp.push(' x=1')
303 self.assertTrue(isp.push_accepts_more())
283 self.assertTrue(isp.push_accepts_more())
304 isp.push('')
284 isp.push('')
305 self.assertFalse(isp.push_accepts_more())
285 self.assertFalse(isp.push_accepts_more())
306
286
307 def test_push_accepts_more3(self):
287 def test_push_accepts_more3(self):
308 isp = self.isp
288 isp = self.isp
309 isp.push("x = (2+\n3)")
289 isp.push("x = (2+\n3)")
310 self.assertFalse(isp.push_accepts_more())
290 self.assertFalse(isp.push_accepts_more())
311
291
312 def test_push_accepts_more4(self):
292 def test_push_accepts_more4(self):
313 # In cell mode, inputs must be fed in whole blocks, so skip this test
314 if self.isp.input_mode == 'cell': return
315
316 isp = self.isp
293 isp = self.isp
317 # When a multiline statement contains parens or multiline strings, we
294 # When a multiline statement contains parens or multiline strings, we
318 # shouldn't get confused.
295 # shouldn't get confused.
319 # FIXME: we should be able to better handle de-dents in statements like
296 # FIXME: we should be able to better handle de-dents in statements like
320 # multiline strings and multiline expressions (continued with \ or
297 # multiline strings and multiline expressions (continued with \ or
321 # parens). Right now we aren't handling the indentation tracking quite
298 # parens). Right now we aren't handling the indentation tracking quite
322 # correctly with this, though in practice it may not be too much of a
299 # correctly with this, though in practice it may not be too much of a
323 # problem. We'll need to see.
300 # problem. We'll need to see.
324 isp.push("if 1:")
301 isp.push("if 1:")
325 isp.push(" x = (2+")
302 isp.push(" x = (2+")
326 isp.push(" 3)")
303 isp.push(" 3)")
327 self.assertTrue(isp.push_accepts_more())
304 self.assertTrue(isp.push_accepts_more())
328 isp.push(" y = 3")
305 isp.push(" y = 3")
329 self.assertTrue(isp.push_accepts_more())
306 self.assertTrue(isp.push_accepts_more())
330 isp.push('')
307 isp.push('')
331 self.assertFalse(isp.push_accepts_more())
308 self.assertFalse(isp.push_accepts_more())
332
309
333 def test_push_accepts_more5(self):
310 def test_push_accepts_more5(self):
334 # In cell mode, inputs must be fed in whole blocks, so skip this test
335 if self.isp.input_mode == 'cell': return
336
337 isp = self.isp
311 isp = self.isp
338 isp.push('try:')
312 isp.push('try:')
339 isp.push(' a = 5')
313 isp.push(' a = 5')
340 isp.push('except:')
314 isp.push('except:')
341 isp.push(' raise')
315 isp.push(' raise')
316 # We want to be able to add an else: block at this point, so it should
317 # wait for a blank line.
342 self.assertTrue(isp.push_accepts_more())
318 self.assertTrue(isp.push_accepts_more())
343
319
344 def test_continuation(self):
320 def test_continuation(self):
345 isp = self.isp
321 isp = self.isp
346 isp.push("import os, \\")
322 isp.push("import os, \\")
347 self.assertTrue(isp.push_accepts_more())
323 self.assertTrue(isp.push_accepts_more())
348 isp.push("sys")
324 isp.push("sys")
349 self.assertFalse(isp.push_accepts_more())
325 self.assertFalse(isp.push_accepts_more())
350
326
351 def test_syntax_error(self):
327 def test_syntax_error(self):
352 isp = self.isp
328 isp = self.isp
353 # Syntax errors immediately produce a 'ready' block, so the invalid
329 # Syntax errors immediately produce a 'ready' block, so the invalid
354 # Python can be sent to the kernel for evaluation with possible ipython
330 # Python can be sent to the kernel for evaluation with possible ipython
355 # special-syntax conversion.
331 # special-syntax conversion.
356 isp.push('run foo')
332 isp.push('run foo')
357 self.assertFalse(isp.push_accepts_more())
333 self.assertFalse(isp.push_accepts_more())
358
334
359 def test_unicode(self):
335 def test_unicode(self):
360 self.isp.push(u"PΓ©rez")
336 self.isp.push(u"PΓ©rez")
361 self.isp.push(u'\xc3\xa9')
337 self.isp.push(u'\xc3\xa9')
362 self.isp.push(u"u'\xc3\xa9'")
338 self.isp.push(u"u'\xc3\xa9'")
363
339
364 def test_line_continuation(self):
340 def test_line_continuation(self):
365 """ Test issue #2108."""
341 """ Test issue #2108."""
366 isp = self.isp
342 isp = self.isp
367 # A blank line after a line continuation should not accept more
343 # A blank line after a line continuation should not accept more
368 isp.push("1 \\\n\n")
344 isp.push("1 \\\n\n")
369 self.assertFalse(isp.push_accepts_more())
345 self.assertFalse(isp.push_accepts_more())
370 # Whitespace after a \ is a SyntaxError. The only way to test that
346 # Whitespace after a \ is a SyntaxError. The only way to test that
371 # here is to test that push doesn't accept more (as with
347 # here is to test that push doesn't accept more (as with
372 # test_syntax_error() above).
348 # test_syntax_error() above).
373 isp.push(r"1 \ ")
349 isp.push(r"1 \ ")
374 self.assertFalse(isp.push_accepts_more())
350 self.assertFalse(isp.push_accepts_more())
375 # Even if the line is continuable (c.f. the regular Python
351 # Even if the line is continuable (c.f. the regular Python
376 # interpreter)
352 # interpreter)
377 isp.push(r"(1 \ ")
353 isp.push(r"(1 \ ")
378 self.assertFalse(isp.push_accepts_more())
354 self.assertFalse(isp.push_accepts_more())
379
355
380 class InteractiveLoopTestCase(unittest.TestCase):
356 class InteractiveLoopTestCase(unittest.TestCase):
381 """Tests for an interactive loop like a python shell.
357 """Tests for an interactive loop like a python shell.
382 """
358 """
383 def check_ns(self, lines, ns):
359 def check_ns(self, lines, ns):
384 """Validate that the given input lines produce the resulting namespace.
360 """Validate that the given input lines produce the resulting namespace.
385
361
386 Note: the input lines are given exactly as they would be typed in an
362 Note: the input lines are given exactly as they would be typed in an
387 auto-indenting environment, as mini_interactive_loop above already does
363 auto-indenting environment, as mini_interactive_loop above already does
388 auto-indenting and prepends spaces to the input.
364 auto-indenting and prepends spaces to the input.
389 """
365 """
390 src = mini_interactive_loop(pseudo_input(lines))
366 src = mini_interactive_loop(pseudo_input(lines))
391 test_ns = {}
367 test_ns = {}
392 exec src in test_ns
368 exec src in test_ns
393 # We can't check that the provided ns is identical to the test_ns,
369 # We can't check that the provided ns is identical to the test_ns,
394 # because Python fills test_ns with extra keys (copyright, etc). But
370 # because Python fills test_ns with extra keys (copyright, etc). But
395 # we can check that the given dict is *contained* in test_ns
371 # we can check that the given dict is *contained* in test_ns
396 for k,v in ns.iteritems():
372 for k,v in ns.iteritems():
397 self.assertEqual(test_ns[k], v)
373 self.assertEqual(test_ns[k], v)
398
374
399 def test_simple(self):
375 def test_simple(self):
400 self.check_ns(['x=1'], dict(x=1))
376 self.check_ns(['x=1'], dict(x=1))
401
377
402 def test_simple2(self):
378 def test_simple2(self):
403 self.check_ns(['if 1:', 'x=2'], dict(x=2))
379 self.check_ns(['if 1:', 'x=2'], dict(x=2))
404
380
405 def test_xy(self):
381 def test_xy(self):
406 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
382 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
407
383
408 def test_abc(self):
384 def test_abc(self):
409 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
385 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
410
386
411 def test_multi(self):
387 def test_multi(self):
412 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
388 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
413
389
414
390
415 def test_LineInfo():
391 def test_LineInfo():
416 """Simple test for LineInfo construction and str()"""
392 """Simple test for LineInfo construction and str()"""
417 linfo = isp.LineInfo(' %cd /home')
393 linfo = isp.LineInfo(' %cd /home')
418 nt.assert_equal(str(linfo), 'LineInfo [ |%|cd|/home]')
394 nt.assert_equal(str(linfo), 'LineInfo [ |%|cd|/home]')
419
395
420
396
421
397
422
398
423 class IPythonInputTestCase(InputSplitterTestCase):
399 class IPythonInputTestCase(InputSplitterTestCase):
424 """By just creating a new class whose .isp is a different instance, we
400 """By just creating a new class whose .isp is a different instance, we
425 re-run the same test battery on the new input splitter.
401 re-run the same test battery on the new input splitter.
426
402
427 In addition, this runs the tests over the syntax and syntax_ml dicts that
403 In addition, this runs the tests over the syntax and syntax_ml dicts that
428 were tested by individual functions, as part of the OO interface.
404 were tested by individual functions, as part of the OO interface.
429
405
430 It also makes some checks on the raw buffer storage.
406 It also makes some checks on the raw buffer storage.
431 """
407 """
432
408
433 def setUp(self):
409 def setUp(self):
434 self.isp = isp.IPythonInputSplitter(input_mode='line')
410 self.isp = isp.IPythonInputSplitter()
435
411
436 def test_syntax(self):
412 def test_syntax(self):
437 """Call all single-line syntax tests from the main object"""
413 """Call all single-line syntax tests from the main object"""
438 isp = self.isp
414 isp = self.isp
439 for example in syntax.itervalues():
415 for example in syntax.itervalues():
440 for raw, out_t in example:
416 for raw, out_t in example:
441 if raw.startswith(' '):
417 if raw.startswith(' '):
442 continue
418 continue
443
419
444 isp.push(raw+'\n')
420 isp.push(raw+'\n')
445 out, out_raw = isp.source_raw_reset()
421 out, out_raw = isp.source_raw_reset()
446 self.assertEqual(out.rstrip(), out_t,
422 self.assertEqual(out.rstrip(), out_t,
447 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
423 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
448 self.assertEqual(out_raw.rstrip(), raw.rstrip())
424 self.assertEqual(out_raw.rstrip(), raw.rstrip())
449
425
450 def test_syntax_multiline(self):
426 def test_syntax_multiline(self):
451 isp = self.isp
427 isp = self.isp
452 for example in syntax_ml.itervalues():
428 for example in syntax_ml.itervalues():
453 for line_pairs in example:
429 for line_pairs in example:
454 out_t_parts = []
430 out_t_parts = []
455 raw_parts = []
431 raw_parts = []
456 for lraw, out_t_part in line_pairs:
432 for lraw, out_t_part in line_pairs:
457 if out_t_part is not None:
433 if out_t_part is not None:
458 out_t_parts.append(out_t_part)
434 out_t_parts.append(out_t_part)
459
435
460 if lraw is not None:
436 if lraw is not None:
461 isp.push(lraw)
437 isp.push(lraw)
462 raw_parts.append(lraw)
438 raw_parts.append(lraw)
463
439
464 out, out_raw = isp.source_raw_reset()
440 out, out_raw = isp.source_raw_reset()
465 out_t = '\n'.join(out_t_parts).rstrip()
441 out_t = '\n'.join(out_t_parts).rstrip()
466 raw = '\n'.join(raw_parts).rstrip()
442 raw = '\n'.join(raw_parts).rstrip()
467 self.assertEqual(out.rstrip(), out_t)
443 self.assertEqual(out.rstrip(), out_t)
468 self.assertEqual(out_raw.rstrip(), raw)
444 self.assertEqual(out_raw.rstrip(), raw)
469
445
470
471 class BlockIPythonInputTestCase(IPythonInputTestCase):
472
473 # Deactivate tests that don't make sense for the block mode
474 test_push3 = test_split = lambda s: None
475
476 def setUp(self):
477 self.isp = isp.IPythonInputSplitter(input_mode='cell')
478
479 def test_syntax_multiline(self):
480 isp = self.isp
481 for example in syntax_ml.itervalues():
482 raw_parts = []
483 out_t_parts = []
484 for line_pairs in example:
485 raw_parts, out_t_parts = zip(*line_pairs)
486
487 raw = '\n'.join(r for r in raw_parts if r is not None)
488 out_t = '\n'.join(o for o in out_t_parts if o is not None)
489
490 isp.push(raw)
491 out, out_raw = isp.source_raw_reset()
492 # Match ignoring trailing whitespace
493 self.assertEqual(out.rstrip(), out_t.rstrip())
494 self.assertEqual(out_raw.rstrip(), raw.rstrip())
495
496 def test_syntax_multiline_cell(self):
446 def test_syntax_multiline_cell(self):
497 isp = self.isp
447 isp = self.isp
498 for example in syntax_ml.itervalues():
448 for example in syntax_ml.itervalues():
499
449
500 out_t_parts = []
450 out_t_parts = []
501 for line_pairs in example:
451 for line_pairs in example:
502 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
452 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
503 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
453 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
504 out = isp.transform_cell(raw)
454 out = isp.transform_cell(raw)
505 # Match ignoring trailing whitespace
455 # Match ignoring trailing whitespace
506 self.assertEqual(out.rstrip(), out_t.rstrip())
456 self.assertEqual(out.rstrip(), out_t.rstrip())
507
457
508 #-----------------------------------------------------------------------------
458 #-----------------------------------------------------------------------------
509 # Main - use as a script, mostly for developer experiments
459 # Main - use as a script, mostly for developer experiments
510 #-----------------------------------------------------------------------------
460 #-----------------------------------------------------------------------------
511
461
512 if __name__ == '__main__':
462 if __name__ == '__main__':
513 # A simple demo for interactive experimentation. This code will not get
463 # A simple demo for interactive experimentation. This code will not get
514 # picked up by any test suite.
464 # picked up by any test suite.
515 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
465 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
516
466
517 # configure here the syntax to use, prompt and whether to autoindent
467 # configure here the syntax to use, prompt and whether to autoindent
518 #isp, start_prompt = InputSplitter(), '>>> '
468 #isp, start_prompt = InputSplitter(), '>>> '
519 isp, start_prompt = IPythonInputSplitter(), 'In> '
469 isp, start_prompt = IPythonInputSplitter(), 'In> '
520
470
521 autoindent = True
471 autoindent = True
522 #autoindent = False
472 #autoindent = False
523
473
524 try:
474 try:
525 while True:
475 while True:
526 prompt = start_prompt
476 prompt = start_prompt
527 while isp.push_accepts_more():
477 while isp.push_accepts_more():
528 indent = ' '*isp.indent_spaces
478 indent = ' '*isp.indent_spaces
529 if autoindent:
479 if autoindent:
530 line = indent + raw_input(prompt+indent)
480 line = indent + raw_input(prompt+indent)
531 else:
481 else:
532 line = raw_input(prompt)
482 line = raw_input(prompt)
533 isp.push(line)
483 isp.push(line)
534 prompt = '... '
484 prompt = '... '
535
485
536 # Here we just return input so we can use it in a test suite, but a
486 # Here we just return input so we can use it in a test suite, but a
537 # real interpreter would instead send it for execution somewhere.
487 # real interpreter would instead send it for execution somewhere.
538 #src = isp.source; raise EOFError # dbg
488 #src = isp.source; raise EOFError # dbg
539 src, raw = isp.source_raw_reset()
489 src, raw = isp.source_raw_reset()
540 print 'Input source was:\n', src
490 print 'Input source was:\n', src
541 print 'Raw source was:\n', raw
491 print 'Raw source was:\n', raw
542 except EOFError:
492 except EOFError:
543 print 'Bye'
493 print 'Bye'
544
494
545 # Tests for cell magics support
495 # Tests for cell magics support
546
496
547 def test_last_blank():
497 def test_last_blank():
548 nt.assert_false(isp.last_blank(''))
498 nt.assert_false(isp.last_blank(''))
549 nt.assert_false(isp.last_blank('abc'))
499 nt.assert_false(isp.last_blank('abc'))
550 nt.assert_false(isp.last_blank('abc\n'))
500 nt.assert_false(isp.last_blank('abc\n'))
551 nt.assert_false(isp.last_blank('abc\na'))
501 nt.assert_false(isp.last_blank('abc\na'))
552
502
553 nt.assert_true(isp.last_blank('\n'))
503 nt.assert_true(isp.last_blank('\n'))
554 nt.assert_true(isp.last_blank('\n '))
504 nt.assert_true(isp.last_blank('\n '))
555 nt.assert_true(isp.last_blank('abc\n '))
505 nt.assert_true(isp.last_blank('abc\n '))
556 nt.assert_true(isp.last_blank('abc\n\n'))
506 nt.assert_true(isp.last_blank('abc\n\n'))
557 nt.assert_true(isp.last_blank('abc\nd\n\n'))
507 nt.assert_true(isp.last_blank('abc\nd\n\n'))
558 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
508 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
559 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
509 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
560
510
561
511
562 def test_last_two_blanks():
512 def test_last_two_blanks():
563 nt.assert_false(isp.last_two_blanks(''))
513 nt.assert_false(isp.last_two_blanks(''))
564 nt.assert_false(isp.last_two_blanks('abc'))
514 nt.assert_false(isp.last_two_blanks('abc'))
565 nt.assert_false(isp.last_two_blanks('abc\n'))
515 nt.assert_false(isp.last_two_blanks('abc\n'))
566 nt.assert_false(isp.last_two_blanks('abc\n\na'))
516 nt.assert_false(isp.last_two_blanks('abc\n\na'))
567 nt.assert_false(isp.last_two_blanks('abc\n \n'))
517 nt.assert_false(isp.last_two_blanks('abc\n \n'))
568 nt.assert_false(isp.last_two_blanks('abc\n\n'))
518 nt.assert_false(isp.last_two_blanks('abc\n\n'))
569
519
570 nt.assert_true(isp.last_two_blanks('\n\n'))
520 nt.assert_true(isp.last_two_blanks('\n\n'))
571 nt.assert_true(isp.last_two_blanks('\n\n '))
521 nt.assert_true(isp.last_two_blanks('\n\n '))
572 nt.assert_true(isp.last_two_blanks('\n \n'))
522 nt.assert_true(isp.last_two_blanks('\n \n'))
573 nt.assert_true(isp.last_two_blanks('abc\n\n '))
523 nt.assert_true(isp.last_two_blanks('abc\n\n '))
574 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
524 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
575 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
525 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
576 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
526 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
577 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
527 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
578 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
528 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
579 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
529 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
580
530
581
531
582 class CellMagicsCommon(object):
532 class CellMagicsCommon(object):
583
533
584 def test_whole_cell(self):
534 def test_whole_cell(self):
585 src = "%%cellm line\nbody\n"
535 src = "%%cellm line\nbody\n"
586 sp = self.sp
536 sp = self.sp
587 sp.push(src)
537 sp.push(src)
588 out = sp.source_reset()
538 out = sp.source_reset()
589 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
539 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
590 nt.assert_equal(out, py3compat.u_format(ref))
540 nt.assert_equal(out, py3compat.u_format(ref))
591
541
592 def tearDown(self):
542 def tearDown(self):
593 self.sp.reset()
543 self.sp.reset()
594
544
595
545
596 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
546 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
597 sp = isp.IPythonInputSplitter(input_mode='cell')
547 sp = isp.IPythonInputSplitter(input_mode='cell')
598
548
599 def test_incremental(self):
549 def test_incremental(self):
600 sp = self.sp
550 sp = self.sp
601 src = '%%cellm line2\n'
551 src = '%%cellm line2\n'
602 sp.push(src)
552 sp.push(src)
603 nt.assert_true(sp.push_accepts_more()) #1
553 nt.assert_true(sp.push_accepts_more()) #1
604 src += '\n'
554 src += '\n'
605 sp.push(src)
555 sp.push(src)
606 # Note: if we ever change the logic to allow full blank lines (see
556 # Note: if we ever change the logic to allow full blank lines (see
607 # _handle_cell_magic), then the following test should change to true
557 # _handle_cell_magic), then the following test should change to true
608 nt.assert_false(sp.push_accepts_more()) #2
558 nt.assert_false(sp.push_accepts_more()) #2
609 # By now, even with full blanks allowed, a second blank should signal
559 # By now, even with full blanks allowed, a second blank should signal
610 # the end. For now this test is only a redundancy safety, but don't
560 # the end. For now this test is only a redundancy safety, but don't
611 # delete it in case we change our mind and the previous one goes to
561 # delete it in case we change our mind and the previous one goes to
612 # true.
562 # true.
613 src += '\n'
563 src += '\n'
614 sp.push(src)
564 sp.push(src)
615 nt.assert_false(sp.push_accepts_more()) #3
565 nt.assert_false(sp.push_accepts_more()) #3
616
566
617
567
618 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
568 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
619 sp = isp.IPythonInputSplitter(input_mode='line')
569 sp = isp.IPythonInputSplitter(input_mode='line')
620
570
621 def test_incremental(self):
571 def test_incremental(self):
622 sp = self.sp
572 sp = self.sp
623 sp.push('%%cellm line2\n')
573 sp.push('%%cellm line2\n')
624 nt.assert_true(sp.push_accepts_more()) #1
574 nt.assert_true(sp.push_accepts_more()) #1
625 sp.push('\n')
575 sp.push('\n')
626 nt.assert_false(sp.push_accepts_more()) #2
576 nt.assert_false(sp.push_accepts_more()) #2
@@ -1,770 +1,771 b''
1 from __future__ import print_function
1 from __future__ import print_function
2
2
3 # Standard library imports
3 # Standard library imports
4 from collections import namedtuple
4 from collections import namedtuple
5 import sys
5 import sys
6 import time
6 import time
7 import uuid
7 import uuid
8
8
9 # System library imports
9 # System library imports
10 from pygments.lexers import PythonLexer
10 from pygments.lexers import PythonLexer
11 from IPython.external import qt
11 from IPython.external import qt
12 from IPython.external.qt import QtCore, QtGui
12 from IPython.external.qt import QtCore, QtGui
13
13
14 # Local imports
14 # Local imports
15 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
15 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
16 from IPython.core.inputtransformer import classic_prompt
16 from IPython.core.inputtransformer import classic_prompt
17 from IPython.core.oinspect import call_tip
17 from IPython.core.oinspect import call_tip
18 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
18 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
19 from IPython.utils.traitlets import Bool, Instance, Unicode
19 from IPython.utils.traitlets import Bool, Instance, Unicode
20 from bracket_matcher import BracketMatcher
20 from bracket_matcher import BracketMatcher
21 from call_tip_widget import CallTipWidget
21 from call_tip_widget import CallTipWidget
22 from completion_lexer import CompletionLexer
22 from completion_lexer import CompletionLexer
23 from history_console_widget import HistoryConsoleWidget
23 from history_console_widget import HistoryConsoleWidget
24 from pygments_highlighter import PygmentsHighlighter
24 from pygments_highlighter import PygmentsHighlighter
25
25
26
26
27 class FrontendHighlighter(PygmentsHighlighter):
27 class FrontendHighlighter(PygmentsHighlighter):
28 """ A PygmentsHighlighter that understands and ignores prompts.
28 """ A PygmentsHighlighter that understands and ignores prompts.
29 """
29 """
30
30
31 def __init__(self, frontend):
31 def __init__(self, frontend):
32 super(FrontendHighlighter, self).__init__(frontend._control.document())
32 super(FrontendHighlighter, self).__init__(frontend._control.document())
33 self._current_offset = 0
33 self._current_offset = 0
34 self._frontend = frontend
34 self._frontend = frontend
35 self.highlighting_on = False
35 self.highlighting_on = False
36
36
37 def highlightBlock(self, string):
37 def highlightBlock(self, string):
38 """ Highlight a block of text. Reimplemented to highlight selectively.
38 """ Highlight a block of text. Reimplemented to highlight selectively.
39 """
39 """
40 if not self.highlighting_on:
40 if not self.highlighting_on:
41 return
41 return
42
42
43 # The input to this function is a unicode string that may contain
43 # The input to this function is a unicode string that may contain
44 # paragraph break characters, non-breaking spaces, etc. Here we acquire
44 # paragraph break characters, non-breaking spaces, etc. Here we acquire
45 # the string as plain text so we can compare it.
45 # the string as plain text so we can compare it.
46 current_block = self.currentBlock()
46 current_block = self.currentBlock()
47 string = self._frontend._get_block_plain_text(current_block)
47 string = self._frontend._get_block_plain_text(current_block)
48
48
49 # Decide whether to check for the regular or continuation prompt.
49 # Decide whether to check for the regular or continuation prompt.
50 if current_block.contains(self._frontend._prompt_pos):
50 if current_block.contains(self._frontend._prompt_pos):
51 prompt = self._frontend._prompt
51 prompt = self._frontend._prompt
52 else:
52 else:
53 prompt = self._frontend._continuation_prompt
53 prompt = self._frontend._continuation_prompt
54
54
55 # Only highlight if we can identify a prompt, but make sure not to
55 # Only highlight if we can identify a prompt, but make sure not to
56 # highlight the prompt.
56 # highlight the prompt.
57 if string.startswith(prompt):
57 if string.startswith(prompt):
58 self._current_offset = len(prompt)
58 self._current_offset = len(prompt)
59 string = string[len(prompt):]
59 string = string[len(prompt):]
60 super(FrontendHighlighter, self).highlightBlock(string)
60 super(FrontendHighlighter, self).highlightBlock(string)
61
61
62 def rehighlightBlock(self, block):
62 def rehighlightBlock(self, block):
63 """ Reimplemented to temporarily enable highlighting if disabled.
63 """ Reimplemented to temporarily enable highlighting if disabled.
64 """
64 """
65 old = self.highlighting_on
65 old = self.highlighting_on
66 self.highlighting_on = True
66 self.highlighting_on = True
67 super(FrontendHighlighter, self).rehighlightBlock(block)
67 super(FrontendHighlighter, self).rehighlightBlock(block)
68 self.highlighting_on = old
68 self.highlighting_on = old
69
69
70 def setFormat(self, start, count, format):
70 def setFormat(self, start, count, format):
71 """ Reimplemented to highlight selectively.
71 """ Reimplemented to highlight selectively.
72 """
72 """
73 start += self._current_offset
73 start += self._current_offset
74 super(FrontendHighlighter, self).setFormat(start, count, format)
74 super(FrontendHighlighter, self).setFormat(start, count, format)
75
75
76
76
77 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
77 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
78 """ A Qt frontend for a generic Python kernel.
78 """ A Qt frontend for a generic Python kernel.
79 """
79 """
80
80
81 # The text to show when the kernel is (re)started.
81 # The text to show when the kernel is (re)started.
82 banner = Unicode()
82 banner = Unicode()
83
83
84 # An option and corresponding signal for overriding the default kernel
84 # An option and corresponding signal for overriding the default kernel
85 # interrupt behavior.
85 # interrupt behavior.
86 custom_interrupt = Bool(False)
86 custom_interrupt = Bool(False)
87 custom_interrupt_requested = QtCore.Signal()
87 custom_interrupt_requested = QtCore.Signal()
88
88
89 # An option and corresponding signals for overriding the default kernel
89 # An option and corresponding signals for overriding the default kernel
90 # restart behavior.
90 # restart behavior.
91 custom_restart = Bool(False)
91 custom_restart = Bool(False)
92 custom_restart_kernel_died = QtCore.Signal(float)
92 custom_restart_kernel_died = QtCore.Signal(float)
93 custom_restart_requested = QtCore.Signal()
93 custom_restart_requested = QtCore.Signal()
94
94
95 # Whether to automatically show calltips on open-parentheses.
95 # Whether to automatically show calltips on open-parentheses.
96 enable_calltips = Bool(True, config=True,
96 enable_calltips = Bool(True, config=True,
97 help="Whether to draw information calltips on open-parentheses.")
97 help="Whether to draw information calltips on open-parentheses.")
98
98
99 clear_on_kernel_restart = Bool(True, config=True,
99 clear_on_kernel_restart = Bool(True, config=True,
100 help="Whether to clear the console when the kernel is restarted")
100 help="Whether to clear the console when the kernel is restarted")
101
101
102 confirm_restart = Bool(True, config=True,
102 confirm_restart = Bool(True, config=True,
103 help="Whether to ask for user confirmation when restarting kernel")
103 help="Whether to ask for user confirmation when restarting kernel")
104
104
105 # Emitted when a user visible 'execute_request' has been submitted to the
105 # Emitted when a user visible 'execute_request' has been submitted to the
106 # kernel from the FrontendWidget. Contains the code to be executed.
106 # kernel from the FrontendWidget. Contains the code to be executed.
107 executing = QtCore.Signal(object)
107 executing = QtCore.Signal(object)
108
108
109 # Emitted when a user-visible 'execute_reply' has been received from the
109 # Emitted when a user-visible 'execute_reply' has been received from the
110 # kernel and processed by the FrontendWidget. Contains the response message.
110 # kernel and processed by the FrontendWidget. Contains the response message.
111 executed = QtCore.Signal(object)
111 executed = QtCore.Signal(object)
112
112
113 # Emitted when an exit request has been received from the kernel.
113 # Emitted when an exit request has been received from the kernel.
114 exit_requested = QtCore.Signal(object)
114 exit_requested = QtCore.Signal(object)
115
115
116 # Protected class variables.
116 # Protected class variables.
117 _prompt_transformer = IPythonInputSplitter(physical_line_transforms=[classic_prompt()],
117 _prompt_transformer = IPythonInputSplitter(physical_line_transforms=[classic_prompt()],
118 logical_line_transforms=[],
118 logical_line_transforms=[],
119 python_line_transforms=[],
119 python_line_transforms=[],
120 )
120 )
121 _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
121 _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
122 _CompletionRequest = namedtuple('_CompletionRequest', ['id', 'pos'])
122 _CompletionRequest = namedtuple('_CompletionRequest', ['id', 'pos'])
123 _ExecutionRequest = namedtuple('_ExecutionRequest', ['id', 'kind'])
123 _ExecutionRequest = namedtuple('_ExecutionRequest', ['id', 'kind'])
124 _input_splitter_class = InputSplitter
124 _input_splitter_class = InputSplitter
125 _local_kernel = False
125 _local_kernel = False
126 _highlighter = Instance(FrontendHighlighter)
126 _highlighter = Instance(FrontendHighlighter)
127
127
128 #---------------------------------------------------------------------------
128 #---------------------------------------------------------------------------
129 # 'object' interface
129 # 'object' interface
130 #---------------------------------------------------------------------------
130 #---------------------------------------------------------------------------
131
131
132 def __init__(self, *args, **kw):
132 def __init__(self, *args, **kw):
133 super(FrontendWidget, self).__init__(*args, **kw)
133 super(FrontendWidget, self).__init__(*args, **kw)
134 # FIXME: remove this when PySide min version is updated past 1.0.7
134 # FIXME: remove this when PySide min version is updated past 1.0.7
135 # forcefully disable calltips if PySide is < 1.0.7, because they crash
135 # forcefully disable calltips if PySide is < 1.0.7, because they crash
136 if qt.QT_API == qt.QT_API_PYSIDE:
136 if qt.QT_API == qt.QT_API_PYSIDE:
137 import PySide
137 import PySide
138 if PySide.__version_info__ < (1,0,7):
138 if PySide.__version_info__ < (1,0,7):
139 self.log.warn("PySide %s < 1.0.7 detected, disabling calltips" % PySide.__version__)
139 self.log.warn("PySide %s < 1.0.7 detected, disabling calltips" % PySide.__version__)
140 self.enable_calltips = False
140 self.enable_calltips = False
141
141
142 # FrontendWidget protected variables.
142 # FrontendWidget protected variables.
143 self._bracket_matcher = BracketMatcher(self._control)
143 self._bracket_matcher = BracketMatcher(self._control)
144 self._call_tip_widget = CallTipWidget(self._control)
144 self._call_tip_widget = CallTipWidget(self._control)
145 self._completion_lexer = CompletionLexer(PythonLexer())
145 self._completion_lexer = CompletionLexer(PythonLexer())
146 self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None)
146 self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None)
147 self._hidden = False
147 self._hidden = False
148 self._highlighter = FrontendHighlighter(self)
148 self._highlighter = FrontendHighlighter(self)
149 self._input_splitter = self._input_splitter_class(input_mode='cell')
149 self._input_splitter = self._input_splitter_class()
150 self._kernel_manager = None
150 self._kernel_manager = None
151 self._request_info = {}
151 self._request_info = {}
152 self._request_info['execute'] = {};
152 self._request_info['execute'] = {};
153 self._callback_dict = {}
153 self._callback_dict = {}
154
154
155 # Configure the ConsoleWidget.
155 # Configure the ConsoleWidget.
156 self.tab_width = 4
156 self.tab_width = 4
157 self._set_continuation_prompt('... ')
157 self._set_continuation_prompt('... ')
158
158
159 # Configure the CallTipWidget.
159 # Configure the CallTipWidget.
160 self._call_tip_widget.setFont(self.font)
160 self._call_tip_widget.setFont(self.font)
161 self.font_changed.connect(self._call_tip_widget.setFont)
161 self.font_changed.connect(self._call_tip_widget.setFont)
162
162
163 # Configure actions.
163 # Configure actions.
164 action = self._copy_raw_action
164 action = self._copy_raw_action
165 key = QtCore.Qt.CTRL | QtCore.Qt.SHIFT | QtCore.Qt.Key_C
165 key = QtCore.Qt.CTRL | QtCore.Qt.SHIFT | QtCore.Qt.Key_C
166 action.setEnabled(False)
166 action.setEnabled(False)
167 action.setShortcut(QtGui.QKeySequence(key))
167 action.setShortcut(QtGui.QKeySequence(key))
168 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
168 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
169 action.triggered.connect(self.copy_raw)
169 action.triggered.connect(self.copy_raw)
170 self.copy_available.connect(action.setEnabled)
170 self.copy_available.connect(action.setEnabled)
171 self.addAction(action)
171 self.addAction(action)
172
172
173 # Connect signal handlers.
173 # Connect signal handlers.
174 document = self._control.document()
174 document = self._control.document()
175 document.contentsChange.connect(self._document_contents_change)
175 document.contentsChange.connect(self._document_contents_change)
176
176
177 # Set flag for whether we are connected via localhost.
177 # Set flag for whether we are connected via localhost.
178 self._local_kernel = kw.get('local_kernel',
178 self._local_kernel = kw.get('local_kernel',
179 FrontendWidget._local_kernel)
179 FrontendWidget._local_kernel)
180
180
181 #---------------------------------------------------------------------------
181 #---------------------------------------------------------------------------
182 # 'ConsoleWidget' public interface
182 # 'ConsoleWidget' public interface
183 #---------------------------------------------------------------------------
183 #---------------------------------------------------------------------------
184
184
185 def copy(self):
185 def copy(self):
186 """ Copy the currently selected text to the clipboard, removing prompts.
186 """ Copy the currently selected text to the clipboard, removing prompts.
187 """
187 """
188 if self._page_control is not None and self._page_control.hasFocus():
188 if self._page_control is not None and self._page_control.hasFocus():
189 self._page_control.copy()
189 self._page_control.copy()
190 elif self._control.hasFocus():
190 elif self._control.hasFocus():
191 text = self._control.textCursor().selection().toPlainText()
191 text = self._control.textCursor().selection().toPlainText()
192 if text:
192 if text:
193 text = self._prompt_transformer.transform_cell(text)
193 text = self._prompt_transformer.transform_cell(text)
194 QtGui.QApplication.clipboard().setText(text)
194 QtGui.QApplication.clipboard().setText(text)
195 else:
195 else:
196 self.log.debug("frontend widget : unknown copy target")
196 self.log.debug("frontend widget : unknown copy target")
197
197
198 #---------------------------------------------------------------------------
198 #---------------------------------------------------------------------------
199 # 'ConsoleWidget' abstract interface
199 # 'ConsoleWidget' abstract interface
200 #---------------------------------------------------------------------------
200 #---------------------------------------------------------------------------
201
201
202 def _is_complete(self, source, interactive):
202 def _is_complete(self, source, interactive):
203 """ Returns whether 'source' can be completely processed and a new
203 """ Returns whether 'source' can be completely processed and a new
204 prompt created. When triggered by an Enter/Return key press,
204 prompt created. When triggered by an Enter/Return key press,
205 'interactive' is True; otherwise, it is False.
205 'interactive' is True; otherwise, it is False.
206 """
206 """
207 self._input_splitter.reset()
207 complete = self._input_splitter.push(source)
208 complete = self._input_splitter.push(source)
208 if interactive:
209 if interactive:
209 complete = not self._input_splitter.push_accepts_more()
210 complete = not self._input_splitter.push_accepts_more()
210 return complete
211 return complete
211
212
212 def _execute(self, source, hidden):
213 def _execute(self, source, hidden):
213 """ Execute 'source'. If 'hidden', do not show any output.
214 """ Execute 'source'. If 'hidden', do not show any output.
214
215
215 See parent class :meth:`execute` docstring for full details.
216 See parent class :meth:`execute` docstring for full details.
216 """
217 """
217 msg_id = self.kernel_manager.shell_channel.execute(source, hidden)
218 msg_id = self.kernel_manager.shell_channel.execute(source, hidden)
218 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'user')
219 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'user')
219 self._hidden = hidden
220 self._hidden = hidden
220 if not hidden:
221 if not hidden:
221 self.executing.emit(source)
222 self.executing.emit(source)
222
223
223 def _prompt_started_hook(self):
224 def _prompt_started_hook(self):
224 """ Called immediately after a new prompt is displayed.
225 """ Called immediately after a new prompt is displayed.
225 """
226 """
226 if not self._reading:
227 if not self._reading:
227 self._highlighter.highlighting_on = True
228 self._highlighter.highlighting_on = True
228
229
229 def _prompt_finished_hook(self):
230 def _prompt_finished_hook(self):
230 """ Called immediately after a prompt is finished, i.e. when some input
231 """ Called immediately after a prompt is finished, i.e. when some input
231 will be processed and a new prompt displayed.
232 will be processed and a new prompt displayed.
232 """
233 """
233 # Flush all state from the input splitter so the next round of
234 # Flush all state from the input splitter so the next round of
234 # reading input starts with a clean buffer.
235 # reading input starts with a clean buffer.
235 self._input_splitter.reset()
236 self._input_splitter.reset()
236
237
237 if not self._reading:
238 if not self._reading:
238 self._highlighter.highlighting_on = False
239 self._highlighter.highlighting_on = False
239
240
240 def _tab_pressed(self):
241 def _tab_pressed(self):
241 """ Called when the tab key is pressed. Returns whether to continue
242 """ Called when the tab key is pressed. Returns whether to continue
242 processing the event.
243 processing the event.
243 """
244 """
244 # Perform tab completion if:
245 # Perform tab completion if:
245 # 1) The cursor is in the input buffer.
246 # 1) The cursor is in the input buffer.
246 # 2) There is a non-whitespace character before the cursor.
247 # 2) There is a non-whitespace character before the cursor.
247 text = self._get_input_buffer_cursor_line()
248 text = self._get_input_buffer_cursor_line()
248 if text is None:
249 if text is None:
249 return False
250 return False
250 complete = bool(text[:self._get_input_buffer_cursor_column()].strip())
251 complete = bool(text[:self._get_input_buffer_cursor_column()].strip())
251 if complete:
252 if complete:
252 self._complete()
253 self._complete()
253 return not complete
254 return not complete
254
255
255 #---------------------------------------------------------------------------
256 #---------------------------------------------------------------------------
256 # 'ConsoleWidget' protected interface
257 # 'ConsoleWidget' protected interface
257 #---------------------------------------------------------------------------
258 #---------------------------------------------------------------------------
258
259
259 def _context_menu_make(self, pos):
260 def _context_menu_make(self, pos):
260 """ Reimplemented to add an action for raw copy.
261 """ Reimplemented to add an action for raw copy.
261 """
262 """
262 menu = super(FrontendWidget, self)._context_menu_make(pos)
263 menu = super(FrontendWidget, self)._context_menu_make(pos)
263 for before_action in menu.actions():
264 for before_action in menu.actions():
264 if before_action.shortcut().matches(QtGui.QKeySequence.Paste) == \
265 if before_action.shortcut().matches(QtGui.QKeySequence.Paste) == \
265 QtGui.QKeySequence.ExactMatch:
266 QtGui.QKeySequence.ExactMatch:
266 menu.insertAction(before_action, self._copy_raw_action)
267 menu.insertAction(before_action, self._copy_raw_action)
267 break
268 break
268 return menu
269 return menu
269
270
270 def request_interrupt_kernel(self):
271 def request_interrupt_kernel(self):
271 if self._executing:
272 if self._executing:
272 self.interrupt_kernel()
273 self.interrupt_kernel()
273
274
274 def request_restart_kernel(self):
275 def request_restart_kernel(self):
275 message = 'Are you sure you want to restart the kernel?'
276 message = 'Are you sure you want to restart the kernel?'
276 self.restart_kernel(message, now=False)
277 self.restart_kernel(message, now=False)
277
278
278 def _event_filter_console_keypress(self, event):
279 def _event_filter_console_keypress(self, event):
279 """ Reimplemented for execution interruption and smart backspace.
280 """ Reimplemented for execution interruption and smart backspace.
280 """
281 """
281 key = event.key()
282 key = event.key()
282 if self._control_key_down(event.modifiers(), include_command=False):
283 if self._control_key_down(event.modifiers(), include_command=False):
283
284
284 if key == QtCore.Qt.Key_C and self._executing:
285 if key == QtCore.Qt.Key_C and self._executing:
285 self.request_interrupt_kernel()
286 self.request_interrupt_kernel()
286 return True
287 return True
287
288
288 elif key == QtCore.Qt.Key_Period:
289 elif key == QtCore.Qt.Key_Period:
289 self.request_restart_kernel()
290 self.request_restart_kernel()
290 return True
291 return True
291
292
292 elif not event.modifiers() & QtCore.Qt.AltModifier:
293 elif not event.modifiers() & QtCore.Qt.AltModifier:
293
294
294 # Smart backspace: remove four characters in one backspace if:
295 # Smart backspace: remove four characters in one backspace if:
295 # 1) everything left of the cursor is whitespace
296 # 1) everything left of the cursor is whitespace
296 # 2) the four characters immediately left of the cursor are spaces
297 # 2) the four characters immediately left of the cursor are spaces
297 if key == QtCore.Qt.Key_Backspace:
298 if key == QtCore.Qt.Key_Backspace:
298 col = self._get_input_buffer_cursor_column()
299 col = self._get_input_buffer_cursor_column()
299 cursor = self._control.textCursor()
300 cursor = self._control.textCursor()
300 if col > 3 and not cursor.hasSelection():
301 if col > 3 and not cursor.hasSelection():
301 text = self._get_input_buffer_cursor_line()[:col]
302 text = self._get_input_buffer_cursor_line()[:col]
302 if text.endswith(' ') and not text.strip():
303 if text.endswith(' ') and not text.strip():
303 cursor.movePosition(QtGui.QTextCursor.Left,
304 cursor.movePosition(QtGui.QTextCursor.Left,
304 QtGui.QTextCursor.KeepAnchor, 4)
305 QtGui.QTextCursor.KeepAnchor, 4)
305 cursor.removeSelectedText()
306 cursor.removeSelectedText()
306 return True
307 return True
307
308
308 return super(FrontendWidget, self)._event_filter_console_keypress(event)
309 return super(FrontendWidget, self)._event_filter_console_keypress(event)
309
310
310 def _insert_continuation_prompt(self, cursor):
311 def _insert_continuation_prompt(self, cursor):
311 """ Reimplemented for auto-indentation.
312 """ Reimplemented for auto-indentation.
312 """
313 """
313 super(FrontendWidget, self)._insert_continuation_prompt(cursor)
314 super(FrontendWidget, self)._insert_continuation_prompt(cursor)
314 cursor.insertText(' ' * self._input_splitter.indent_spaces)
315 cursor.insertText(' ' * self._input_splitter.indent_spaces)
315
316
316 #---------------------------------------------------------------------------
317 #---------------------------------------------------------------------------
317 # 'BaseFrontendMixin' abstract interface
318 # 'BaseFrontendMixin' abstract interface
318 #---------------------------------------------------------------------------
319 #---------------------------------------------------------------------------
319
320
320 def _handle_complete_reply(self, rep):
321 def _handle_complete_reply(self, rep):
321 """ Handle replies for tab completion.
322 """ Handle replies for tab completion.
322 """
323 """
323 self.log.debug("complete: %s", rep.get('content', ''))
324 self.log.debug("complete: %s", rep.get('content', ''))
324 cursor = self._get_cursor()
325 cursor = self._get_cursor()
325 info = self._request_info.get('complete')
326 info = self._request_info.get('complete')
326 if info and info.id == rep['parent_header']['msg_id'] and \
327 if info and info.id == rep['parent_header']['msg_id'] and \
327 info.pos == cursor.position():
328 info.pos == cursor.position():
328 text = '.'.join(self._get_context())
329 text = '.'.join(self._get_context())
329 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
330 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
330 self._complete_with_items(cursor, rep['content']['matches'])
331 self._complete_with_items(cursor, rep['content']['matches'])
331
332
332 def _silent_exec_callback(self, expr, callback):
333 def _silent_exec_callback(self, expr, callback):
333 """Silently execute `expr` in the kernel and call `callback` with reply
334 """Silently execute `expr` in the kernel and call `callback` with reply
334
335
335 the `expr` is evaluated silently in the kernel (without) output in
336 the `expr` is evaluated silently in the kernel (without) output in
336 the frontend. Call `callback` with the
337 the frontend. Call `callback` with the
337 `repr <http://docs.python.org/library/functions.html#repr> `_ as first argument
338 `repr <http://docs.python.org/library/functions.html#repr> `_ as first argument
338
339
339 Parameters
340 Parameters
340 ----------
341 ----------
341 expr : string
342 expr : string
342 valid string to be executed by the kernel.
343 valid string to be executed by the kernel.
343 callback : function
344 callback : function
344 function accepting one argument, as a string. The string will be
345 function accepting one argument, as a string. The string will be
345 the `repr` of the result of evaluating `expr`
346 the `repr` of the result of evaluating `expr`
346
347
347 The `callback` is called with the `repr()` of the result of `expr` as
348 The `callback` is called with the `repr()` of the result of `expr` as
348 first argument. To get the object, do `eval()` on the passed value.
349 first argument. To get the object, do `eval()` on the passed value.
349
350
350 See Also
351 See Also
351 --------
352 --------
352 _handle_exec_callback : private method, deal with calling callback with reply
353 _handle_exec_callback : private method, deal with calling callback with reply
353
354
354 """
355 """
355
356
356 # generate uuid, which would be used as an indication of whether or
357 # generate uuid, which would be used as an indication of whether or
357 # not the unique request originated from here (can use msg id ?)
358 # not the unique request originated from here (can use msg id ?)
358 local_uuid = str(uuid.uuid1())
359 local_uuid = str(uuid.uuid1())
359 msg_id = self.kernel_manager.shell_channel.execute('',
360 msg_id = self.kernel_manager.shell_channel.execute('',
360 silent=True, user_expressions={ local_uuid:expr })
361 silent=True, user_expressions={ local_uuid:expr })
361 self._callback_dict[local_uuid] = callback
362 self._callback_dict[local_uuid] = callback
362 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'silent_exec_callback')
363 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'silent_exec_callback')
363
364
364 def _handle_exec_callback(self, msg):
365 def _handle_exec_callback(self, msg):
365 """Execute `callback` corresponding to `msg` reply, after ``_silent_exec_callback``
366 """Execute `callback` corresponding to `msg` reply, after ``_silent_exec_callback``
366
367
367 Parameters
368 Parameters
368 ----------
369 ----------
369 msg : raw message send by the kernel containing an `user_expressions`
370 msg : raw message send by the kernel containing an `user_expressions`
370 and having a 'silent_exec_callback' kind.
371 and having a 'silent_exec_callback' kind.
371
372
372 Notes
373 Notes
373 -----
374 -----
374 This function will look for a `callback` associated with the
375 This function will look for a `callback` associated with the
375 corresponding message id. Association has been made by
376 corresponding message id. Association has been made by
376 `_silent_exec_callback`. `callback` is then called with the `repr()`
377 `_silent_exec_callback`. `callback` is then called with the `repr()`
377 of the value of corresponding `user_expressions` as argument.
378 of the value of corresponding `user_expressions` as argument.
378 `callback` is then removed from the known list so that any message
379 `callback` is then removed from the known list so that any message
379 coming again with the same id won't trigger it.
380 coming again with the same id won't trigger it.
380
381
381 """
382 """
382
383
383 user_exp = msg['content'].get('user_expressions')
384 user_exp = msg['content'].get('user_expressions')
384 if not user_exp:
385 if not user_exp:
385 return
386 return
386 for expression in user_exp:
387 for expression in user_exp:
387 if expression in self._callback_dict:
388 if expression in self._callback_dict:
388 self._callback_dict.pop(expression)(user_exp[expression])
389 self._callback_dict.pop(expression)(user_exp[expression])
389
390
390 def _handle_execute_reply(self, msg):
391 def _handle_execute_reply(self, msg):
391 """ Handles replies for code execution.
392 """ Handles replies for code execution.
392 """
393 """
393 self.log.debug("execute: %s", msg.get('content', ''))
394 self.log.debug("execute: %s", msg.get('content', ''))
394 msg_id = msg['parent_header']['msg_id']
395 msg_id = msg['parent_header']['msg_id']
395 info = self._request_info['execute'].get(msg_id)
396 info = self._request_info['execute'].get(msg_id)
396 # unset reading flag, because if execute finished, raw_input can't
397 # unset reading flag, because if execute finished, raw_input can't
397 # still be pending.
398 # still be pending.
398 self._reading = False
399 self._reading = False
399 if info and info.kind == 'user' and not self._hidden:
400 if info and info.kind == 'user' and not self._hidden:
400 # Make sure that all output from the SUB channel has been processed
401 # Make sure that all output from the SUB channel has been processed
401 # before writing a new prompt.
402 # before writing a new prompt.
402 self.kernel_manager.iopub_channel.flush()
403 self.kernel_manager.iopub_channel.flush()
403
404
404 # Reset the ANSI style information to prevent bad text in stdout
405 # Reset the ANSI style information to prevent bad text in stdout
405 # from messing up our colors. We're not a true terminal so we're
406 # from messing up our colors. We're not a true terminal so we're
406 # allowed to do this.
407 # allowed to do this.
407 if self.ansi_codes:
408 if self.ansi_codes:
408 self._ansi_processor.reset_sgr()
409 self._ansi_processor.reset_sgr()
409
410
410 content = msg['content']
411 content = msg['content']
411 status = content['status']
412 status = content['status']
412 if status == 'ok':
413 if status == 'ok':
413 self._process_execute_ok(msg)
414 self._process_execute_ok(msg)
414 elif status == 'error':
415 elif status == 'error':
415 self._process_execute_error(msg)
416 self._process_execute_error(msg)
416 elif status == 'aborted':
417 elif status == 'aborted':
417 self._process_execute_abort(msg)
418 self._process_execute_abort(msg)
418
419
419 self._show_interpreter_prompt_for_reply(msg)
420 self._show_interpreter_prompt_for_reply(msg)
420 self.executed.emit(msg)
421 self.executed.emit(msg)
421 self._request_info['execute'].pop(msg_id)
422 self._request_info['execute'].pop(msg_id)
422 elif info and info.kind == 'silent_exec_callback' and not self._hidden:
423 elif info and info.kind == 'silent_exec_callback' and not self._hidden:
423 self._handle_exec_callback(msg)
424 self._handle_exec_callback(msg)
424 self._request_info['execute'].pop(msg_id)
425 self._request_info['execute'].pop(msg_id)
425 else:
426 else:
426 super(FrontendWidget, self)._handle_execute_reply(msg)
427 super(FrontendWidget, self)._handle_execute_reply(msg)
427
428
428 def _handle_input_request(self, msg):
429 def _handle_input_request(self, msg):
429 """ Handle requests for raw_input.
430 """ Handle requests for raw_input.
430 """
431 """
431 self.log.debug("input: %s", msg.get('content', ''))
432 self.log.debug("input: %s", msg.get('content', ''))
432 if self._hidden:
433 if self._hidden:
433 raise RuntimeError('Request for raw input during hidden execution.')
434 raise RuntimeError('Request for raw input during hidden execution.')
434
435
435 # Make sure that all output from the SUB channel has been processed
436 # Make sure that all output from the SUB channel has been processed
436 # before entering readline mode.
437 # before entering readline mode.
437 self.kernel_manager.iopub_channel.flush()
438 self.kernel_manager.iopub_channel.flush()
438
439
439 def callback(line):
440 def callback(line):
440 self.kernel_manager.stdin_channel.input(line)
441 self.kernel_manager.stdin_channel.input(line)
441 if self._reading:
442 if self._reading:
442 self.log.debug("Got second input request, assuming first was interrupted.")
443 self.log.debug("Got second input request, assuming first was interrupted.")
443 self._reading = False
444 self._reading = False
444 self._readline(msg['content']['prompt'], callback=callback)
445 self._readline(msg['content']['prompt'], callback=callback)
445
446
446 def _handle_kernel_died(self, since_last_heartbeat):
447 def _handle_kernel_died(self, since_last_heartbeat):
447 """ Handle the kernel's death by asking if the user wants to restart.
448 """ Handle the kernel's death by asking if the user wants to restart.
448 """
449 """
449 self.log.debug("kernel died: %s", since_last_heartbeat)
450 self.log.debug("kernel died: %s", since_last_heartbeat)
450 if self.custom_restart:
451 if self.custom_restart:
451 self.custom_restart_kernel_died.emit(since_last_heartbeat)
452 self.custom_restart_kernel_died.emit(since_last_heartbeat)
452 else:
453 else:
453 message = 'The kernel heartbeat has been inactive for %.2f ' \
454 message = 'The kernel heartbeat has been inactive for %.2f ' \
454 'seconds. Do you want to restart the kernel? You may ' \
455 'seconds. Do you want to restart the kernel? You may ' \
455 'first want to check the network connection.' % \
456 'first want to check the network connection.' % \
456 since_last_heartbeat
457 since_last_heartbeat
457 self.restart_kernel(message, now=True)
458 self.restart_kernel(message, now=True)
458
459
459 def _handle_object_info_reply(self, rep):
460 def _handle_object_info_reply(self, rep):
460 """ Handle replies for call tips.
461 """ Handle replies for call tips.
461 """
462 """
462 self.log.debug("oinfo: %s", rep.get('content', ''))
463 self.log.debug("oinfo: %s", rep.get('content', ''))
463 cursor = self._get_cursor()
464 cursor = self._get_cursor()
464 info = self._request_info.get('call_tip')
465 info = self._request_info.get('call_tip')
465 if info and info.id == rep['parent_header']['msg_id'] and \
466 if info and info.id == rep['parent_header']['msg_id'] and \
466 info.pos == cursor.position():
467 info.pos == cursor.position():
467 # Get the information for a call tip. For now we format the call
468 # Get the information for a call tip. For now we format the call
468 # line as string, later we can pass False to format_call and
469 # line as string, later we can pass False to format_call and
469 # syntax-highlight it ourselves for nicer formatting in the
470 # syntax-highlight it ourselves for nicer formatting in the
470 # calltip.
471 # calltip.
471 content = rep['content']
472 content = rep['content']
472 # if this is from pykernel, 'docstring' will be the only key
473 # if this is from pykernel, 'docstring' will be the only key
473 if content.get('ismagic', False):
474 if content.get('ismagic', False):
474 # Don't generate a call-tip for magics. Ideally, we should
475 # Don't generate a call-tip for magics. Ideally, we should
475 # generate a tooltip, but not on ( like we do for actual
476 # generate a tooltip, but not on ( like we do for actual
476 # callables.
477 # callables.
477 call_info, doc = None, None
478 call_info, doc = None, None
478 else:
479 else:
479 call_info, doc = call_tip(content, format_call=True)
480 call_info, doc = call_tip(content, format_call=True)
480 if call_info or doc:
481 if call_info or doc:
481 self._call_tip_widget.show_call_info(call_info, doc)
482 self._call_tip_widget.show_call_info(call_info, doc)
482
483
483 def _handle_pyout(self, msg):
484 def _handle_pyout(self, msg):
484 """ Handle display hook output.
485 """ Handle display hook output.
485 """
486 """
486 self.log.debug("pyout: %s", msg.get('content', ''))
487 self.log.debug("pyout: %s", msg.get('content', ''))
487 if not self._hidden and self._is_from_this_session(msg):
488 if not self._hidden and self._is_from_this_session(msg):
488 text = msg['content']['data']
489 text = msg['content']['data']
489 self._append_plain_text(text + '\n', before_prompt=True)
490 self._append_plain_text(text + '\n', before_prompt=True)
490
491
491 def _handle_stream(self, msg):
492 def _handle_stream(self, msg):
492 """ Handle stdout, stderr, and stdin.
493 """ Handle stdout, stderr, and stdin.
493 """
494 """
494 self.log.debug("stream: %s", msg.get('content', ''))
495 self.log.debug("stream: %s", msg.get('content', ''))
495 if not self._hidden and self._is_from_this_session(msg):
496 if not self._hidden and self._is_from_this_session(msg):
496 # Most consoles treat tabs as being 8 space characters. Convert tabs
497 # Most consoles treat tabs as being 8 space characters. Convert tabs
497 # to spaces so that output looks as expected regardless of this
498 # to spaces so that output looks as expected regardless of this
498 # widget's tab width.
499 # widget's tab width.
499 text = msg['content']['data'].expandtabs(8)
500 text = msg['content']['data'].expandtabs(8)
500
501
501 self._append_plain_text(text, before_prompt=True)
502 self._append_plain_text(text, before_prompt=True)
502 self._control.moveCursor(QtGui.QTextCursor.End)
503 self._control.moveCursor(QtGui.QTextCursor.End)
503
504
504 def _handle_shutdown_reply(self, msg):
505 def _handle_shutdown_reply(self, msg):
505 """ Handle shutdown signal, only if from other console.
506 """ Handle shutdown signal, only if from other console.
506 """
507 """
507 self.log.debug("shutdown: %s", msg.get('content', ''))
508 self.log.debug("shutdown: %s", msg.get('content', ''))
508 if not self._hidden and not self._is_from_this_session(msg):
509 if not self._hidden and not self._is_from_this_session(msg):
509 if self._local_kernel:
510 if self._local_kernel:
510 if not msg['content']['restart']:
511 if not msg['content']['restart']:
511 self.exit_requested.emit(self)
512 self.exit_requested.emit(self)
512 else:
513 else:
513 # we just got notified of a restart!
514 # we just got notified of a restart!
514 time.sleep(0.25) # wait 1/4 sec to reset
515 time.sleep(0.25) # wait 1/4 sec to reset
515 # lest the request for a new prompt
516 # lest the request for a new prompt
516 # goes to the old kernel
517 # goes to the old kernel
517 self.reset()
518 self.reset()
518 else: # remote kernel, prompt on Kernel shutdown/reset
519 else: # remote kernel, prompt on Kernel shutdown/reset
519 title = self.window().windowTitle()
520 title = self.window().windowTitle()
520 if not msg['content']['restart']:
521 if not msg['content']['restart']:
521 reply = QtGui.QMessageBox.question(self, title,
522 reply = QtGui.QMessageBox.question(self, title,
522 "Kernel has been shutdown permanently. "
523 "Kernel has been shutdown permanently. "
523 "Close the Console?",
524 "Close the Console?",
524 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
525 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
525 if reply == QtGui.QMessageBox.Yes:
526 if reply == QtGui.QMessageBox.Yes:
526 self.exit_requested.emit(self)
527 self.exit_requested.emit(self)
527 else:
528 else:
528 # XXX: remove message box in favor of using the
529 # XXX: remove message box in favor of using the
529 # clear_on_kernel_restart setting?
530 # clear_on_kernel_restart setting?
530 reply = QtGui.QMessageBox.question(self, title,
531 reply = QtGui.QMessageBox.question(self, title,
531 "Kernel has been reset. Clear the Console?",
532 "Kernel has been reset. Clear the Console?",
532 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
533 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
533 if reply == QtGui.QMessageBox.Yes:
534 if reply == QtGui.QMessageBox.Yes:
534 time.sleep(0.25) # wait 1/4 sec to reset
535 time.sleep(0.25) # wait 1/4 sec to reset
535 # lest the request for a new prompt
536 # lest the request for a new prompt
536 # goes to the old kernel
537 # goes to the old kernel
537 self.reset()
538 self.reset()
538
539
539 def _started_channels(self):
540 def _started_channels(self):
540 """ Called when the KernelManager channels have started listening or
541 """ Called when the KernelManager channels have started listening or
541 when the frontend is assigned an already listening KernelManager.
542 when the frontend is assigned an already listening KernelManager.
542 """
543 """
543 self.reset(clear=True)
544 self.reset(clear=True)
544
545
545 #---------------------------------------------------------------------------
546 #---------------------------------------------------------------------------
546 # 'FrontendWidget' public interface
547 # 'FrontendWidget' public interface
547 #---------------------------------------------------------------------------
548 #---------------------------------------------------------------------------
548
549
549 def copy_raw(self):
550 def copy_raw(self):
550 """ Copy the currently selected text to the clipboard without attempting
551 """ Copy the currently selected text to the clipboard without attempting
551 to remove prompts or otherwise alter the text.
552 to remove prompts or otherwise alter the text.
552 """
553 """
553 self._control.copy()
554 self._control.copy()
554
555
555 def execute_file(self, path, hidden=False):
556 def execute_file(self, path, hidden=False):
556 """ Attempts to execute file with 'path'. If 'hidden', no output is
557 """ Attempts to execute file with 'path'. If 'hidden', no output is
557 shown.
558 shown.
558 """
559 """
559 self.execute('execfile(%r)' % path, hidden=hidden)
560 self.execute('execfile(%r)' % path, hidden=hidden)
560
561
561 def interrupt_kernel(self):
562 def interrupt_kernel(self):
562 """ Attempts to interrupt the running kernel.
563 """ Attempts to interrupt the running kernel.
563
564
564 Also unsets _reading flag, to avoid runtime errors
565 Also unsets _reading flag, to avoid runtime errors
565 if raw_input is called again.
566 if raw_input is called again.
566 """
567 """
567 if self.custom_interrupt:
568 if self.custom_interrupt:
568 self._reading = False
569 self._reading = False
569 self.custom_interrupt_requested.emit()
570 self.custom_interrupt_requested.emit()
570 elif self.kernel_manager.has_kernel:
571 elif self.kernel_manager.has_kernel:
571 self._reading = False
572 self._reading = False
572 self.kernel_manager.interrupt_kernel()
573 self.kernel_manager.interrupt_kernel()
573 else:
574 else:
574 self._append_plain_text('Kernel process is either remote or '
575 self._append_plain_text('Kernel process is either remote or '
575 'unspecified. Cannot interrupt.\n')
576 'unspecified. Cannot interrupt.\n')
576
577
577 def reset(self, clear=False):
578 def reset(self, clear=False):
578 """ Resets the widget to its initial state if ``clear`` parameter or
579 """ Resets the widget to its initial state if ``clear`` parameter or
579 ``clear_on_kernel_restart`` configuration setting is True, otherwise
580 ``clear_on_kernel_restart`` configuration setting is True, otherwise
580 prints a visual indication of the fact that the kernel restarted, but
581 prints a visual indication of the fact that the kernel restarted, but
581 does not clear the traces from previous usage of the kernel before it
582 does not clear the traces from previous usage of the kernel before it
582 was restarted. With ``clear=True``, it is similar to ``%clear``, but
583 was restarted. With ``clear=True``, it is similar to ``%clear``, but
583 also re-writes the banner and aborts execution if necessary.
584 also re-writes the banner and aborts execution if necessary.
584 """
585 """
585 if self._executing:
586 if self._executing:
586 self._executing = False
587 self._executing = False
587 self._request_info['execute'] = {}
588 self._request_info['execute'] = {}
588 self._reading = False
589 self._reading = False
589 self._highlighter.highlighting_on = False
590 self._highlighter.highlighting_on = False
590
591
591 if self.clear_on_kernel_restart or clear:
592 if self.clear_on_kernel_restart or clear:
592 self._control.clear()
593 self._control.clear()
593 self._append_plain_text(self.banner)
594 self._append_plain_text(self.banner)
594 else:
595 else:
595 self._append_plain_text("# restarting kernel...")
596 self._append_plain_text("# restarting kernel...")
596 self._append_html("<hr><br>")
597 self._append_html("<hr><br>")
597 # XXX: Reprinting the full banner may be too much, but once #1680 is
598 # XXX: Reprinting the full banner may be too much, but once #1680 is
598 # addressed, that will mitigate it.
599 # addressed, that will mitigate it.
599 #self._append_plain_text(self.banner)
600 #self._append_plain_text(self.banner)
600 # update output marker for stdout/stderr, so that startup
601 # update output marker for stdout/stderr, so that startup
601 # messages appear after banner:
602 # messages appear after banner:
602 self._append_before_prompt_pos = self._get_cursor().position()
603 self._append_before_prompt_pos = self._get_cursor().position()
603 self._show_interpreter_prompt()
604 self._show_interpreter_prompt()
604
605
605 def restart_kernel(self, message, now=False):
606 def restart_kernel(self, message, now=False):
606 """ Attempts to restart the running kernel.
607 """ Attempts to restart the running kernel.
607 """
608 """
608 # FIXME: now should be configurable via a checkbox in the dialog. Right
609 # FIXME: now should be configurable via a checkbox in the dialog. Right
609 # now at least the heartbeat path sets it to True and the manual restart
610 # now at least the heartbeat path sets it to True and the manual restart
610 # to False. But those should just be the pre-selected states of a
611 # to False. But those should just be the pre-selected states of a
611 # checkbox that the user could override if so desired. But I don't know
612 # checkbox that the user could override if so desired. But I don't know
612 # enough Qt to go implementing the checkbox now.
613 # enough Qt to go implementing the checkbox now.
613
614
614 if self.custom_restart:
615 if self.custom_restart:
615 self.custom_restart_requested.emit()
616 self.custom_restart_requested.emit()
616
617
617 elif self.kernel_manager.has_kernel:
618 elif self.kernel_manager.has_kernel:
618 # Pause the heart beat channel to prevent further warnings.
619 # Pause the heart beat channel to prevent further warnings.
619 self.kernel_manager.hb_channel.pause()
620 self.kernel_manager.hb_channel.pause()
620
621
621 # Prompt the user to restart the kernel. Un-pause the heartbeat if
622 # Prompt the user to restart the kernel. Un-pause the heartbeat if
622 # they decline. (If they accept, the heartbeat will be un-paused
623 # they decline. (If they accept, the heartbeat will be un-paused
623 # automatically when the kernel is restarted.)
624 # automatically when the kernel is restarted.)
624 if self.confirm_restart:
625 if self.confirm_restart:
625 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
626 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
626 result = QtGui.QMessageBox.question(self, 'Restart kernel?',
627 result = QtGui.QMessageBox.question(self, 'Restart kernel?',
627 message, buttons)
628 message, buttons)
628 do_restart = result == QtGui.QMessageBox.Yes
629 do_restart = result == QtGui.QMessageBox.Yes
629 else:
630 else:
630 # confirm_restart is False, so we don't need to ask user
631 # confirm_restart is False, so we don't need to ask user
631 # anything, just do the restart
632 # anything, just do the restart
632 do_restart = True
633 do_restart = True
633 if do_restart:
634 if do_restart:
634 try:
635 try:
635 self.kernel_manager.restart_kernel(now=now)
636 self.kernel_manager.restart_kernel(now=now)
636 except RuntimeError:
637 except RuntimeError:
637 self._append_plain_text('Kernel started externally. '
638 self._append_plain_text('Kernel started externally. '
638 'Cannot restart.\n',
639 'Cannot restart.\n',
639 before_prompt=True
640 before_prompt=True
640 )
641 )
641 else:
642 else:
642 self.reset()
643 self.reset()
643 else:
644 else:
644 self.kernel_manager.hb_channel.unpause()
645 self.kernel_manager.hb_channel.unpause()
645
646
646 else:
647 else:
647 self._append_plain_text('Kernel process is either remote or '
648 self._append_plain_text('Kernel process is either remote or '
648 'unspecified. Cannot restart.\n',
649 'unspecified. Cannot restart.\n',
649 before_prompt=True
650 before_prompt=True
650 )
651 )
651
652
652 #---------------------------------------------------------------------------
653 #---------------------------------------------------------------------------
653 # 'FrontendWidget' protected interface
654 # 'FrontendWidget' protected interface
654 #---------------------------------------------------------------------------
655 #---------------------------------------------------------------------------
655
656
656 def _call_tip(self):
657 def _call_tip(self):
657 """ Shows a call tip, if appropriate, at the current cursor location.
658 """ Shows a call tip, if appropriate, at the current cursor location.
658 """
659 """
659 # Decide if it makes sense to show a call tip
660 # Decide if it makes sense to show a call tip
660 if not self.enable_calltips:
661 if not self.enable_calltips:
661 return False
662 return False
662 cursor = self._get_cursor()
663 cursor = self._get_cursor()
663 cursor.movePosition(QtGui.QTextCursor.Left)
664 cursor.movePosition(QtGui.QTextCursor.Left)
664 if cursor.document().characterAt(cursor.position()) != '(':
665 if cursor.document().characterAt(cursor.position()) != '(':
665 return False
666 return False
666 context = self._get_context(cursor)
667 context = self._get_context(cursor)
667 if not context:
668 if not context:
668 return False
669 return False
669
670
670 # Send the metadata request to the kernel
671 # Send the metadata request to the kernel
671 name = '.'.join(context)
672 name = '.'.join(context)
672 msg_id = self.kernel_manager.shell_channel.object_info(name)
673 msg_id = self.kernel_manager.shell_channel.object_info(name)
673 pos = self._get_cursor().position()
674 pos = self._get_cursor().position()
674 self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos)
675 self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos)
675 return True
676 return True
676
677
677 def _complete(self):
678 def _complete(self):
678 """ Performs completion at the current cursor location.
679 """ Performs completion at the current cursor location.
679 """
680 """
680 context = self._get_context()
681 context = self._get_context()
681 if context:
682 if context:
682 # Send the completion request to the kernel
683 # Send the completion request to the kernel
683 msg_id = self.kernel_manager.shell_channel.complete(
684 msg_id = self.kernel_manager.shell_channel.complete(
684 '.'.join(context), # text
685 '.'.join(context), # text
685 self._get_input_buffer_cursor_line(), # line
686 self._get_input_buffer_cursor_line(), # line
686 self._get_input_buffer_cursor_column(), # cursor_pos
687 self._get_input_buffer_cursor_column(), # cursor_pos
687 self.input_buffer) # block
688 self.input_buffer) # block
688 pos = self._get_cursor().position()
689 pos = self._get_cursor().position()
689 info = self._CompletionRequest(msg_id, pos)
690 info = self._CompletionRequest(msg_id, pos)
690 self._request_info['complete'] = info
691 self._request_info['complete'] = info
691
692
692 def _get_context(self, cursor=None):
693 def _get_context(self, cursor=None):
693 """ Gets the context for the specified cursor (or the current cursor
694 """ Gets the context for the specified cursor (or the current cursor
694 if none is specified).
695 if none is specified).
695 """
696 """
696 if cursor is None:
697 if cursor is None:
697 cursor = self._get_cursor()
698 cursor = self._get_cursor()
698 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
699 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
699 QtGui.QTextCursor.KeepAnchor)
700 QtGui.QTextCursor.KeepAnchor)
700 text = cursor.selection().toPlainText()
701 text = cursor.selection().toPlainText()
701 return self._completion_lexer.get_context(text)
702 return self._completion_lexer.get_context(text)
702
703
703 def _process_execute_abort(self, msg):
704 def _process_execute_abort(self, msg):
704 """ Process a reply for an aborted execution request.
705 """ Process a reply for an aborted execution request.
705 """
706 """
706 self._append_plain_text("ERROR: execution aborted\n")
707 self._append_plain_text("ERROR: execution aborted\n")
707
708
708 def _process_execute_error(self, msg):
709 def _process_execute_error(self, msg):
709 """ Process a reply for an execution request that resulted in an error.
710 """ Process a reply for an execution request that resulted in an error.
710 """
711 """
711 content = msg['content']
712 content = msg['content']
712 # If a SystemExit is passed along, this means exit() was called - also
713 # If a SystemExit is passed along, this means exit() was called - also
713 # all the ipython %exit magic syntax of '-k' to be used to keep
714 # all the ipython %exit magic syntax of '-k' to be used to keep
714 # the kernel running
715 # the kernel running
715 if content['ename']=='SystemExit':
716 if content['ename']=='SystemExit':
716 keepkernel = content['evalue']=='-k' or content['evalue']=='True'
717 keepkernel = content['evalue']=='-k' or content['evalue']=='True'
717 self._keep_kernel_on_exit = keepkernel
718 self._keep_kernel_on_exit = keepkernel
718 self.exit_requested.emit(self)
719 self.exit_requested.emit(self)
719 else:
720 else:
720 traceback = ''.join(content['traceback'])
721 traceback = ''.join(content['traceback'])
721 self._append_plain_text(traceback)
722 self._append_plain_text(traceback)
722
723
723 def _process_execute_ok(self, msg):
724 def _process_execute_ok(self, msg):
724 """ Process a reply for a successful execution request.
725 """ Process a reply for a successful execution request.
725 """
726 """
726 payload = msg['content']['payload']
727 payload = msg['content']['payload']
727 for item in payload:
728 for item in payload:
728 if not self._process_execute_payload(item):
729 if not self._process_execute_payload(item):
729 warning = 'Warning: received unknown payload of type %s'
730 warning = 'Warning: received unknown payload of type %s'
730 print(warning % repr(item['source']))
731 print(warning % repr(item['source']))
731
732
732 def _process_execute_payload(self, item):
733 def _process_execute_payload(self, item):
733 """ Process a single payload item from the list of payload items in an
734 """ Process a single payload item from the list of payload items in an
734 execution reply. Returns whether the payload was handled.
735 execution reply. Returns whether the payload was handled.
735 """
736 """
736 # The basic FrontendWidget doesn't handle payloads, as they are a
737 # The basic FrontendWidget doesn't handle payloads, as they are a
737 # mechanism for going beyond the standard Python interpreter model.
738 # mechanism for going beyond the standard Python interpreter model.
738 return False
739 return False
739
740
740 def _show_interpreter_prompt(self):
741 def _show_interpreter_prompt(self):
741 """ Shows a prompt for the interpreter.
742 """ Shows a prompt for the interpreter.
742 """
743 """
743 self._show_prompt('>>> ')
744 self._show_prompt('>>> ')
744
745
745 def _show_interpreter_prompt_for_reply(self, msg):
746 def _show_interpreter_prompt_for_reply(self, msg):
746 """ Shows a prompt for the interpreter given an 'execute_reply' message.
747 """ Shows a prompt for the interpreter given an 'execute_reply' message.
747 """
748 """
748 self._show_interpreter_prompt()
749 self._show_interpreter_prompt()
749
750
750 #------ Signal handlers ----------------------------------------------------
751 #------ Signal handlers ----------------------------------------------------
751
752
752 def _document_contents_change(self, position, removed, added):
753 def _document_contents_change(self, position, removed, added):
753 """ Called whenever the document's content changes. Display a call tip
754 """ Called whenever the document's content changes. Display a call tip
754 if appropriate.
755 if appropriate.
755 """
756 """
756 # Calculate where the cursor should be *after* the change:
757 # Calculate where the cursor should be *after* the change:
757 position += added
758 position += added
758
759
759 document = self._control.document()
760 document = self._control.document()
760 if position == self._get_cursor().position():
761 if position == self._get_cursor().position():
761 self._call_tip()
762 self._call_tip()
762
763
763 #------ Trait default initializers -----------------------------------------
764 #------ Trait default initializers -----------------------------------------
764
765
765 def _banner_default(self):
766 def _banner_default(self):
766 """ Returns the standard Python banner.
767 """ Returns the standard Python banner.
767 """
768 """
768 banner = 'Python %s on %s\nType "help", "copyright", "credits" or ' \
769 banner = 'Python %s on %s\nType "help", "copyright", "credits" or ' \
769 '"license" for more information.'
770 '"license" for more information.'
770 return banner % (sys.version, sys.platform)
771 return banner % (sys.version, sys.platform)
@@ -1,752 +1,752 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Subclass of InteractiveShell for terminal based frontends."""
2 """Subclass of InteractiveShell for terminal based frontends."""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
5 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 import bdb
18 import bdb
19 import os
19 import os
20 import re
20 import re
21 import sys
21 import sys
22 import textwrap
22 import textwrap
23
23
24 # We need to use nested to support python 2.6, once we move to >=2.7, we can
24 # We need to use nested to support python 2.6, once we move to >=2.7, we can
25 # use the with keyword's new builtin support for nested managers
25 # use the with keyword's new builtin support for nested managers
26 try:
26 try:
27 from contextlib import nested
27 from contextlib import nested
28 except:
28 except:
29 from IPython.utils.nested_context import nested
29 from IPython.utils.nested_context import nested
30
30
31 from IPython.core.error import TryNext, UsageError
31 from IPython.core.error import TryNext, UsageError
32 from IPython.core.usage import interactive_usage, default_banner
32 from IPython.core.usage import interactive_usage, default_banner
33 from IPython.core.inputsplitter import IPythonInputSplitter
33 from IPython.core.inputsplitter import IPythonInputSplitter
34 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
34 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
35 from IPython.core.magic import Magics, magics_class, line_magic
35 from IPython.core.magic import Magics, magics_class, line_magic
36 from IPython.testing.skipdoctest import skip_doctest
36 from IPython.testing.skipdoctest import skip_doctest
37 from IPython.utils.encoding import get_stream_enc
37 from IPython.utils.encoding import get_stream_enc
38 from IPython.utils import py3compat
38 from IPython.utils import py3compat
39 from IPython.utils.terminal import toggle_set_term_title, set_term_title
39 from IPython.utils.terminal import toggle_set_term_title, set_term_title
40 from IPython.utils.process import abbrev_cwd
40 from IPython.utils.process import abbrev_cwd
41 from IPython.utils.warn import warn, error
41 from IPython.utils.warn import warn, error
42 from IPython.utils.text import num_ini_spaces, SList, strip_email_quotes
42 from IPython.utils.text import num_ini_spaces, SList, strip_email_quotes
43 from IPython.utils.traitlets import Integer, CBool, Unicode
43 from IPython.utils.traitlets import Integer, CBool, Unicode
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # Utilities
46 # Utilities
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49 def get_default_editor():
49 def get_default_editor():
50 try:
50 try:
51 ed = os.environ['EDITOR']
51 ed = os.environ['EDITOR']
52 except KeyError:
52 except KeyError:
53 if os.name == 'posix':
53 if os.name == 'posix':
54 ed = 'vi' # the only one guaranteed to be there!
54 ed = 'vi' # the only one guaranteed to be there!
55 else:
55 else:
56 ed = 'notepad' # same in Windows!
56 ed = 'notepad' # same in Windows!
57 return ed
57 return ed
58
58
59
59
60 def get_pasted_lines(sentinel, l_input=py3compat.input):
60 def get_pasted_lines(sentinel, l_input=py3compat.input):
61 """ Yield pasted lines until the user enters the given sentinel value.
61 """ Yield pasted lines until the user enters the given sentinel value.
62 """
62 """
63 print("Pasting code; enter '%s' alone on the line to stop or use Ctrl-D." \
63 print("Pasting code; enter '%s' alone on the line to stop or use Ctrl-D." \
64 % sentinel)
64 % sentinel)
65 while True:
65 while True:
66 try:
66 try:
67 l = l_input(':')
67 l = l_input(':')
68 if l == sentinel:
68 if l == sentinel:
69 return
69 return
70 else:
70 else:
71 yield l
71 yield l
72 except EOFError:
72 except EOFError:
73 print('<EOF>')
73 print('<EOF>')
74 return
74 return
75
75
76
76
77 #------------------------------------------------------------------------
77 #------------------------------------------------------------------------
78 # Terminal-specific magics
78 # Terminal-specific magics
79 #------------------------------------------------------------------------
79 #------------------------------------------------------------------------
80
80
81 @magics_class
81 @magics_class
82 class TerminalMagics(Magics):
82 class TerminalMagics(Magics):
83 def __init__(self, shell):
83 def __init__(self, shell):
84 super(TerminalMagics, self).__init__(shell)
84 super(TerminalMagics, self).__init__(shell)
85 self.input_splitter = IPythonInputSplitter(input_mode='line')
85 self.input_splitter = IPythonInputSplitter()
86
86
87 def cleanup_input(self, block):
87 def cleanup_input(self, block):
88 """Apply all possible IPython cleanups to an input block.
88 """Apply all possible IPython cleanups to an input block.
89
89
90 This means:
90 This means:
91
91
92 - remove any global leading whitespace (dedent)
92 - remove any global leading whitespace (dedent)
93 - remove any email quotes ('>') if they are present in *all* lines
93 - remove any email quotes ('>') if they are present in *all* lines
94 - apply all static inputsplitter transforms and break into sub-blocks
94 - apply all static inputsplitter transforms and break into sub-blocks
95 - apply prefilter() to each sub-block that is a single line.
95 - apply prefilter() to each sub-block that is a single line.
96
96
97 Parameters
97 Parameters
98 ----------
98 ----------
99 block : str
99 block : str
100 A possibly multiline input string of code.
100 A possibly multiline input string of code.
101
101
102 Returns
102 Returns
103 -------
103 -------
104 transformed block : str
104 transformed block : str
105 The input, with all transformations above applied.
105 The input, with all transformations above applied.
106 """
106 """
107 # We have to effectively implement client-side the loop that is done by
107 # We have to effectively implement client-side the loop that is done by
108 # the terminal frontend, and furthermore do it on a block that can
108 # the terminal frontend, and furthermore do it on a block that can
109 # possibly contain multiple statments pasted in one go.
109 # possibly contain multiple statments pasted in one go.
110
110
111 # First, run the input through the block splitting code. We should
111 # First, run the input through the block splitting code. We should
112 # eventually make this a self-contained method in the inputsplitter.
112 # eventually make this a self-contained method in the inputsplitter.
113 isp = self.input_splitter
113 isp = self.input_splitter
114 isp.reset()
114 isp.reset()
115 b = textwrap.dedent(block)
115 b = textwrap.dedent(block)
116
116
117 # Remove email quotes first. These must be consistently applied to
117 # Remove email quotes first. These must be consistently applied to
118 # *all* lines to be removed
118 # *all* lines to be removed
119 b = strip_email_quotes(b)
119 b = strip_email_quotes(b)
120
120
121 # Split the input into independent sub-blocks so we can later do
121 # Split the input into independent sub-blocks so we can later do
122 # prefiltering (which must be done *only* to single-line inputs)
122 # prefiltering (which must be done *only* to single-line inputs)
123 blocks = []
123 blocks = []
124 last_block = []
124 last_block = []
125 for line in b.splitlines():
125 for line in b.splitlines():
126 isp.push(line)
126 isp.push(line)
127 last_block.append(line)
127 last_block.append(line)
128 if not isp.push_accepts_more():
128 if not isp.push_accepts_more():
129 blocks.append(isp.source_reset())
129 blocks.append(isp.source_reset())
130 last_block = []
130 last_block = []
131 if last_block:
131 if last_block:
132 blocks.append('\n'.join(last_block))
132 blocks.append('\n'.join(last_block))
133
133
134 # Now, apply prefiltering to any one-line block to match the behavior
134 # Now, apply prefiltering to any one-line block to match the behavior
135 # of the interactive terminal
135 # of the interactive terminal
136 final_blocks = []
136 final_blocks = []
137 for block in blocks:
137 for block in blocks:
138 lines = block.splitlines()
138 lines = block.splitlines()
139 if len(lines) == 1:
139 if len(lines) == 1:
140 final_blocks.append(self.shell.prefilter(lines[0]))
140 final_blocks.append(self.shell.prefilter(lines[0]))
141 else:
141 else:
142 final_blocks.append(block)
142 final_blocks.append(block)
143
143
144 # We now have the final version of the input code as a list of blocks,
144 # We now have the final version of the input code as a list of blocks,
145 # with all inputsplitter transformations applied and single-line blocks
145 # with all inputsplitter transformations applied and single-line blocks
146 # run through prefilter. For further processing, turn into a single
146 # run through prefilter. For further processing, turn into a single
147 # string as the rest of our apis use string inputs.
147 # string as the rest of our apis use string inputs.
148 return '\n'.join(final_blocks)
148 return '\n'.join(final_blocks)
149
149
150 def store_or_execute(self, block, name):
150 def store_or_execute(self, block, name):
151 """ Execute a block, or store it in a variable, per the user's request.
151 """ Execute a block, or store it in a variable, per the user's request.
152 """
152 """
153
153
154 b = self.cleanup_input(block)
154 b = self.cleanup_input(block)
155 if name:
155 if name:
156 # If storing it for further editing
156 # If storing it for further editing
157 self.shell.user_ns[name] = SList(b.splitlines())
157 self.shell.user_ns[name] = SList(b.splitlines())
158 print("Block assigned to '%s'" % name)
158 print("Block assigned to '%s'" % name)
159 else:
159 else:
160 self.shell.user_ns['pasted_block'] = b
160 self.shell.user_ns['pasted_block'] = b
161 self.shell.using_paste_magics = True
161 self.shell.using_paste_magics = True
162 try:
162 try:
163 self.shell.run_cell(b)
163 self.shell.run_cell(b)
164 finally:
164 finally:
165 self.shell.using_paste_magics = False
165 self.shell.using_paste_magics = False
166
166
167 def rerun_pasted(self, name='pasted_block'):
167 def rerun_pasted(self, name='pasted_block'):
168 """ Rerun a previously pasted command.
168 """ Rerun a previously pasted command.
169 """
169 """
170 b = self.shell.user_ns.get(name)
170 b = self.shell.user_ns.get(name)
171
171
172 # Sanity checks
172 # Sanity checks
173 if b is None:
173 if b is None:
174 raise UsageError('No previous pasted block available')
174 raise UsageError('No previous pasted block available')
175 if not isinstance(b, basestring):
175 if not isinstance(b, basestring):
176 raise UsageError(
176 raise UsageError(
177 "Variable 'pasted_block' is not a string, can't execute")
177 "Variable 'pasted_block' is not a string, can't execute")
178
178
179 print("Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)))
179 print("Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)))
180 self.shell.run_cell(b)
180 self.shell.run_cell(b)
181
181
182 @line_magic
182 @line_magic
183 def autoindent(self, parameter_s = ''):
183 def autoindent(self, parameter_s = ''):
184 """Toggle autoindent on/off (if available)."""
184 """Toggle autoindent on/off (if available)."""
185
185
186 self.shell.set_autoindent()
186 self.shell.set_autoindent()
187 print("Automatic indentation is:",['OFF','ON'][self.shell.autoindent])
187 print("Automatic indentation is:",['OFF','ON'][self.shell.autoindent])
188
188
189 @skip_doctest
189 @skip_doctest
190 @line_magic
190 @line_magic
191 def cpaste(self, parameter_s=''):
191 def cpaste(self, parameter_s=''):
192 """Paste & execute a pre-formatted code block from clipboard.
192 """Paste & execute a pre-formatted code block from clipboard.
193
193
194 You must terminate the block with '--' (two minus-signs) or Ctrl-D
194 You must terminate the block with '--' (two minus-signs) or Ctrl-D
195 alone on the line. You can also provide your own sentinel with '%paste
195 alone on the line. You can also provide your own sentinel with '%paste
196 -s %%' ('%%' is the new sentinel for this operation)
196 -s %%' ('%%' is the new sentinel for this operation)
197
197
198 The block is dedented prior to execution to enable execution of method
198 The block is dedented prior to execution to enable execution of method
199 definitions. '>' and '+' characters at the beginning of a line are
199 definitions. '>' and '+' characters at the beginning of a line are
200 ignored, to allow pasting directly from e-mails, diff files and
200 ignored, to allow pasting directly from e-mails, diff files and
201 doctests (the '...' continuation prompt is also stripped). The
201 doctests (the '...' continuation prompt is also stripped). The
202 executed block is also assigned to variable named 'pasted_block' for
202 executed block is also assigned to variable named 'pasted_block' for
203 later editing with '%edit pasted_block'.
203 later editing with '%edit pasted_block'.
204
204
205 You can also pass a variable name as an argument, e.g. '%cpaste foo'.
205 You can also pass a variable name as an argument, e.g. '%cpaste foo'.
206 This assigns the pasted block to variable 'foo' as string, without
206 This assigns the pasted block to variable 'foo' as string, without
207 dedenting or executing it (preceding >>> and + is still stripped)
207 dedenting or executing it (preceding >>> and + is still stripped)
208
208
209 '%cpaste -r' re-executes the block previously entered by cpaste.
209 '%cpaste -r' re-executes the block previously entered by cpaste.
210
210
211 Do not be alarmed by garbled output on Windows (it's a readline bug).
211 Do not be alarmed by garbled output on Windows (it's a readline bug).
212 Just press enter and type -- (and press enter again) and the block
212 Just press enter and type -- (and press enter again) and the block
213 will be what was just pasted.
213 will be what was just pasted.
214
214
215 IPython statements (magics, shell escapes) are not supported (yet).
215 IPython statements (magics, shell escapes) are not supported (yet).
216
216
217 See also
217 See also
218 --------
218 --------
219 paste: automatically pull code from clipboard.
219 paste: automatically pull code from clipboard.
220
220
221 Examples
221 Examples
222 --------
222 --------
223 ::
223 ::
224
224
225 In [8]: %cpaste
225 In [8]: %cpaste
226 Pasting code; enter '--' alone on the line to stop.
226 Pasting code; enter '--' alone on the line to stop.
227 :>>> a = ["world!", "Hello"]
227 :>>> a = ["world!", "Hello"]
228 :>>> print " ".join(sorted(a))
228 :>>> print " ".join(sorted(a))
229 :--
229 :--
230 Hello world!
230 Hello world!
231 """
231 """
232 opts, name = self.parse_options(parameter_s, 'rs:', mode='string')
232 opts, name = self.parse_options(parameter_s, 'rs:', mode='string')
233 if 'r' in opts:
233 if 'r' in opts:
234 self.rerun_pasted()
234 self.rerun_pasted()
235 return
235 return
236
236
237 sentinel = opts.get('s', '--')
237 sentinel = opts.get('s', '--')
238 block = '\n'.join(get_pasted_lines(sentinel))
238 block = '\n'.join(get_pasted_lines(sentinel))
239 self.store_or_execute(block, name)
239 self.store_or_execute(block, name)
240
240
241 @line_magic
241 @line_magic
242 def paste(self, parameter_s=''):
242 def paste(self, parameter_s=''):
243 """Paste & execute a pre-formatted code block from clipboard.
243 """Paste & execute a pre-formatted code block from clipboard.
244
244
245 The text is pulled directly from the clipboard without user
245 The text is pulled directly from the clipboard without user
246 intervention and printed back on the screen before execution (unless
246 intervention and printed back on the screen before execution (unless
247 the -q flag is given to force quiet mode).
247 the -q flag is given to force quiet mode).
248
248
249 The block is dedented prior to execution to enable execution of method
249 The block is dedented prior to execution to enable execution of method
250 definitions. '>' and '+' characters at the beginning of a line are
250 definitions. '>' and '+' characters at the beginning of a line are
251 ignored, to allow pasting directly from e-mails, diff files and
251 ignored, to allow pasting directly from e-mails, diff files and
252 doctests (the '...' continuation prompt is also stripped). The
252 doctests (the '...' continuation prompt is also stripped). The
253 executed block is also assigned to variable named 'pasted_block' for
253 executed block is also assigned to variable named 'pasted_block' for
254 later editing with '%edit pasted_block'.
254 later editing with '%edit pasted_block'.
255
255
256 You can also pass a variable name as an argument, e.g. '%paste foo'.
256 You can also pass a variable name as an argument, e.g. '%paste foo'.
257 This assigns the pasted block to variable 'foo' as string, without
257 This assigns the pasted block to variable 'foo' as string, without
258 executing it (preceding >>> and + is still stripped).
258 executing it (preceding >>> and + is still stripped).
259
259
260 Options
260 Options
261 -------
261 -------
262
262
263 -r: re-executes the block previously entered by cpaste.
263 -r: re-executes the block previously entered by cpaste.
264
264
265 -q: quiet mode: do not echo the pasted text back to the terminal.
265 -q: quiet mode: do not echo the pasted text back to the terminal.
266
266
267 IPython statements (magics, shell escapes) are not supported (yet).
267 IPython statements (magics, shell escapes) are not supported (yet).
268
268
269 See also
269 See also
270 --------
270 --------
271 cpaste: manually paste code into terminal until you mark its end.
271 cpaste: manually paste code into terminal until you mark its end.
272 """
272 """
273 opts, name = self.parse_options(parameter_s, 'rq', mode='string')
273 opts, name = self.parse_options(parameter_s, 'rq', mode='string')
274 if 'r' in opts:
274 if 'r' in opts:
275 self.rerun_pasted()
275 self.rerun_pasted()
276 return
276 return
277 try:
277 try:
278 block = self.shell.hooks.clipboard_get()
278 block = self.shell.hooks.clipboard_get()
279 except TryNext as clipboard_exc:
279 except TryNext as clipboard_exc:
280 message = getattr(clipboard_exc, 'args')
280 message = getattr(clipboard_exc, 'args')
281 if message:
281 if message:
282 error(message[0])
282 error(message[0])
283 else:
283 else:
284 error('Could not get text from the clipboard.')
284 error('Could not get text from the clipboard.')
285 return
285 return
286
286
287 # By default, echo back to terminal unless quiet mode is requested
287 # By default, echo back to terminal unless quiet mode is requested
288 if 'q' not in opts:
288 if 'q' not in opts:
289 write = self.shell.write
289 write = self.shell.write
290 write(self.shell.pycolorize(block))
290 write(self.shell.pycolorize(block))
291 if not block.endswith('\n'):
291 if not block.endswith('\n'):
292 write('\n')
292 write('\n')
293 write("## -- End pasted text --\n")
293 write("## -- End pasted text --\n")
294
294
295 self.store_or_execute(block, name)
295 self.store_or_execute(block, name)
296
296
297 # Class-level: add a '%cls' magic only on Windows
297 # Class-level: add a '%cls' magic only on Windows
298 if sys.platform == 'win32':
298 if sys.platform == 'win32':
299 @line_magic
299 @line_magic
300 def cls(self, s):
300 def cls(self, s):
301 """Clear screen.
301 """Clear screen.
302 """
302 """
303 os.system("cls")
303 os.system("cls")
304
304
305 #-----------------------------------------------------------------------------
305 #-----------------------------------------------------------------------------
306 # Main class
306 # Main class
307 #-----------------------------------------------------------------------------
307 #-----------------------------------------------------------------------------
308
308
309 class TerminalInteractiveShell(InteractiveShell):
309 class TerminalInteractiveShell(InteractiveShell):
310
310
311 autoedit_syntax = CBool(False, config=True,
311 autoedit_syntax = CBool(False, config=True,
312 help="auto editing of files with syntax errors.")
312 help="auto editing of files with syntax errors.")
313 banner = Unicode('')
313 banner = Unicode('')
314 banner1 = Unicode(default_banner, config=True,
314 banner1 = Unicode(default_banner, config=True,
315 help="""The part of the banner to be printed before the profile"""
315 help="""The part of the banner to be printed before the profile"""
316 )
316 )
317 banner2 = Unicode('', config=True,
317 banner2 = Unicode('', config=True,
318 help="""The part of the banner to be printed after the profile"""
318 help="""The part of the banner to be printed after the profile"""
319 )
319 )
320 confirm_exit = CBool(True, config=True,
320 confirm_exit = CBool(True, config=True,
321 help="""
321 help="""
322 Set to confirm when you try to exit IPython with an EOF (Control-D
322 Set to confirm when you try to exit IPython with an EOF (Control-D
323 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
323 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
324 you can force a direct exit without any confirmation.""",
324 you can force a direct exit without any confirmation.""",
325 )
325 )
326 # This display_banner only controls whether or not self.show_banner()
326 # This display_banner only controls whether or not self.show_banner()
327 # is called when mainloop/interact are called. The default is False
327 # is called when mainloop/interact are called. The default is False
328 # because for the terminal based application, the banner behavior
328 # because for the terminal based application, the banner behavior
329 # is controlled by Global.display_banner, which IPythonApp looks at
329 # is controlled by Global.display_banner, which IPythonApp looks at
330 # to determine if *it* should call show_banner() by hand or not.
330 # to determine if *it* should call show_banner() by hand or not.
331 display_banner = CBool(False) # This isn't configurable!
331 display_banner = CBool(False) # This isn't configurable!
332 embedded = CBool(False)
332 embedded = CBool(False)
333 embedded_active = CBool(False)
333 embedded_active = CBool(False)
334 editor = Unicode(get_default_editor(), config=True,
334 editor = Unicode(get_default_editor(), config=True,
335 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
335 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
336 )
336 )
337 pager = Unicode('less', config=True,
337 pager = Unicode('less', config=True,
338 help="The shell program to be used for paging.")
338 help="The shell program to be used for paging.")
339
339
340 screen_length = Integer(0, config=True,
340 screen_length = Integer(0, config=True,
341 help=
341 help=
342 """Number of lines of your screen, used to control printing of very
342 """Number of lines of your screen, used to control printing of very
343 long strings. Strings longer than this number of lines will be sent
343 long strings. Strings longer than this number of lines will be sent
344 through a pager instead of directly printed. The default value for
344 through a pager instead of directly printed. The default value for
345 this is 0, which means IPython will auto-detect your screen size every
345 this is 0, which means IPython will auto-detect your screen size every
346 time it needs to print certain potentially long strings (this doesn't
346 time it needs to print certain potentially long strings (this doesn't
347 change the behavior of the 'print' keyword, it's only triggered
347 change the behavior of the 'print' keyword, it's only triggered
348 internally). If for some reason this isn't working well (it needs
348 internally). If for some reason this isn't working well (it needs
349 curses support), specify it yourself. Otherwise don't change the
349 curses support), specify it yourself. Otherwise don't change the
350 default.""",
350 default.""",
351 )
351 )
352 term_title = CBool(False, config=True,
352 term_title = CBool(False, config=True,
353 help="Enable auto setting the terminal title."
353 help="Enable auto setting the terminal title."
354 )
354 )
355
355
356 # This `using_paste_magics` is used to detect whether the code is being
356 # This `using_paste_magics` is used to detect whether the code is being
357 # executed via paste magics functions
357 # executed via paste magics functions
358 using_paste_magics = CBool(False)
358 using_paste_magics = CBool(False)
359
359
360 # In the terminal, GUI control is done via PyOS_InputHook
360 # In the terminal, GUI control is done via PyOS_InputHook
361 @staticmethod
361 @staticmethod
362 def enable_gui(gui=None, app=None):
362 def enable_gui(gui=None, app=None):
363 """Switch amongst GUI input hooks by name.
363 """Switch amongst GUI input hooks by name.
364 """
364 """
365 # Deferred import
365 # Deferred import
366 from IPython.lib.inputhook import enable_gui as real_enable_gui
366 from IPython.lib.inputhook import enable_gui as real_enable_gui
367 return real_enable_gui(gui, app)
367 return real_enable_gui(gui, app)
368
368
369 def __init__(self, config=None, ipython_dir=None, profile_dir=None,
369 def __init__(self, config=None, ipython_dir=None, profile_dir=None,
370 user_ns=None, user_module=None, custom_exceptions=((),None),
370 user_ns=None, user_module=None, custom_exceptions=((),None),
371 usage=None, banner1=None, banner2=None, display_banner=None):
371 usage=None, banner1=None, banner2=None, display_banner=None):
372
372
373 super(TerminalInteractiveShell, self).__init__(
373 super(TerminalInteractiveShell, self).__init__(
374 config=config, ipython_dir=ipython_dir, profile_dir=profile_dir, user_ns=user_ns,
374 config=config, ipython_dir=ipython_dir, profile_dir=profile_dir, user_ns=user_ns,
375 user_module=user_module, custom_exceptions=custom_exceptions
375 user_module=user_module, custom_exceptions=custom_exceptions
376 )
376 )
377 # use os.system instead of utils.process.system by default,
377 # use os.system instead of utils.process.system by default,
378 # because piped system doesn't make sense in the Terminal:
378 # because piped system doesn't make sense in the Terminal:
379 self.system = self.system_raw
379 self.system = self.system_raw
380
380
381 self.init_term_title()
381 self.init_term_title()
382 self.init_usage(usage)
382 self.init_usage(usage)
383 self.init_banner(banner1, banner2, display_banner)
383 self.init_banner(banner1, banner2, display_banner)
384
384
385 #-------------------------------------------------------------------------
385 #-------------------------------------------------------------------------
386 # Overrides of init stages
386 # Overrides of init stages
387 #-------------------------------------------------------------------------
387 #-------------------------------------------------------------------------
388
388
389 def init_display_formatter(self):
389 def init_display_formatter(self):
390 super(TerminalInteractiveShell, self).init_display_formatter()
390 super(TerminalInteractiveShell, self).init_display_formatter()
391 # terminal only supports plaintext
391 # terminal only supports plaintext
392 self.display_formatter.active_types = ['text/plain']
392 self.display_formatter.active_types = ['text/plain']
393
393
394 #-------------------------------------------------------------------------
394 #-------------------------------------------------------------------------
395 # Things related to the terminal
395 # Things related to the terminal
396 #-------------------------------------------------------------------------
396 #-------------------------------------------------------------------------
397
397
398 @property
398 @property
399 def usable_screen_length(self):
399 def usable_screen_length(self):
400 if self.screen_length == 0:
400 if self.screen_length == 0:
401 return 0
401 return 0
402 else:
402 else:
403 num_lines_bot = self.separate_in.count('\n')+1
403 num_lines_bot = self.separate_in.count('\n')+1
404 return self.screen_length - num_lines_bot
404 return self.screen_length - num_lines_bot
405
405
406 def init_term_title(self):
406 def init_term_title(self):
407 # Enable or disable the terminal title.
407 # Enable or disable the terminal title.
408 if self.term_title:
408 if self.term_title:
409 toggle_set_term_title(True)
409 toggle_set_term_title(True)
410 set_term_title('IPython: ' + abbrev_cwd())
410 set_term_title('IPython: ' + abbrev_cwd())
411 else:
411 else:
412 toggle_set_term_title(False)
412 toggle_set_term_title(False)
413
413
414 #-------------------------------------------------------------------------
414 #-------------------------------------------------------------------------
415 # Things related to aliases
415 # Things related to aliases
416 #-------------------------------------------------------------------------
416 #-------------------------------------------------------------------------
417
417
418 def init_alias(self):
418 def init_alias(self):
419 # The parent class defines aliases that can be safely used with any
419 # The parent class defines aliases that can be safely used with any
420 # frontend.
420 # frontend.
421 super(TerminalInteractiveShell, self).init_alias()
421 super(TerminalInteractiveShell, self).init_alias()
422
422
423 # Now define aliases that only make sense on the terminal, because they
423 # Now define aliases that only make sense on the terminal, because they
424 # need direct access to the console in a way that we can't emulate in
424 # need direct access to the console in a way that we can't emulate in
425 # GUI or web frontend
425 # GUI or web frontend
426 if os.name == 'posix':
426 if os.name == 'posix':
427 aliases = [('clear', 'clear'), ('more', 'more'), ('less', 'less'),
427 aliases = [('clear', 'clear'), ('more', 'more'), ('less', 'less'),
428 ('man', 'man')]
428 ('man', 'man')]
429 elif os.name == 'nt':
429 elif os.name == 'nt':
430 aliases = [('cls', 'cls')]
430 aliases = [('cls', 'cls')]
431
431
432
432
433 for name, cmd in aliases:
433 for name, cmd in aliases:
434 self.alias_manager.define_alias(name, cmd)
434 self.alias_manager.define_alias(name, cmd)
435
435
436 #-------------------------------------------------------------------------
436 #-------------------------------------------------------------------------
437 # Things related to the banner and usage
437 # Things related to the banner and usage
438 #-------------------------------------------------------------------------
438 #-------------------------------------------------------------------------
439
439
440 def _banner1_changed(self):
440 def _banner1_changed(self):
441 self.compute_banner()
441 self.compute_banner()
442
442
443 def _banner2_changed(self):
443 def _banner2_changed(self):
444 self.compute_banner()
444 self.compute_banner()
445
445
446 def _term_title_changed(self, name, new_value):
446 def _term_title_changed(self, name, new_value):
447 self.init_term_title()
447 self.init_term_title()
448
448
449 def init_banner(self, banner1, banner2, display_banner):
449 def init_banner(self, banner1, banner2, display_banner):
450 if banner1 is not None:
450 if banner1 is not None:
451 self.banner1 = banner1
451 self.banner1 = banner1
452 if banner2 is not None:
452 if banner2 is not None:
453 self.banner2 = banner2
453 self.banner2 = banner2
454 if display_banner is not None:
454 if display_banner is not None:
455 self.display_banner = display_banner
455 self.display_banner = display_banner
456 self.compute_banner()
456 self.compute_banner()
457
457
458 def show_banner(self, banner=None):
458 def show_banner(self, banner=None):
459 if banner is None:
459 if banner is None:
460 banner = self.banner
460 banner = self.banner
461 self.write(banner)
461 self.write(banner)
462
462
463 def compute_banner(self):
463 def compute_banner(self):
464 self.banner = self.banner1
464 self.banner = self.banner1
465 if self.profile and self.profile != 'default':
465 if self.profile and self.profile != 'default':
466 self.banner += '\nIPython profile: %s\n' % self.profile
466 self.banner += '\nIPython profile: %s\n' % self.profile
467 if self.banner2:
467 if self.banner2:
468 self.banner += '\n' + self.banner2
468 self.banner += '\n' + self.banner2
469
469
470 def init_usage(self, usage=None):
470 def init_usage(self, usage=None):
471 if usage is None:
471 if usage is None:
472 self.usage = interactive_usage
472 self.usage = interactive_usage
473 else:
473 else:
474 self.usage = usage
474 self.usage = usage
475
475
476 #-------------------------------------------------------------------------
476 #-------------------------------------------------------------------------
477 # Mainloop and code execution logic
477 # Mainloop and code execution logic
478 #-------------------------------------------------------------------------
478 #-------------------------------------------------------------------------
479
479
480 def mainloop(self, display_banner=None):
480 def mainloop(self, display_banner=None):
481 """Start the mainloop.
481 """Start the mainloop.
482
482
483 If an optional banner argument is given, it will override the
483 If an optional banner argument is given, it will override the
484 internally created default banner.
484 internally created default banner.
485 """
485 """
486
486
487 with nested(self.builtin_trap, self.display_trap):
487 with nested(self.builtin_trap, self.display_trap):
488
488
489 while 1:
489 while 1:
490 try:
490 try:
491 self.interact(display_banner=display_banner)
491 self.interact(display_banner=display_banner)
492 #self.interact_with_readline()
492 #self.interact_with_readline()
493 # XXX for testing of a readline-decoupled repl loop, call
493 # XXX for testing of a readline-decoupled repl loop, call
494 # interact_with_readline above
494 # interact_with_readline above
495 break
495 break
496 except KeyboardInterrupt:
496 except KeyboardInterrupt:
497 # this should not be necessary, but KeyboardInterrupt
497 # this should not be necessary, but KeyboardInterrupt
498 # handling seems rather unpredictable...
498 # handling seems rather unpredictable...
499 self.write("\nKeyboardInterrupt in interact()\n")
499 self.write("\nKeyboardInterrupt in interact()\n")
500
500
501 def _replace_rlhist_multiline(self, source_raw, hlen_before_cell):
501 def _replace_rlhist_multiline(self, source_raw, hlen_before_cell):
502 """Store multiple lines as a single entry in history"""
502 """Store multiple lines as a single entry in history"""
503
503
504 # do nothing without readline or disabled multiline
504 # do nothing without readline or disabled multiline
505 if not self.has_readline or not self.multiline_history:
505 if not self.has_readline or not self.multiline_history:
506 return hlen_before_cell
506 return hlen_before_cell
507
507
508 # windows rl has no remove_history_item
508 # windows rl has no remove_history_item
509 if not hasattr(self.readline, "remove_history_item"):
509 if not hasattr(self.readline, "remove_history_item"):
510 return hlen_before_cell
510 return hlen_before_cell
511
511
512 # skip empty cells
512 # skip empty cells
513 if not source_raw.rstrip():
513 if not source_raw.rstrip():
514 return hlen_before_cell
514 return hlen_before_cell
515
515
516 # nothing changed do nothing, e.g. when rl removes consecutive dups
516 # nothing changed do nothing, e.g. when rl removes consecutive dups
517 hlen = self.readline.get_current_history_length()
517 hlen = self.readline.get_current_history_length()
518 if hlen == hlen_before_cell:
518 if hlen == hlen_before_cell:
519 return hlen_before_cell
519 return hlen_before_cell
520
520
521 for i in range(hlen - hlen_before_cell):
521 for i in range(hlen - hlen_before_cell):
522 self.readline.remove_history_item(hlen - i - 1)
522 self.readline.remove_history_item(hlen - i - 1)
523 stdin_encoding = get_stream_enc(sys.stdin, 'utf-8')
523 stdin_encoding = get_stream_enc(sys.stdin, 'utf-8')
524 self.readline.add_history(py3compat.unicode_to_str(source_raw.rstrip(),
524 self.readline.add_history(py3compat.unicode_to_str(source_raw.rstrip(),
525 stdin_encoding))
525 stdin_encoding))
526 return self.readline.get_current_history_length()
526 return self.readline.get_current_history_length()
527
527
528 def interact(self, display_banner=None):
528 def interact(self, display_banner=None):
529 """Closely emulate the interactive Python console."""
529 """Closely emulate the interactive Python console."""
530
530
531 # batch run -> do not interact
531 # batch run -> do not interact
532 if self.exit_now:
532 if self.exit_now:
533 return
533 return
534
534
535 if display_banner is None:
535 if display_banner is None:
536 display_banner = self.display_banner
536 display_banner = self.display_banner
537
537
538 if isinstance(display_banner, basestring):
538 if isinstance(display_banner, basestring):
539 self.show_banner(display_banner)
539 self.show_banner(display_banner)
540 elif display_banner:
540 elif display_banner:
541 self.show_banner()
541 self.show_banner()
542
542
543 more = False
543 more = False
544
544
545 if self.has_readline:
545 if self.has_readline:
546 self.readline_startup_hook(self.pre_readline)
546 self.readline_startup_hook(self.pre_readline)
547 hlen_b4_cell = self.readline.get_current_history_length()
547 hlen_b4_cell = self.readline.get_current_history_length()
548 else:
548 else:
549 hlen_b4_cell = 0
549 hlen_b4_cell = 0
550 # exit_now is set by a call to %Exit or %Quit, through the
550 # exit_now is set by a call to %Exit or %Quit, through the
551 # ask_exit callback.
551 # ask_exit callback.
552
552
553 while not self.exit_now:
553 while not self.exit_now:
554 self.hooks.pre_prompt_hook()
554 self.hooks.pre_prompt_hook()
555 if more:
555 if more:
556 try:
556 try:
557 prompt = self.prompt_manager.render('in2')
557 prompt = self.prompt_manager.render('in2')
558 except:
558 except:
559 self.showtraceback()
559 self.showtraceback()
560 if self.autoindent:
560 if self.autoindent:
561 self.rl_do_indent = True
561 self.rl_do_indent = True
562
562
563 else:
563 else:
564 try:
564 try:
565 prompt = self.separate_in + self.prompt_manager.render('in')
565 prompt = self.separate_in + self.prompt_manager.render('in')
566 except:
566 except:
567 self.showtraceback()
567 self.showtraceback()
568 try:
568 try:
569 line = self.raw_input(prompt)
569 line = self.raw_input(prompt)
570 if self.exit_now:
570 if self.exit_now:
571 # quick exit on sys.std[in|out] close
571 # quick exit on sys.std[in|out] close
572 break
572 break
573 if self.autoindent:
573 if self.autoindent:
574 self.rl_do_indent = False
574 self.rl_do_indent = False
575
575
576 except KeyboardInterrupt:
576 except KeyboardInterrupt:
577 #double-guard against keyboardinterrupts during kbdint handling
577 #double-guard against keyboardinterrupts during kbdint handling
578 try:
578 try:
579 self.write('\nKeyboardInterrupt\n')
579 self.write('\nKeyboardInterrupt\n')
580 source_raw = self.input_splitter.source_raw_reset()[1]
580 source_raw = self.input_splitter.source_raw_reset()[1]
581 hlen_b4_cell = \
581 hlen_b4_cell = \
582 self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
582 self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
583 more = False
583 more = False
584 except KeyboardInterrupt:
584 except KeyboardInterrupt:
585 pass
585 pass
586 except EOFError:
586 except EOFError:
587 if self.autoindent:
587 if self.autoindent:
588 self.rl_do_indent = False
588 self.rl_do_indent = False
589 if self.has_readline:
589 if self.has_readline:
590 self.readline_startup_hook(None)
590 self.readline_startup_hook(None)
591 self.write('\n')
591 self.write('\n')
592 self.exit()
592 self.exit()
593 except bdb.BdbQuit:
593 except bdb.BdbQuit:
594 warn('The Python debugger has exited with a BdbQuit exception.\n'
594 warn('The Python debugger has exited with a BdbQuit exception.\n'
595 'Because of how pdb handles the stack, it is impossible\n'
595 'Because of how pdb handles the stack, it is impossible\n'
596 'for IPython to properly format this particular exception.\n'
596 'for IPython to properly format this particular exception.\n'
597 'IPython will resume normal operation.')
597 'IPython will resume normal operation.')
598 except:
598 except:
599 # exceptions here are VERY RARE, but they can be triggered
599 # exceptions here are VERY RARE, but they can be triggered
600 # asynchronously by signal handlers, for example.
600 # asynchronously by signal handlers, for example.
601 self.showtraceback()
601 self.showtraceback()
602 else:
602 else:
603 self.input_splitter.push(line)
603 self.input_splitter.push(line)
604 more = self.input_splitter.push_accepts_more()
604 more = self.input_splitter.push_accepts_more()
605 if (self.SyntaxTB.last_syntax_error and
605 if (self.SyntaxTB.last_syntax_error and
606 self.autoedit_syntax):
606 self.autoedit_syntax):
607 self.edit_syntax_error()
607 self.edit_syntax_error()
608 if not more:
608 if not more:
609 source_raw = self.input_splitter.source_raw_reset()[1]
609 source_raw = self.input_splitter.source_raw_reset()[1]
610 self.run_cell(source_raw, store_history=True)
610 self.run_cell(source_raw, store_history=True)
611 hlen_b4_cell = \
611 hlen_b4_cell = \
612 self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
612 self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
613
613
614 # Turn off the exit flag, so the mainloop can be restarted if desired
614 # Turn off the exit flag, so the mainloop can be restarted if desired
615 self.exit_now = False
615 self.exit_now = False
616
616
617 def raw_input(self, prompt=''):
617 def raw_input(self, prompt=''):
618 """Write a prompt and read a line.
618 """Write a prompt and read a line.
619
619
620 The returned line does not include the trailing newline.
620 The returned line does not include the trailing newline.
621 When the user enters the EOF key sequence, EOFError is raised.
621 When the user enters the EOF key sequence, EOFError is raised.
622
622
623 Optional inputs:
623 Optional inputs:
624
624
625 - prompt(''): a string to be printed to prompt the user.
625 - prompt(''): a string to be printed to prompt the user.
626
626
627 - continue_prompt(False): whether this line is the first one or a
627 - continue_prompt(False): whether this line is the first one or a
628 continuation in a sequence of inputs.
628 continuation in a sequence of inputs.
629 """
629 """
630 # Code run by the user may have modified the readline completer state.
630 # Code run by the user may have modified the readline completer state.
631 # We must ensure that our completer is back in place.
631 # We must ensure that our completer is back in place.
632
632
633 if self.has_readline:
633 if self.has_readline:
634 self.set_readline_completer()
634 self.set_readline_completer()
635
635
636 # raw_input expects str, but we pass it unicode sometimes
636 # raw_input expects str, but we pass it unicode sometimes
637 prompt = py3compat.cast_bytes_py2(prompt)
637 prompt = py3compat.cast_bytes_py2(prompt)
638
638
639 try:
639 try:
640 line = py3compat.str_to_unicode(self.raw_input_original(prompt))
640 line = py3compat.str_to_unicode(self.raw_input_original(prompt))
641 except ValueError:
641 except ValueError:
642 warn("\n********\nYou or a %run:ed script called sys.stdin.close()"
642 warn("\n********\nYou or a %run:ed script called sys.stdin.close()"
643 " or sys.stdout.close()!\nExiting IPython!\n")
643 " or sys.stdout.close()!\nExiting IPython!\n")
644 self.ask_exit()
644 self.ask_exit()
645 return ""
645 return ""
646
646
647 # Try to be reasonably smart about not re-indenting pasted input more
647 # Try to be reasonably smart about not re-indenting pasted input more
648 # than necessary. We do this by trimming out the auto-indent initial
648 # than necessary. We do this by trimming out the auto-indent initial
649 # spaces, if the user's actual input started itself with whitespace.
649 # spaces, if the user's actual input started itself with whitespace.
650 if self.autoindent:
650 if self.autoindent:
651 if num_ini_spaces(line) > self.indent_current_nsp:
651 if num_ini_spaces(line) > self.indent_current_nsp:
652 line = line[self.indent_current_nsp:]
652 line = line[self.indent_current_nsp:]
653 self.indent_current_nsp = 0
653 self.indent_current_nsp = 0
654
654
655 return line
655 return line
656
656
657 #-------------------------------------------------------------------------
657 #-------------------------------------------------------------------------
658 # Methods to support auto-editing of SyntaxErrors.
658 # Methods to support auto-editing of SyntaxErrors.
659 #-------------------------------------------------------------------------
659 #-------------------------------------------------------------------------
660
660
661 def edit_syntax_error(self):
661 def edit_syntax_error(self):
662 """The bottom half of the syntax error handler called in the main loop.
662 """The bottom half of the syntax error handler called in the main loop.
663
663
664 Loop until syntax error is fixed or user cancels.
664 Loop until syntax error is fixed or user cancels.
665 """
665 """
666
666
667 while self.SyntaxTB.last_syntax_error:
667 while self.SyntaxTB.last_syntax_error:
668 # copy and clear last_syntax_error
668 # copy and clear last_syntax_error
669 err = self.SyntaxTB.clear_err_state()
669 err = self.SyntaxTB.clear_err_state()
670 if not self._should_recompile(err):
670 if not self._should_recompile(err):
671 return
671 return
672 try:
672 try:
673 # may set last_syntax_error again if a SyntaxError is raised
673 # may set last_syntax_error again if a SyntaxError is raised
674 self.safe_execfile(err.filename,self.user_ns)
674 self.safe_execfile(err.filename,self.user_ns)
675 except:
675 except:
676 self.showtraceback()
676 self.showtraceback()
677 else:
677 else:
678 try:
678 try:
679 f = open(err.filename)
679 f = open(err.filename)
680 try:
680 try:
681 # This should be inside a display_trap block and I
681 # This should be inside a display_trap block and I
682 # think it is.
682 # think it is.
683 sys.displayhook(f.read())
683 sys.displayhook(f.read())
684 finally:
684 finally:
685 f.close()
685 f.close()
686 except:
686 except:
687 self.showtraceback()
687 self.showtraceback()
688
688
689 def _should_recompile(self,e):
689 def _should_recompile(self,e):
690 """Utility routine for edit_syntax_error"""
690 """Utility routine for edit_syntax_error"""
691
691
692 if e.filename in ('<ipython console>','<input>','<string>',
692 if e.filename in ('<ipython console>','<input>','<string>',
693 '<console>','<BackgroundJob compilation>',
693 '<console>','<BackgroundJob compilation>',
694 None):
694 None):
695
695
696 return False
696 return False
697 try:
697 try:
698 if (self.autoedit_syntax and
698 if (self.autoedit_syntax and
699 not self.ask_yes_no('Return to editor to correct syntax error? '
699 not self.ask_yes_no('Return to editor to correct syntax error? '
700 '[Y/n] ','y')):
700 '[Y/n] ','y')):
701 return False
701 return False
702 except EOFError:
702 except EOFError:
703 return False
703 return False
704
704
705 def int0(x):
705 def int0(x):
706 try:
706 try:
707 return int(x)
707 return int(x)
708 except TypeError:
708 except TypeError:
709 return 0
709 return 0
710 # always pass integer line and offset values to editor hook
710 # always pass integer line and offset values to editor hook
711 try:
711 try:
712 self.hooks.fix_error_editor(e.filename,
712 self.hooks.fix_error_editor(e.filename,
713 int0(e.lineno),int0(e.offset),e.msg)
713 int0(e.lineno),int0(e.offset),e.msg)
714 except TryNext:
714 except TryNext:
715 warn('Could not open editor')
715 warn('Could not open editor')
716 return False
716 return False
717 return True
717 return True
718
718
719 #-------------------------------------------------------------------------
719 #-------------------------------------------------------------------------
720 # Things related to exiting
720 # Things related to exiting
721 #-------------------------------------------------------------------------
721 #-------------------------------------------------------------------------
722
722
723 def ask_exit(self):
723 def ask_exit(self):
724 """ Ask the shell to exit. Can be overiden and used as a callback. """
724 """ Ask the shell to exit. Can be overiden and used as a callback. """
725 self.exit_now = True
725 self.exit_now = True
726
726
727 def exit(self):
727 def exit(self):
728 """Handle interactive exit.
728 """Handle interactive exit.
729
729
730 This method calls the ask_exit callback."""
730 This method calls the ask_exit callback."""
731 if self.confirm_exit:
731 if self.confirm_exit:
732 if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'):
732 if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'):
733 self.ask_exit()
733 self.ask_exit()
734 else:
734 else:
735 self.ask_exit()
735 self.ask_exit()
736
736
737 #-------------------------------------------------------------------------
737 #-------------------------------------------------------------------------
738 # Things related to magics
738 # Things related to magics
739 #-------------------------------------------------------------------------
739 #-------------------------------------------------------------------------
740
740
741 def init_magics(self):
741 def init_magics(self):
742 super(TerminalInteractiveShell, self).init_magics()
742 super(TerminalInteractiveShell, self).init_magics()
743 self.register_magics(TerminalMagics)
743 self.register_magics(TerminalMagics)
744
744
745 def showindentationerror(self):
745 def showindentationerror(self):
746 super(TerminalInteractiveShell, self).showindentationerror()
746 super(TerminalInteractiveShell, self).showindentationerror()
747 if not self.using_paste_magics:
747 if not self.using_paste_magics:
748 print("If you want to paste code into IPython, try the "
748 print("If you want to paste code into IPython, try the "
749 "%paste and %cpaste magic functions.")
749 "%paste and %cpaste magic functions.")
750
750
751
751
752 InteractiveShellABC.register(TerminalInteractiveShell)
752 InteractiveShellABC.register(TerminalInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now