##// END OF EJS Templates
always pass single lines to transform.push...
MinRK -
Show More
@@ -1,638 +1,646 b''
1 """Input handling and transformation machinery.
1 """Input handling and transformation machinery.
2
2
3 The first class in this module, :class:`InputSplitter`, is designed to tell when
3 The first class in this module, :class:`InputSplitter`, is designed to tell when
4 input from a line-oriented frontend is complete and should be executed, and when
4 input from a line-oriented frontend is complete and should be executed, and when
5 the user should be prompted for another line of code instead. The name 'input
5 the user should be prompted for another line of code instead. The name 'input
6 splitter' is largely for historical reasons.
6 splitter' is largely for historical reasons.
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 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
10 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
11 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
11 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
12 and stores the results.
12 and stores the results.
13
13
14 For more details, see the class docstrings below.
14 For more details, see the class docstrings below.
15
16 Authors
17 -------
18
19 * Fernando Perez
20 * Brian Granger
21 * Thomas Kluyver
22 """
15 """
23 #-----------------------------------------------------------------------------
24 # Copyright (C) 2010 The IPython Development Team
25 #
26 # Distributed under the terms of the BSD License. The full license is in
27 # the file COPYING, distributed as part of this software.
28 #-----------------------------------------------------------------------------
29
16
30 #-----------------------------------------------------------------------------
17 # Copyright (c) IPython Development Team.
31 # Imports
18 # Distributed under the terms of the Modified BSD License.
32 #-----------------------------------------------------------------------------
33 # stdlib
34 import ast
19 import ast
35 import codeop
20 import codeop
36 import re
21 import re
37 import sys
22 import sys
38
23
39 # IPython modules
40 from IPython.utils.py3compat import cast_unicode
24 from IPython.utils.py3compat import cast_unicode
41 from IPython.core.inputtransformer import (leading_indent,
25 from IPython.core.inputtransformer import (leading_indent,
42 classic_prompt,
26 classic_prompt,
43 ipy_prompt,
27 ipy_prompt,
44 strip_encoding_cookie,
28 strip_encoding_cookie,
45 cellmagic,
29 cellmagic,
46 assemble_logical_lines,
30 assemble_logical_lines,
47 help_end,
31 help_end,
48 escaped_commands,
32 escaped_commands,
49 assign_from_magic,
33 assign_from_magic,
50 assign_from_system,
34 assign_from_system,
51 assemble_python_lines,
35 assemble_python_lines,
52 )
36 )
53
37
54 # These are available in this module for backwards compatibility.
38 # These are available in this module for backwards compatibility.
55 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
39 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
56 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
40 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
57 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
41 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
58
42
59 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
60 # Utilities
44 # Utilities
61 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
62
46
63 # FIXME: These are general-purpose utilities that later can be moved to the
47 # FIXME: These are general-purpose utilities that later can be moved to the
64 # general ward. Kept here for now because we're being very strict about test
48 # general ward. Kept here for now because we're being very strict about test
65 # coverage with this code, and this lets us ensure that we keep 100% coverage
49 # coverage with this code, and this lets us ensure that we keep 100% coverage
66 # while developing.
50 # while developing.
67
51
68 # compiled regexps for autoindent management
52 # compiled regexps for autoindent management
69 dedent_re = re.compile('|'.join([
53 dedent_re = re.compile('|'.join([
70 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
54 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
71 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
55 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
72 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
56 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
73 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
57 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
74 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
58 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
75 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
59 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
76 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
60 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
77 ]))
61 ]))
78 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
62 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
79
63
80 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
64 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
81 # before pure comments
65 # before pure comments
82 comment_line_re = re.compile('^\s*\#')
66 comment_line_re = re.compile('^\s*\#')
83
67
84
68
85 def num_ini_spaces(s):
69 def num_ini_spaces(s):
86 """Return the number of initial spaces in a string.
70 """Return the number of initial spaces in a string.
87
71
88 Note that tabs are counted as a single space. For now, we do *not* support
72 Note that tabs are counted as a single space. For now, we do *not* support
89 mixing of tabs and spaces in the user's input.
73 mixing of tabs and spaces in the user's input.
90
74
91 Parameters
75 Parameters
92 ----------
76 ----------
93 s : string
77 s : string
94
78
95 Returns
79 Returns
96 -------
80 -------
97 n : int
81 n : int
98 """
82 """
99
83
100 ini_spaces = ini_spaces_re.match(s)
84 ini_spaces = ini_spaces_re.match(s)
101 if ini_spaces:
85 if ini_spaces:
102 return ini_spaces.end()
86 return ini_spaces.end()
103 else:
87 else:
104 return 0
88 return 0
105
89
106 def last_blank(src):
90 def last_blank(src):
107 """Determine if the input source ends in a blank.
91 """Determine if the input source ends in a blank.
108
92
109 A blank is either a newline or a line consisting of whitespace.
93 A blank is either a newline or a line consisting of whitespace.
110
94
111 Parameters
95 Parameters
112 ----------
96 ----------
113 src : string
97 src : string
114 A single or multiline string.
98 A single or multiline string.
115 """
99 """
116 if not src: return False
100 if not src: return False
117 ll = src.splitlines()[-1]
101 ll = src.splitlines()[-1]
118 return (ll == '') or ll.isspace()
102 return (ll == '') or ll.isspace()
119
103
120
104
121 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
105 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
122 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
106 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
123
107
124 def last_two_blanks(src):
108 def last_two_blanks(src):
125 """Determine if the input source ends in two blanks.
109 """Determine if the input source ends in two blanks.
126
110
127 A blank is either a newline or a line consisting of whitespace.
111 A blank is either a newline or a line consisting of whitespace.
128
112
129 Parameters
113 Parameters
130 ----------
114 ----------
131 src : string
115 src : string
132 A single or multiline string.
116 A single or multiline string.
133 """
117 """
134 if not src: return False
118 if not src: return False
135 # The logic here is tricky: I couldn't get a regexp to work and pass all
119 # The logic here is tricky: I couldn't get a regexp to work and pass all
136 # the tests, so I took a different approach: split the source by lines,
120 # the tests, so I took a different approach: split the source by lines,
137 # grab the last two and prepend '###\n' as a stand-in for whatever was in
121 # grab the last two and prepend '###\n' as a stand-in for whatever was in
138 # the body before the last two lines. Then, with that structure, it's
122 # the body before the last two lines. Then, with that structure, it's
139 # possible to analyze with two regexps. Not the most elegant solution, but
123 # possible to analyze with two regexps. Not the most elegant solution, but
140 # it works. If anyone tries to change this logic, make sure to validate
124 # it works. If anyone tries to change this logic, make sure to validate
141 # the whole test suite first!
125 # the whole test suite first!
142 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
126 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
143 return (bool(last_two_blanks_re.match(new_src)) or
127 return (bool(last_two_blanks_re.match(new_src)) or
144 bool(last_two_blanks_re2.match(new_src)) )
128 bool(last_two_blanks_re2.match(new_src)) )
145
129
146
130
147 def remove_comments(src):
131 def remove_comments(src):
148 """Remove all comments from input source.
132 """Remove all comments from input source.
149
133
150 Note: comments are NOT recognized inside of strings!
134 Note: comments are NOT recognized inside of strings!
151
135
152 Parameters
136 Parameters
153 ----------
137 ----------
154 src : string
138 src : string
155 A single or multiline input string.
139 A single or multiline input string.
156
140
157 Returns
141 Returns
158 -------
142 -------
159 String with all Python comments removed.
143 String with all Python comments removed.
160 """
144 """
161
145
162 return re.sub('#.*', '', src)
146 return re.sub('#.*', '', src)
163
147
164
148
165 def get_input_encoding():
149 def get_input_encoding():
166 """Return the default standard input encoding.
150 """Return the default standard input encoding.
167
151
168 If sys.stdin has no encoding, 'ascii' is returned."""
152 If sys.stdin has no encoding, 'ascii' is returned."""
169 # There are strange environments for which sys.stdin.encoding is None. We
153 # There are strange environments for which sys.stdin.encoding is None. We
170 # ensure that a valid encoding is returned.
154 # ensure that a valid encoding is returned.
171 encoding = getattr(sys.stdin, 'encoding', None)
155 encoding = getattr(sys.stdin, 'encoding', None)
172 if encoding is None:
156 if encoding is None:
173 encoding = 'ascii'
157 encoding = 'ascii'
174 return encoding
158 return encoding
175
159
176 #-----------------------------------------------------------------------------
160 #-----------------------------------------------------------------------------
177 # Classes and functions for normal Python syntax handling
161 # Classes and functions for normal Python syntax handling
178 #-----------------------------------------------------------------------------
162 #-----------------------------------------------------------------------------
179
163
180 class InputSplitter(object):
164 class InputSplitter(object):
181 r"""An object that can accumulate lines of Python source before execution.
165 r"""An object that can accumulate lines of Python source before execution.
182
166
183 This object is designed to be fed python source line-by-line, using
167 This object is designed to be fed python source line-by-line, using
184 :meth:`push`. It will return on each push whether the currently pushed
168 :meth:`push`. It will return on each push whether the currently pushed
185 code could be executed already. In addition, it provides a method called
169 code could be executed already. In addition, it provides a method called
186 :meth:`push_accepts_more` that can be used to query whether more input
170 :meth:`push_accepts_more` that can be used to query whether more input
187 can be pushed into a single interactive block.
171 can be pushed into a single interactive block.
188
172
189 This is a simple example of how an interactive terminal-based client can use
173 This is a simple example of how an interactive terminal-based client can use
190 this tool::
174 this tool::
191
175
192 isp = InputSplitter()
176 isp = InputSplitter()
193 while isp.push_accepts_more():
177 while isp.push_accepts_more():
194 indent = ' '*isp.indent_spaces
178 indent = ' '*isp.indent_spaces
195 prompt = '>>> ' + indent
179 prompt = '>>> ' + indent
196 line = indent + raw_input(prompt)
180 line = indent + raw_input(prompt)
197 isp.push(line)
181 isp.push(line)
198 print 'Input source was:\n', isp.source_reset(),
182 print 'Input source was:\n', isp.source_reset(),
199 """
183 """
200 # Number of spaces of indentation computed from input that has been pushed
184 # Number of spaces of indentation computed from input that has been pushed
201 # so far. This is the attributes callers should query to get the current
185 # so far. This is the attributes callers should query to get the current
202 # indentation level, in order to provide auto-indent facilities.
186 # indentation level, in order to provide auto-indent facilities.
203 indent_spaces = 0
187 indent_spaces = 0
204 # String, indicating the default input encoding. It is computed by default
188 # String, indicating the default input encoding. It is computed by default
205 # at initialization time via get_input_encoding(), but it can be reset by a
189 # at initialization time via get_input_encoding(), but it can be reset by a
206 # client with specific knowledge of the encoding.
190 # client with specific knowledge of the encoding.
207 encoding = ''
191 encoding = ''
208 # String where the current full source input is stored, properly encoded.
192 # String where the current full source input is stored, properly encoded.
209 # Reading this attribute is the normal way of querying the currently pushed
193 # Reading this attribute is the normal way of querying the currently pushed
210 # source code, that has been properly encoded.
194 # source code, that has been properly encoded.
211 source = ''
195 source = ''
212 # Code object corresponding to the current source. It is automatically
196 # Code object corresponding to the current source. It is automatically
213 # synced to the source, so it can be queried at any time to obtain the code
197 # synced to the source, so it can be queried at any time to obtain the code
214 # object; it will be None if the source doesn't compile to valid Python.
198 # object; it will be None if the source doesn't compile to valid Python.
215 code = None
199 code = None
216
200
217 # Private attributes
201 # Private attributes
218
202
219 # List with lines of input accumulated so far
203 # List with lines of input accumulated so far
220 _buffer = None
204 _buffer = None
221 # Command compiler
205 # Command compiler
222 _compile = None
206 _compile = None
223 # Mark when input has changed indentation all the way back to flush-left
207 # Mark when input has changed indentation all the way back to flush-left
224 _full_dedent = False
208 _full_dedent = False
225 # Boolean indicating whether the current block is complete
209 # Boolean indicating whether the current block is complete
226 _is_complete = None
210 _is_complete = None
227
211
228 def __init__(self):
212 def __init__(self):
229 """Create a new InputSplitter instance.
213 """Create a new InputSplitter instance.
230 """
214 """
231 self._buffer = []
215 self._buffer = []
232 self._compile = codeop.CommandCompiler()
216 self._compile = codeop.CommandCompiler()
233 self.encoding = get_input_encoding()
217 self.encoding = get_input_encoding()
234
218
235 def reset(self):
219 def reset(self):
236 """Reset the input buffer and associated state."""
220 """Reset the input buffer and associated state."""
237 self.indent_spaces = 0
221 self.indent_spaces = 0
238 self._buffer[:] = []
222 self._buffer[:] = []
239 self.source = ''
223 self.source = ''
240 self.code = None
224 self.code = None
241 self._is_complete = False
225 self._is_complete = False
242 self._full_dedent = False
226 self._full_dedent = False
243
227
244 def source_reset(self):
228 def source_reset(self):
245 """Return the input source and perform a full reset.
229 """Return the input source and perform a full reset.
246 """
230 """
247 out = self.source
231 out = self.source
248 self.reset()
232 self.reset()
249 return out
233 return out
250
234
251 def push(self, lines):
235 def push(self, lines):
252 """Push one or more lines of input.
236 """Push one or more lines of input.
253
237
254 This stores the given lines and returns a status code indicating
238 This stores the given lines and returns a status code indicating
255 whether the code forms a complete Python block or not.
239 whether the code forms a complete Python block or not.
256
240
257 Any exceptions generated in compilation are swallowed, but if an
241 Any exceptions generated in compilation are swallowed, but if an
258 exception was produced, the method returns True.
242 exception was produced, the method returns True.
259
243
260 Parameters
244 Parameters
261 ----------
245 ----------
262 lines : string
246 lines : string
263 One or more lines of Python input.
247 One or more lines of Python input.
264
248
265 Returns
249 Returns
266 -------
250 -------
267 is_complete : boolean
251 is_complete : boolean
268 True if the current input source (the result of the current input
252 True if the current input source (the result of the current input
269 plus prior inputs) forms a complete Python execution block. Note that
253 plus prior inputs) forms a complete Python execution block. Note that
270 this value is also stored as a private attribute (``_is_complete``), so it
254 this value is also stored as a private attribute (``_is_complete``), so it
271 can be queried at any time.
255 can be queried at any time.
272 """
256 """
273 self._store(lines)
257 self._store(lines)
274 source = self.source
258 source = self.source
275
259
276 # Before calling _compile(), reset the code object to None so that if an
260 # Before calling _compile(), reset the code object to None so that if an
277 # exception is raised in compilation, we don't mislead by having
261 # exception is raised in compilation, we don't mislead by having
278 # inconsistent code/source attributes.
262 # inconsistent code/source attributes.
279 self.code, self._is_complete = None, None
263 self.code, self._is_complete = None, None
280
264
281 # Honor termination lines properly
265 # Honor termination lines properly
282 if source.endswith('\\\n'):
266 if source.endswith('\\\n'):
283 return False
267 return False
284
268
285 self._update_indent(lines)
269 self._update_indent(lines)
286 try:
270 try:
287 self.code = self._compile(source, symbol="exec")
271 self.code = self._compile(source, symbol="exec")
288 # Invalid syntax can produce any of a number of different errors from
272 # Invalid syntax can produce any of a number of different errors from
289 # inside the compiler, so we have to catch them all. Syntax errors
273 # inside the compiler, so we have to catch them all. Syntax errors
290 # immediately produce a 'ready' block, so the invalid Python can be
274 # immediately produce a 'ready' block, so the invalid Python can be
291 # sent to the kernel for evaluation with possible ipython
275 # sent to the kernel for evaluation with possible ipython
292 # special-syntax conversion.
276 # special-syntax conversion.
293 except (SyntaxError, OverflowError, ValueError, TypeError,
277 except (SyntaxError, OverflowError, ValueError, TypeError,
294 MemoryError):
278 MemoryError):
295 self._is_complete = True
279 self._is_complete = True
296 else:
280 else:
297 # Compilation didn't produce any exceptions (though it may not have
281 # Compilation didn't produce any exceptions (though it may not have
298 # given a complete code object)
282 # given a complete code object)
299 self._is_complete = self.code is not None
283 self._is_complete = self.code is not None
300
284
301 return self._is_complete
285 return self._is_complete
302
286
303 def push_accepts_more(self):
287 def push_accepts_more(self):
304 """Return whether a block of interactive input can accept more input.
288 """Return whether a block of interactive input can accept more input.
305
289
306 This method is meant to be used by line-oriented frontends, who need to
290 This method is meant to be used by line-oriented frontends, who need to
307 guess whether a block is complete or not based solely on prior and
291 guess whether a block is complete or not based solely on prior and
308 current input lines. The InputSplitter considers it has a complete
292 current input lines. The InputSplitter considers it has a complete
309 interactive block and will not accept more input when either:
293 interactive block and will not accept more input when either:
310
294
311 * A SyntaxError is raised
295 * A SyntaxError is raised
312
296
313 * The code is complete and consists of a single line or a single
297 * The code is complete and consists of a single line or a single
314 non-compound statement
298 non-compound statement
315
299
316 * The code is complete and has a blank line at the end
300 * The code is complete and has a blank line at the end
317
301
318 If the current input produces a syntax error, this method immediately
302 If the current input produces a syntax error, this method immediately
319 returns False but does *not* raise the syntax error exception, as
303 returns False but does *not* raise the syntax error exception, as
320 typically clients will want to send invalid syntax to an execution
304 typically clients will want to send invalid syntax to an execution
321 backend which might convert the invalid syntax into valid Python via
305 backend which might convert the invalid syntax into valid Python via
322 one of the dynamic IPython mechanisms.
306 one of the dynamic IPython mechanisms.
323 """
307 """
324
308
325 # With incomplete input, unconditionally accept more
309 # With incomplete input, unconditionally accept more
326 # A syntax error also sets _is_complete to True - see push()
310 # A syntax error also sets _is_complete to True - see push()
327 if not self._is_complete:
311 if not self._is_complete:
328 #print("Not complete") # debug
312 #print("Not complete") # debug
329 return True
313 return True
330
314
331 # The user can make any (complete) input execute by leaving a blank line
315 # The user can make any (complete) input execute by leaving a blank line
332 last_line = self.source.splitlines()[-1]
316 last_line = self.source.splitlines()[-1]
333 if (not last_line) or last_line.isspace():
317 if (not last_line) or last_line.isspace():
334 #print("Blank line") # debug
318 #print("Blank line") # debug
335 return False
319 return False
336
320
337 # If there's just a single line or AST node, and we're flush left, as is
321 # If there's just a single line or AST node, and we're flush left, as is
338 # the case after a simple statement such as 'a=1', we want to execute it
322 # the case after a simple statement such as 'a=1', we want to execute it
339 # straight away.
323 # straight away.
340 if self.indent_spaces==0:
324 if self.indent_spaces==0:
341 if len(self.source.splitlines()) <= 1:
325 if len(self.source.splitlines()) <= 1:
342 return False
326 return False
343
327
344 try:
328 try:
345 code_ast = ast.parse(u''.join(self._buffer))
329 code_ast = ast.parse(u''.join(self._buffer))
346 except Exception:
330 except Exception:
347 #print("Can't parse AST") # debug
331 #print("Can't parse AST") # debug
348 return False
332 return False
349 else:
333 else:
350 if len(code_ast.body) == 1 and \
334 if len(code_ast.body) == 1 and \
351 not hasattr(code_ast.body[0], 'body'):
335 not hasattr(code_ast.body[0], 'body'):
352 #print("Simple statement") # debug
336 #print("Simple statement") # debug
353 return False
337 return False
354
338
355 # General fallback - accept more code
339 # General fallback - accept more code
356 return True
340 return True
357
341
358 #------------------------------------------------------------------------
342 #------------------------------------------------------------------------
359 # Private interface
343 # Private interface
360 #------------------------------------------------------------------------
344 #------------------------------------------------------------------------
361
345
362 def _find_indent(self, line):
346 def _find_indent(self, line):
363 """Compute the new indentation level for a single line.
347 """Compute the new indentation level for a single line.
364
348
365 Parameters
349 Parameters
366 ----------
350 ----------
367 line : str
351 line : str
368 A single new line of non-whitespace, non-comment Python input.
352 A single new line of non-whitespace, non-comment Python input.
369
353
370 Returns
354 Returns
371 -------
355 -------
372 indent_spaces : int
356 indent_spaces : int
373 New value for the indent level (it may be equal to self.indent_spaces
357 New value for the indent level (it may be equal to self.indent_spaces
374 if indentation doesn't change.
358 if indentation doesn't change.
375
359
376 full_dedent : boolean
360 full_dedent : boolean
377 Whether the new line causes a full flush-left dedent.
361 Whether the new line causes a full flush-left dedent.
378 """
362 """
379 indent_spaces = self.indent_spaces
363 indent_spaces = self.indent_spaces
380 full_dedent = self._full_dedent
364 full_dedent = self._full_dedent
381
365
382 inisp = num_ini_spaces(line)
366 inisp = num_ini_spaces(line)
383 if inisp < indent_spaces:
367 if inisp < indent_spaces:
384 indent_spaces = inisp
368 indent_spaces = inisp
385 if indent_spaces <= 0:
369 if indent_spaces <= 0:
386 #print 'Full dedent in text',self.source # dbg
370 #print 'Full dedent in text',self.source # dbg
387 full_dedent = True
371 full_dedent = True
388
372
389 if line.rstrip()[-1] == ':':
373 if line.rstrip()[-1] == ':':
390 indent_spaces += 4
374 indent_spaces += 4
391 elif dedent_re.match(line):
375 elif dedent_re.match(line):
392 indent_spaces -= 4
376 indent_spaces -= 4
393 if indent_spaces <= 0:
377 if indent_spaces <= 0:
394 full_dedent = True
378 full_dedent = True
395
379
396 # Safety
380 # Safety
397 if indent_spaces < 0:
381 if indent_spaces < 0:
398 indent_spaces = 0
382 indent_spaces = 0
399 #print 'safety' # dbg
383 #print 'safety' # dbg
400
384
401 return indent_spaces, full_dedent
385 return indent_spaces, full_dedent
402
386
403 def _update_indent(self, lines):
387 def _update_indent(self, lines):
404 for line in remove_comments(lines).splitlines():
388 for line in remove_comments(lines).splitlines():
405 if line and not line.isspace():
389 if line and not line.isspace():
406 self.indent_spaces, self._full_dedent = self._find_indent(line)
390 self.indent_spaces, self._full_dedent = self._find_indent(line)
407
391
408 def _store(self, lines, buffer=None, store='source'):
392 def _store(self, lines, buffer=None, store='source'):
409 """Store one or more lines of input.
393 """Store one or more lines of input.
410
394
411 If input lines are not newline-terminated, a newline is automatically
395 If input lines are not newline-terminated, a newline is automatically
412 appended."""
396 appended."""
413
397
414 if buffer is None:
398 if buffer is None:
415 buffer = self._buffer
399 buffer = self._buffer
416
400
417 if lines.endswith('\n'):
401 if lines.endswith('\n'):
418 buffer.append(lines)
402 buffer.append(lines)
419 else:
403 else:
420 buffer.append(lines+'\n')
404 buffer.append(lines+'\n')
421 setattr(self, store, self._set_source(buffer))
405 setattr(self, store, self._set_source(buffer))
422
406
423 def _set_source(self, buffer):
407 def _set_source(self, buffer):
424 return u''.join(buffer)
408 return u''.join(buffer)
425
409
426
410
427 class IPythonInputSplitter(InputSplitter):
411 class IPythonInputSplitter(InputSplitter):
428 """An input splitter that recognizes all of IPython's special syntax."""
412 """An input splitter that recognizes all of IPython's special syntax."""
429
413
430 # String with raw, untransformed input.
414 # String with raw, untransformed input.
431 source_raw = ''
415 source_raw = ''
432
416
433 # Flag to track when a transformer has stored input that it hasn't given
417 # Flag to track when a transformer has stored input that it hasn't given
434 # back yet.
418 # back yet.
435 transformer_accumulating = False
419 transformer_accumulating = False
436
420
437 # Flag to track when assemble_python_lines has stored input that it hasn't
421 # Flag to track when assemble_python_lines has stored input that it hasn't
438 # given back yet.
422 # given back yet.
439 within_python_line = False
423 within_python_line = False
440
424
441 # Private attributes
425 # Private attributes
442
426
443 # List with lines of raw input accumulated so far.
427 # List with lines of raw input accumulated so far.
444 _buffer_raw = None
428 _buffer_raw = None
445
429
446 def __init__(self, line_input_checker=True, physical_line_transforms=None,
430 def __init__(self, line_input_checker=True, physical_line_transforms=None,
447 logical_line_transforms=None, python_line_transforms=None):
431 logical_line_transforms=None, python_line_transforms=None):
448 super(IPythonInputSplitter, self).__init__()
432 super(IPythonInputSplitter, self).__init__()
449 self._buffer_raw = []
433 self._buffer_raw = []
450 self._validate = True
434 self._validate = True
451
435
452 if physical_line_transforms is not None:
436 if physical_line_transforms is not None:
453 self.physical_line_transforms = physical_line_transforms
437 self.physical_line_transforms = physical_line_transforms
454 else:
438 else:
455 self.physical_line_transforms = [
439 self.physical_line_transforms = [
456 leading_indent(),
440 leading_indent(),
457 classic_prompt(),
441 classic_prompt(),
458 ipy_prompt(),
442 ipy_prompt(),
459 strip_encoding_cookie(),
443 strip_encoding_cookie(),
460 cellmagic(end_on_blank_line=line_input_checker),
444 cellmagic(end_on_blank_line=line_input_checker),
461 ]
445 ]
462
446
463 self.assemble_logical_lines = assemble_logical_lines()
447 self.assemble_logical_lines = assemble_logical_lines()
464 if logical_line_transforms is not None:
448 if logical_line_transforms is not None:
465 self.logical_line_transforms = logical_line_transforms
449 self.logical_line_transforms = logical_line_transforms
466 else:
450 else:
467 self.logical_line_transforms = [
451 self.logical_line_transforms = [
468 help_end(),
452 help_end(),
469 escaped_commands(),
453 escaped_commands(),
470 assign_from_magic(),
454 assign_from_magic(),
471 assign_from_system(),
455 assign_from_system(),
472 ]
456 ]
473
457
474 self.assemble_python_lines = assemble_python_lines()
458 self.assemble_python_lines = assemble_python_lines()
475 if python_line_transforms is not None:
459 if python_line_transforms is not None:
476 self.python_line_transforms = python_line_transforms
460 self.python_line_transforms = python_line_transforms
477 else:
461 else:
478 # We don't use any of these at present
462 # We don't use any of these at present
479 self.python_line_transforms = []
463 self.python_line_transforms = []
480
464
481 @property
465 @property
482 def transforms(self):
466 def transforms(self):
483 "Quick access to all transformers."
467 "Quick access to all transformers."
484 return self.physical_line_transforms + \
468 return self.physical_line_transforms + \
485 [self.assemble_logical_lines] + self.logical_line_transforms + \
469 [self.assemble_logical_lines] + self.logical_line_transforms + \
486 [self.assemble_python_lines] + self.python_line_transforms
470 [self.assemble_python_lines] + self.python_line_transforms
487
471
488 @property
472 @property
489 def transforms_in_use(self):
473 def transforms_in_use(self):
490 """Transformers, excluding logical line transformers if we're in a
474 """Transformers, excluding logical line transformers if we're in a
491 Python line."""
475 Python line."""
492 t = self.physical_line_transforms[:]
476 t = self.physical_line_transforms[:]
493 if not self.within_python_line:
477 if not self.within_python_line:
494 t += [self.assemble_logical_lines] + self.logical_line_transforms
478 t += [self.assemble_logical_lines] + self.logical_line_transforms
495 return t + [self.assemble_python_lines] + self.python_line_transforms
479 return t + [self.assemble_python_lines] + self.python_line_transforms
496
480
497 def reset(self):
481 def reset(self):
498 """Reset the input buffer and associated state."""
482 """Reset the input buffer and associated state."""
499 super(IPythonInputSplitter, self).reset()
483 super(IPythonInputSplitter, self).reset()
500 self._buffer_raw[:] = []
484 self._buffer_raw[:] = []
501 self.source_raw = ''
485 self.source_raw = ''
502 self.transformer_accumulating = False
486 self.transformer_accumulating = False
503 self.within_python_line = False
487 self.within_python_line = False
504
488
505 for t in self.transforms:
489 for t in self.transforms:
506 try:
490 try:
507 t.reset()
491 t.reset()
508 except SyntaxError:
492 except SyntaxError:
509 # Nothing that calls reset() expects to handle transformer
493 # Nothing that calls reset() expects to handle transformer
510 # errors
494 # errors
511 pass
495 pass
512
496
513 def flush_transformers(self):
497 def flush_transformers(self):
514 def _flush(transform, out):
498 def _flush(transform, out):
515 if out is not None:
499 """yield transformed lines
516 tmp = transform.push(out)
517 return tmp or transform.reset() or None
518 else:
519 return transform.reset() or None
520
500
521 out = None
501 always strings, never None
502
503 transform: the current transform
504 out: an iterable of previously transformed inputs.
505 Each may be multiline, which will be passed
506 one line at a time to transform.
507 """
508 anything = False
509 for out in out:
510 anything = True
511 tmp = None
512 for line in out.splitlines():
513 # push one line at a time
514 tmp = transform.push(line)
515 if tmp is not None:
516 yield tmp
517 if tmp is None:
518 # transformer is still consuming, reset
519 tmp = transform.reset()
520 if tmp is not None:
521 yield tmp
522 if not anything:
523 # nothing was pushed, reset
524 tmp = transform.reset()
525 if tmp is not None:
526 yield tmp
527
528 out = []
522 for t in self.transforms_in_use:
529 for t in self.transforms_in_use:
523 out = _flush(t, out)
530 out = _flush(t, out)
524
531
525 if out is not None:
532 out = list(out)
526 self._store(out)
533 if out:
534 self._store('\n'.join(out))
527
535
528 def raw_reset(self):
536 def raw_reset(self):
529 """Return raw input only and perform a full reset.
537 """Return raw input only and perform a full reset.
530 """
538 """
531 out = self.source_raw
539 out = self.source_raw
532 self.reset()
540 self.reset()
533 return out
541 return out
534
542
535 def source_reset(self):
543 def source_reset(self):
536 try:
544 try:
537 self.flush_transformers()
545 self.flush_transformers()
538 return self.source
546 return self.source
539 finally:
547 finally:
540 self.reset()
548 self.reset()
541
549
542 def push_accepts_more(self):
550 def push_accepts_more(self):
543 if self.transformer_accumulating:
551 if self.transformer_accumulating:
544 return True
552 return True
545 else:
553 else:
546 return super(IPythonInputSplitter, self).push_accepts_more()
554 return super(IPythonInputSplitter, self).push_accepts_more()
547
555
548 def transform_cell(self, cell):
556 def transform_cell(self, cell):
549 """Process and translate a cell of input.
557 """Process and translate a cell of input.
550 """
558 """
551 self.reset()
559 self.reset()
552 try:
560 try:
553 self.push(cell)
561 self.push(cell)
554 self.flush_transformers()
562 self.flush_transformers()
555 return self.source
563 return self.source
556 finally:
564 finally:
557 self.reset()
565 self.reset()
558
566
559 def push(self, lines):
567 def push(self, lines):
560 """Push one or more lines of IPython input.
568 """Push one or more lines of IPython input.
561
569
562 This stores the given lines and returns a status code indicating
570 This stores the given lines and returns a status code indicating
563 whether the code forms a complete Python block or not, after processing
571 whether the code forms a complete Python block or not, after processing
564 all input lines for special IPython syntax.
572 all input lines for special IPython syntax.
565
573
566 Any exceptions generated in compilation are swallowed, but if an
574 Any exceptions generated in compilation are swallowed, but if an
567 exception was produced, the method returns True.
575 exception was produced, the method returns True.
568
576
569 Parameters
577 Parameters
570 ----------
578 ----------
571 lines : string
579 lines : string
572 One or more lines of Python input.
580 One or more lines of Python input.
573
581
574 Returns
582 Returns
575 -------
583 -------
576 is_complete : boolean
584 is_complete : boolean
577 True if the current input source (the result of the current input
585 True if the current input source (the result of the current input
578 plus prior inputs) forms a complete Python execution block. Note that
586 plus prior inputs) forms a complete Python execution block. Note that
579 this value is also stored as a private attribute (_is_complete), so it
587 this value is also stored as a private attribute (_is_complete), so it
580 can be queried at any time.
588 can be queried at any time.
581 """
589 """
582
590
583 # We must ensure all input is pure unicode
591 # We must ensure all input is pure unicode
584 lines = cast_unicode(lines, self.encoding)
592 lines = cast_unicode(lines, self.encoding)
585
593
586 # ''.splitlines() --> [], but we need to push the empty line to transformers
594 # ''.splitlines() --> [], but we need to push the empty line to transformers
587 lines_list = lines.splitlines()
595 lines_list = lines.splitlines()
588 if not lines_list:
596 if not lines_list:
589 lines_list = ['']
597 lines_list = ['']
590
598
591 # Store raw source before applying any transformations to it. Note
599 # Store raw source before applying any transformations to it. Note
592 # that this must be done *after* the reset() call that would otherwise
600 # that this must be done *after* the reset() call that would otherwise
593 # flush the buffer.
601 # flush the buffer.
594 self._store(lines, self._buffer_raw, 'source_raw')
602 self._store(lines, self._buffer_raw, 'source_raw')
595
603
596 for line in lines_list:
604 for line in lines_list:
597 out = self.push_line(line)
605 out = self.push_line(line)
598
606
599 return out
607 return out
600
608
601 def push_line(self, line):
609 def push_line(self, line):
602 buf = self._buffer
610 buf = self._buffer
603
611
604 def _accumulating(dbg):
612 def _accumulating(dbg):
605 #print(dbg)
613 #print(dbg)
606 self.transformer_accumulating = True
614 self.transformer_accumulating = True
607 return False
615 return False
608
616
609 for transformer in self.physical_line_transforms:
617 for transformer in self.physical_line_transforms:
610 line = transformer.push(line)
618 line = transformer.push(line)
611 if line is None:
619 if line is None:
612 return _accumulating(transformer)
620 return _accumulating(transformer)
613
621
614 if not self.within_python_line:
622 if not self.within_python_line:
615 line = self.assemble_logical_lines.push(line)
623 line = self.assemble_logical_lines.push(line)
616 if line is None:
624 if line is None:
617 return _accumulating('acc logical line')
625 return _accumulating('acc logical line')
618
626
619 for transformer in self.logical_line_transforms:
627 for transformer in self.logical_line_transforms:
620 line = transformer.push(line)
628 line = transformer.push(line)
621 if line is None:
629 if line is None:
622 return _accumulating(transformer)
630 return _accumulating(transformer)
623
631
624 line = self.assemble_python_lines.push(line)
632 line = self.assemble_python_lines.push(line)
625 if line is None:
633 if line is None:
626 self.within_python_line = True
634 self.within_python_line = True
627 return _accumulating('acc python line')
635 return _accumulating('acc python line')
628 else:
636 else:
629 self.within_python_line = False
637 self.within_python_line = False
630
638
631 for transformer in self.python_line_transforms:
639 for transformer in self.python_line_transforms:
632 line = transformer.push(line)
640 line = transformer.push(line)
633 if line is None:
641 if line is None:
634 return _accumulating(transformer)
642 return _accumulating(transformer)
635
643
636 #print("transformers clear") #debug
644 #print("transformers clear") #debug
637 self.transformer_accumulating = False
645 self.transformer_accumulating = False
638 return super(IPythonInputSplitter, self).push(line)
646 return super(IPythonInputSplitter, self).push(line)
@@ -1,585 +1,597 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
5 -------
6 * Fernando Perez
7 * Robert Kern
8 """
9 from __future__ import print_function
4 from __future__ import print_function
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2010-2011 The IPython Development Team
12 #
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
16
5
17 #-----------------------------------------------------------------------------
6 # Copyright (c) IPython Development Team.
18 # Imports
7 # Distributed under the terms of the Modified BSD License.
19 #-----------------------------------------------------------------------------
8
20 # stdlib
21 import unittest
9 import unittest
22 import sys
10 import sys
23
11
24 # Third party
25 import nose.tools as nt
12 import nose.tools as nt
26
13
27 # Our own
28 from IPython.core import inputsplitter as isp
14 from IPython.core import inputsplitter as isp
15 from IPython.core.inputtransformer import InputTransformer
29 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
16 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
30 from IPython.testing import tools as tt
17 from IPython.testing import tools as tt
31 from IPython.utils import py3compat
18 from IPython.utils import py3compat
32 from IPython.utils.py3compat import string_types, input
19 from IPython.utils.py3compat import string_types, input
33
20
34 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
35 # Semi-complete examples (also used as tests)
22 # Semi-complete examples (also used as tests)
36 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
37
24
38 # Note: at the bottom, there's a slightly more complete version of this that
25 # Note: at the bottom, there's a slightly more complete version of this that
39 # can be useful during development of code here.
26 # can be useful during development of code here.
40
27
41 def mini_interactive_loop(input_func):
28 def mini_interactive_loop(input_func):
42 """Minimal example of the logic of an interactive interpreter loop.
29 """Minimal example of the logic of an interactive interpreter loop.
43
30
44 This serves as an example, and it is used by the test system with a fake
31 This serves as an example, and it is used by the test system with a fake
45 raw_input that simulates interactive input."""
32 raw_input that simulates interactive input."""
46
33
47 from IPython.core.inputsplitter import InputSplitter
34 from IPython.core.inputsplitter import InputSplitter
48
35
49 isp = InputSplitter()
36 isp = InputSplitter()
50 # In practice, this input loop would be wrapped in an outside loop to read
37 # In practice, this input loop would be wrapped in an outside loop to read
51 # input indefinitely, until some exit/quit command was issued. Here we
38 # input indefinitely, until some exit/quit command was issued. Here we
52 # only illustrate the basic inner loop.
39 # only illustrate the basic inner loop.
53 while isp.push_accepts_more():
40 while isp.push_accepts_more():
54 indent = ' '*isp.indent_spaces
41 indent = ' '*isp.indent_spaces
55 prompt = '>>> ' + indent
42 prompt = '>>> ' + indent
56 line = indent + input_func(prompt)
43 line = indent + input_func(prompt)
57 isp.push(line)
44 isp.push(line)
58
45
59 # Here we just return input so we can use it in a test suite, but a real
46 # Here we just return input so we can use it in a test suite, but a real
60 # interpreter would instead send it for execution somewhere.
47 # interpreter would instead send it for execution somewhere.
61 src = isp.source_reset()
48 src = isp.source_reset()
62 #print 'Input source was:\n', src # dbg
49 #print 'Input source was:\n', src # dbg
63 return src
50 return src
64
51
65 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
66 # Test utilities, just for local use
53 # Test utilities, just for local use
67 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
68
55
69 def assemble(block):
56 def assemble(block):
70 """Assemble a block into multi-line sub-blocks."""
57 """Assemble a block into multi-line sub-blocks."""
71 return ['\n'.join(sub_block)+'\n' for sub_block in block]
58 return ['\n'.join(sub_block)+'\n' for sub_block in block]
72
59
73
60
74 def pseudo_input(lines):
61 def pseudo_input(lines):
75 """Return a function that acts like raw_input but feeds the input list."""
62 """Return a function that acts like raw_input but feeds the input list."""
76 ilines = iter(lines)
63 ilines = iter(lines)
77 def raw_in(prompt):
64 def raw_in(prompt):
78 try:
65 try:
79 return next(ilines)
66 return next(ilines)
80 except StopIteration:
67 except StopIteration:
81 return ''
68 return ''
82 return raw_in
69 return raw_in
83
70
84 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
85 # Tests
72 # Tests
86 #-----------------------------------------------------------------------------
73 #-----------------------------------------------------------------------------
87 def test_spaces():
74 def test_spaces():
88 tests = [('', 0),
75 tests = [('', 0),
89 (' ', 1),
76 (' ', 1),
90 ('\n', 0),
77 ('\n', 0),
91 (' \n', 1),
78 (' \n', 1),
92 ('x', 0),
79 ('x', 0),
93 (' x', 1),
80 (' x', 1),
94 (' x',2),
81 (' x',2),
95 (' x',4),
82 (' x',4),
96 # Note: tabs are counted as a single whitespace!
83 # Note: tabs are counted as a single whitespace!
97 ('\tx', 1),
84 ('\tx', 1),
98 ('\t x', 2),
85 ('\t x', 2),
99 ]
86 ]
100 tt.check_pairs(isp.num_ini_spaces, tests)
87 tt.check_pairs(isp.num_ini_spaces, tests)
101
88
102
89
103 def test_remove_comments():
90 def test_remove_comments():
104 tests = [('text', 'text'),
91 tests = [('text', 'text'),
105 ('text # comment', 'text '),
92 ('text # comment', 'text '),
106 ('text # comment\n', 'text \n'),
93 ('text # comment\n', 'text \n'),
107 ('text # comment \n', 'text \n'),
94 ('text # comment \n', 'text \n'),
108 ('line # c \nline\n','line \nline\n'),
95 ('line # c \nline\n','line \nline\n'),
109 ('line # c \nline#c2 \nline\nline #c\n\n',
96 ('line # c \nline#c2 \nline\nline #c\n\n',
110 'line \nline\nline\nline \n\n'),
97 'line \nline\nline\nline \n\n'),
111 ]
98 ]
112 tt.check_pairs(isp.remove_comments, tests)
99 tt.check_pairs(isp.remove_comments, tests)
113
100
114
101
115 def test_get_input_encoding():
102 def test_get_input_encoding():
116 encoding = isp.get_input_encoding()
103 encoding = isp.get_input_encoding()
117 nt.assert_true(isinstance(encoding, string_types))
104 nt.assert_true(isinstance(encoding, string_types))
118 # simple-minded check that at least encoding a simple string works with the
105 # simple-minded check that at least encoding a simple string works with the
119 # encoding we got.
106 # encoding we got.
120 nt.assert_equal(u'test'.encode(encoding), b'test')
107 nt.assert_equal(u'test'.encode(encoding), b'test')
121
108
122
109
123 class NoInputEncodingTestCase(unittest.TestCase):
110 class NoInputEncodingTestCase(unittest.TestCase):
124 def setUp(self):
111 def setUp(self):
125 self.old_stdin = sys.stdin
112 self.old_stdin = sys.stdin
126 class X: pass
113 class X: pass
127 fake_stdin = X()
114 fake_stdin = X()
128 sys.stdin = fake_stdin
115 sys.stdin = fake_stdin
129
116
130 def test(self):
117 def test(self):
131 # Verify that if sys.stdin has no 'encoding' attribute we do the right
118 # Verify that if sys.stdin has no 'encoding' attribute we do the right
132 # thing
119 # thing
133 enc = isp.get_input_encoding()
120 enc = isp.get_input_encoding()
134 self.assertEqual(enc, 'ascii')
121 self.assertEqual(enc, 'ascii')
135
122
136 def tearDown(self):
123 def tearDown(self):
137 sys.stdin = self.old_stdin
124 sys.stdin = self.old_stdin
138
125
139
126
140 class InputSplitterTestCase(unittest.TestCase):
127 class InputSplitterTestCase(unittest.TestCase):
141 def setUp(self):
128 def setUp(self):
142 self.isp = isp.InputSplitter()
129 self.isp = isp.InputSplitter()
143
130
144 def test_reset(self):
131 def test_reset(self):
145 isp = self.isp
132 isp = self.isp
146 isp.push('x=1')
133 isp.push('x=1')
147 isp.reset()
134 isp.reset()
148 self.assertEqual(isp._buffer, [])
135 self.assertEqual(isp._buffer, [])
149 self.assertEqual(isp.indent_spaces, 0)
136 self.assertEqual(isp.indent_spaces, 0)
150 self.assertEqual(isp.source, '')
137 self.assertEqual(isp.source, '')
151 self.assertEqual(isp.code, None)
138 self.assertEqual(isp.code, None)
152 self.assertEqual(isp._is_complete, False)
139 self.assertEqual(isp._is_complete, False)
153
140
154 def test_source(self):
141 def test_source(self):
155 self.isp._store('1')
142 self.isp._store('1')
156 self.isp._store('2')
143 self.isp._store('2')
157 self.assertEqual(self.isp.source, '1\n2\n')
144 self.assertEqual(self.isp.source, '1\n2\n')
158 self.assertTrue(len(self.isp._buffer)>0)
145 self.assertTrue(len(self.isp._buffer)>0)
159 self.assertEqual(self.isp.source_reset(), '1\n2\n')
146 self.assertEqual(self.isp.source_reset(), '1\n2\n')
160 self.assertEqual(self.isp._buffer, [])
147 self.assertEqual(self.isp._buffer, [])
161 self.assertEqual(self.isp.source, '')
148 self.assertEqual(self.isp.source, '')
162
149
163 def test_indent(self):
150 def test_indent(self):
164 isp = self.isp # shorthand
151 isp = self.isp # shorthand
165 isp.push('x=1')
152 isp.push('x=1')
166 self.assertEqual(isp.indent_spaces, 0)
153 self.assertEqual(isp.indent_spaces, 0)
167 isp.push('if 1:\n x=1')
154 isp.push('if 1:\n x=1')
168 self.assertEqual(isp.indent_spaces, 4)
155 self.assertEqual(isp.indent_spaces, 4)
169 isp.push('y=2\n')
156 isp.push('y=2\n')
170 self.assertEqual(isp.indent_spaces, 0)
157 self.assertEqual(isp.indent_spaces, 0)
171
158
172 def test_indent2(self):
159 def test_indent2(self):
173 isp = self.isp
160 isp = self.isp
174 isp.push('if 1:')
161 isp.push('if 1:')
175 self.assertEqual(isp.indent_spaces, 4)
162 self.assertEqual(isp.indent_spaces, 4)
176 isp.push(' x=1')
163 isp.push(' x=1')
177 self.assertEqual(isp.indent_spaces, 4)
164 self.assertEqual(isp.indent_spaces, 4)
178 # Blank lines shouldn't change the indent level
165 # Blank lines shouldn't change the indent level
179 isp.push(' '*2)
166 isp.push(' '*2)
180 self.assertEqual(isp.indent_spaces, 4)
167 self.assertEqual(isp.indent_spaces, 4)
181
168
182 def test_indent3(self):
169 def test_indent3(self):
183 isp = self.isp
170 isp = self.isp
184 # When a multiline statement contains parens or multiline strings, we
171 # When a multiline statement contains parens or multiline strings, we
185 # shouldn't get confused.
172 # shouldn't get confused.
186 isp.push("if 1:")
173 isp.push("if 1:")
187 isp.push(" x = (1+\n 2)")
174 isp.push(" x = (1+\n 2)")
188 self.assertEqual(isp.indent_spaces, 4)
175 self.assertEqual(isp.indent_spaces, 4)
189
176
190 def test_indent4(self):
177 def test_indent4(self):
191 isp = self.isp
178 isp = self.isp
192 # whitespace after ':' should not screw up indent level
179 # whitespace after ':' should not screw up indent level
193 isp.push('if 1: \n x=1')
180 isp.push('if 1: \n x=1')
194 self.assertEqual(isp.indent_spaces, 4)
181 self.assertEqual(isp.indent_spaces, 4)
195 isp.push('y=2\n')
182 isp.push('y=2\n')
196 self.assertEqual(isp.indent_spaces, 0)
183 self.assertEqual(isp.indent_spaces, 0)
197 isp.push('if 1:\t\n x=1')
184 isp.push('if 1:\t\n x=1')
198 self.assertEqual(isp.indent_spaces, 4)
185 self.assertEqual(isp.indent_spaces, 4)
199 isp.push('y=2\n')
186 isp.push('y=2\n')
200 self.assertEqual(isp.indent_spaces, 0)
187 self.assertEqual(isp.indent_spaces, 0)
201
188
202 def test_dedent_pass(self):
189 def test_dedent_pass(self):
203 isp = self.isp # shorthand
190 isp = self.isp # shorthand
204 # should NOT cause dedent
191 # should NOT cause dedent
205 isp.push('if 1:\n passes = 5')
192 isp.push('if 1:\n passes = 5')
206 self.assertEqual(isp.indent_spaces, 4)
193 self.assertEqual(isp.indent_spaces, 4)
207 isp.push('if 1:\n pass')
194 isp.push('if 1:\n pass')
208 self.assertEqual(isp.indent_spaces, 0)
195 self.assertEqual(isp.indent_spaces, 0)
209 isp.push('if 1:\n pass ')
196 isp.push('if 1:\n pass ')
210 self.assertEqual(isp.indent_spaces, 0)
197 self.assertEqual(isp.indent_spaces, 0)
211
198
212 def test_dedent_break(self):
199 def test_dedent_break(self):
213 isp = self.isp # shorthand
200 isp = self.isp # shorthand
214 # should NOT cause dedent
201 # should NOT cause dedent
215 isp.push('while 1:\n breaks = 5')
202 isp.push('while 1:\n breaks = 5')
216 self.assertEqual(isp.indent_spaces, 4)
203 self.assertEqual(isp.indent_spaces, 4)
217 isp.push('while 1:\n break')
204 isp.push('while 1:\n break')
218 self.assertEqual(isp.indent_spaces, 0)
205 self.assertEqual(isp.indent_spaces, 0)
219 isp.push('while 1:\n break ')
206 isp.push('while 1:\n break ')
220 self.assertEqual(isp.indent_spaces, 0)
207 self.assertEqual(isp.indent_spaces, 0)
221
208
222 def test_dedent_continue(self):
209 def test_dedent_continue(self):
223 isp = self.isp # shorthand
210 isp = self.isp # shorthand
224 # should NOT cause dedent
211 # should NOT cause dedent
225 isp.push('while 1:\n continues = 5')
212 isp.push('while 1:\n continues = 5')
226 self.assertEqual(isp.indent_spaces, 4)
213 self.assertEqual(isp.indent_spaces, 4)
227 isp.push('while 1:\n continue')
214 isp.push('while 1:\n continue')
228 self.assertEqual(isp.indent_spaces, 0)
215 self.assertEqual(isp.indent_spaces, 0)
229 isp.push('while 1:\n continue ')
216 isp.push('while 1:\n continue ')
230 self.assertEqual(isp.indent_spaces, 0)
217 self.assertEqual(isp.indent_spaces, 0)
231
218
232 def test_dedent_raise(self):
219 def test_dedent_raise(self):
233 isp = self.isp # shorthand
220 isp = self.isp # shorthand
234 # should NOT cause dedent
221 # should NOT cause dedent
235 isp.push('if 1:\n raised = 4')
222 isp.push('if 1:\n raised = 4')
236 self.assertEqual(isp.indent_spaces, 4)
223 self.assertEqual(isp.indent_spaces, 4)
237 isp.push('if 1:\n raise TypeError()')
224 isp.push('if 1:\n raise TypeError()')
238 self.assertEqual(isp.indent_spaces, 0)
225 self.assertEqual(isp.indent_spaces, 0)
239 isp.push('if 1:\n raise')
226 isp.push('if 1:\n raise')
240 self.assertEqual(isp.indent_spaces, 0)
227 self.assertEqual(isp.indent_spaces, 0)
241 isp.push('if 1:\n raise ')
228 isp.push('if 1:\n raise ')
242 self.assertEqual(isp.indent_spaces, 0)
229 self.assertEqual(isp.indent_spaces, 0)
243
230
244 def test_dedent_return(self):
231 def test_dedent_return(self):
245 isp = self.isp # shorthand
232 isp = self.isp # shorthand
246 # should NOT cause dedent
233 # should NOT cause dedent
247 isp.push('if 1:\n returning = 4')
234 isp.push('if 1:\n returning = 4')
248 self.assertEqual(isp.indent_spaces, 4)
235 self.assertEqual(isp.indent_spaces, 4)
249 isp.push('if 1:\n return 5 + 493')
236 isp.push('if 1:\n return 5 + 493')
250 self.assertEqual(isp.indent_spaces, 0)
237 self.assertEqual(isp.indent_spaces, 0)
251 isp.push('if 1:\n return')
238 isp.push('if 1:\n return')
252 self.assertEqual(isp.indent_spaces, 0)
239 self.assertEqual(isp.indent_spaces, 0)
253 isp.push('if 1:\n return ')
240 isp.push('if 1:\n return ')
254 self.assertEqual(isp.indent_spaces, 0)
241 self.assertEqual(isp.indent_spaces, 0)
255 isp.push('if 1:\n return(0)')
242 isp.push('if 1:\n return(0)')
256 self.assertEqual(isp.indent_spaces, 0)
243 self.assertEqual(isp.indent_spaces, 0)
257
244
258 def test_push(self):
245 def test_push(self):
259 isp = self.isp
246 isp = self.isp
260 self.assertTrue(isp.push('x=1'))
247 self.assertTrue(isp.push('x=1'))
261
248
262 def test_push2(self):
249 def test_push2(self):
263 isp = self.isp
250 isp = self.isp
264 self.assertFalse(isp.push('if 1:'))
251 self.assertFalse(isp.push('if 1:'))
265 for line in [' x=1', '# a comment', ' y=2']:
252 for line in [' x=1', '# a comment', ' y=2']:
266 print(line)
253 print(line)
267 self.assertTrue(isp.push(line))
254 self.assertTrue(isp.push(line))
268
255
269 def test_push3(self):
256 def test_push3(self):
270 isp = self.isp
257 isp = self.isp
271 isp.push('if True:')
258 isp.push('if True:')
272 isp.push(' a = 1')
259 isp.push(' a = 1')
273 self.assertFalse(isp.push('b = [1,'))
260 self.assertFalse(isp.push('b = [1,'))
274
261
275 def test_push_accepts_more(self):
262 def test_push_accepts_more(self):
276 isp = self.isp
263 isp = self.isp
277 isp.push('x=1')
264 isp.push('x=1')
278 self.assertFalse(isp.push_accepts_more())
265 self.assertFalse(isp.push_accepts_more())
279
266
280 def test_push_accepts_more2(self):
267 def test_push_accepts_more2(self):
281 isp = self.isp
268 isp = self.isp
282 isp.push('if 1:')
269 isp.push('if 1:')
283 self.assertTrue(isp.push_accepts_more())
270 self.assertTrue(isp.push_accepts_more())
284 isp.push(' x=1')
271 isp.push(' x=1')
285 self.assertTrue(isp.push_accepts_more())
272 self.assertTrue(isp.push_accepts_more())
286 isp.push('')
273 isp.push('')
287 self.assertFalse(isp.push_accepts_more())
274 self.assertFalse(isp.push_accepts_more())
288
275
289 def test_push_accepts_more3(self):
276 def test_push_accepts_more3(self):
290 isp = self.isp
277 isp = self.isp
291 isp.push("x = (2+\n3)")
278 isp.push("x = (2+\n3)")
292 self.assertFalse(isp.push_accepts_more())
279 self.assertFalse(isp.push_accepts_more())
293
280
294 def test_push_accepts_more4(self):
281 def test_push_accepts_more4(self):
295 isp = self.isp
282 isp = self.isp
296 # When a multiline statement contains parens or multiline strings, we
283 # When a multiline statement contains parens or multiline strings, we
297 # shouldn't get confused.
284 # shouldn't get confused.
298 # FIXME: we should be able to better handle de-dents in statements like
285 # FIXME: we should be able to better handle de-dents in statements like
299 # multiline strings and multiline expressions (continued with \ or
286 # multiline strings and multiline expressions (continued with \ or
300 # parens). Right now we aren't handling the indentation tracking quite
287 # parens). Right now we aren't handling the indentation tracking quite
301 # correctly with this, though in practice it may not be too much of a
288 # correctly with this, though in practice it may not be too much of a
302 # problem. We'll need to see.
289 # problem. We'll need to see.
303 isp.push("if 1:")
290 isp.push("if 1:")
304 isp.push(" x = (2+")
291 isp.push(" x = (2+")
305 isp.push(" 3)")
292 isp.push(" 3)")
306 self.assertTrue(isp.push_accepts_more())
293 self.assertTrue(isp.push_accepts_more())
307 isp.push(" y = 3")
294 isp.push(" y = 3")
308 self.assertTrue(isp.push_accepts_more())
295 self.assertTrue(isp.push_accepts_more())
309 isp.push('')
296 isp.push('')
310 self.assertFalse(isp.push_accepts_more())
297 self.assertFalse(isp.push_accepts_more())
311
298
312 def test_push_accepts_more5(self):
299 def test_push_accepts_more5(self):
313 isp = self.isp
300 isp = self.isp
314 isp.push('try:')
301 isp.push('try:')
315 isp.push(' a = 5')
302 isp.push(' a = 5')
316 isp.push('except:')
303 isp.push('except:')
317 isp.push(' raise')
304 isp.push(' raise')
318 # We want to be able to add an else: block at this point, so it should
305 # We want to be able to add an else: block at this point, so it should
319 # wait for a blank line.
306 # wait for a blank line.
320 self.assertTrue(isp.push_accepts_more())
307 self.assertTrue(isp.push_accepts_more())
321
308
322 def test_continuation(self):
309 def test_continuation(self):
323 isp = self.isp
310 isp = self.isp
324 isp.push("import os, \\")
311 isp.push("import os, \\")
325 self.assertTrue(isp.push_accepts_more())
312 self.assertTrue(isp.push_accepts_more())
326 isp.push("sys")
313 isp.push("sys")
327 self.assertFalse(isp.push_accepts_more())
314 self.assertFalse(isp.push_accepts_more())
328
315
329 def test_syntax_error(self):
316 def test_syntax_error(self):
330 isp = self.isp
317 isp = self.isp
331 # Syntax errors immediately produce a 'ready' block, so the invalid
318 # Syntax errors immediately produce a 'ready' block, so the invalid
332 # Python can be sent to the kernel for evaluation with possible ipython
319 # Python can be sent to the kernel for evaluation with possible ipython
333 # special-syntax conversion.
320 # special-syntax conversion.
334 isp.push('run foo')
321 isp.push('run foo')
335 self.assertFalse(isp.push_accepts_more())
322 self.assertFalse(isp.push_accepts_more())
336
323
337 def test_unicode(self):
324 def test_unicode(self):
338 self.isp.push(u"PΓ©rez")
325 self.isp.push(u"PΓ©rez")
339 self.isp.push(u'\xc3\xa9')
326 self.isp.push(u'\xc3\xa9')
340 self.isp.push(u"u'\xc3\xa9'")
327 self.isp.push(u"u'\xc3\xa9'")
341
328
342 def test_line_continuation(self):
329 def test_line_continuation(self):
343 """ Test issue #2108."""
330 """ Test issue #2108."""
344 isp = self.isp
331 isp = self.isp
345 # A blank line after a line continuation should not accept more
332 # A blank line after a line continuation should not accept more
346 isp.push("1 \\\n\n")
333 isp.push("1 \\\n\n")
347 self.assertFalse(isp.push_accepts_more())
334 self.assertFalse(isp.push_accepts_more())
348 # Whitespace after a \ is a SyntaxError. The only way to test that
335 # Whitespace after a \ is a SyntaxError. The only way to test that
349 # here is to test that push doesn't accept more (as with
336 # here is to test that push doesn't accept more (as with
350 # test_syntax_error() above).
337 # test_syntax_error() above).
351 isp.push(r"1 \ ")
338 isp.push(r"1 \ ")
352 self.assertFalse(isp.push_accepts_more())
339 self.assertFalse(isp.push_accepts_more())
353 # Even if the line is continuable (c.f. the regular Python
340 # Even if the line is continuable (c.f. the regular Python
354 # interpreter)
341 # interpreter)
355 isp.push(r"(1 \ ")
342 isp.push(r"(1 \ ")
356 self.assertFalse(isp.push_accepts_more())
343 self.assertFalse(isp.push_accepts_more())
357
344
358 class InteractiveLoopTestCase(unittest.TestCase):
345 class InteractiveLoopTestCase(unittest.TestCase):
359 """Tests for an interactive loop like a python shell.
346 """Tests for an interactive loop like a python shell.
360 """
347 """
361 def check_ns(self, lines, ns):
348 def check_ns(self, lines, ns):
362 """Validate that the given input lines produce the resulting namespace.
349 """Validate that the given input lines produce the resulting namespace.
363
350
364 Note: the input lines are given exactly as they would be typed in an
351 Note: the input lines are given exactly as they would be typed in an
365 auto-indenting environment, as mini_interactive_loop above already does
352 auto-indenting environment, as mini_interactive_loop above already does
366 auto-indenting and prepends spaces to the input.
353 auto-indenting and prepends spaces to the input.
367 """
354 """
368 src = mini_interactive_loop(pseudo_input(lines))
355 src = mini_interactive_loop(pseudo_input(lines))
369 test_ns = {}
356 test_ns = {}
370 exec(src, test_ns)
357 exec(src, test_ns)
371 # We can't check that the provided ns is identical to the test_ns,
358 # We can't check that the provided ns is identical to the test_ns,
372 # because Python fills test_ns with extra keys (copyright, etc). But
359 # because Python fills test_ns with extra keys (copyright, etc). But
373 # we can check that the given dict is *contained* in test_ns
360 # we can check that the given dict is *contained* in test_ns
374 for k,v in ns.items():
361 for k,v in ns.items():
375 self.assertEqual(test_ns[k], v)
362 self.assertEqual(test_ns[k], v)
376
363
377 def test_simple(self):
364 def test_simple(self):
378 self.check_ns(['x=1'], dict(x=1))
365 self.check_ns(['x=1'], dict(x=1))
379
366
380 def test_simple2(self):
367 def test_simple2(self):
381 self.check_ns(['if 1:', 'x=2'], dict(x=2))
368 self.check_ns(['if 1:', 'x=2'], dict(x=2))
382
369
383 def test_xy(self):
370 def test_xy(self):
384 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
371 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
385
372
386 def test_abc(self):
373 def test_abc(self):
387 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
374 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
388
375
389 def test_multi(self):
376 def test_multi(self):
390 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
377 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
391
378
392
379
393 class IPythonInputTestCase(InputSplitterTestCase):
380 class IPythonInputTestCase(InputSplitterTestCase):
394 """By just creating a new class whose .isp is a different instance, we
381 """By just creating a new class whose .isp is a different instance, we
395 re-run the same test battery on the new input splitter.
382 re-run the same test battery on the new input splitter.
396
383
397 In addition, this runs the tests over the syntax and syntax_ml dicts that
384 In addition, this runs the tests over the syntax and syntax_ml dicts that
398 were tested by individual functions, as part of the OO interface.
385 were tested by individual functions, as part of the OO interface.
399
386
400 It also makes some checks on the raw buffer storage.
387 It also makes some checks on the raw buffer storage.
401 """
388 """
402
389
403 def setUp(self):
390 def setUp(self):
404 self.isp = isp.IPythonInputSplitter()
391 self.isp = isp.IPythonInputSplitter()
405
392
406 def test_syntax(self):
393 def test_syntax(self):
407 """Call all single-line syntax tests from the main object"""
394 """Call all single-line syntax tests from the main object"""
408 isp = self.isp
395 isp = self.isp
409 for example in syntax.values():
396 for example in syntax.values():
410 for raw, out_t in example:
397 for raw, out_t in example:
411 if raw.startswith(' '):
398 if raw.startswith(' '):
412 continue
399 continue
413
400
414 isp.push(raw+'\n')
401 isp.push(raw+'\n')
415 out_raw = isp.source_raw
402 out_raw = isp.source_raw
416 out = isp.source_reset()
403 out = isp.source_reset()
417 self.assertEqual(out.rstrip(), out_t,
404 self.assertEqual(out.rstrip(), out_t,
418 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
405 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
419 self.assertEqual(out_raw.rstrip(), raw.rstrip())
406 self.assertEqual(out_raw.rstrip(), raw.rstrip())
420
407
421 def test_syntax_multiline(self):
408 def test_syntax_multiline(self):
422 isp = self.isp
409 isp = self.isp
423 for example in syntax_ml.values():
410 for example in syntax_ml.values():
424 for line_pairs in example:
411 for line_pairs in example:
425 out_t_parts = []
412 out_t_parts = []
426 raw_parts = []
413 raw_parts = []
427 for lraw, out_t_part in line_pairs:
414 for lraw, out_t_part in line_pairs:
428 if out_t_part is not None:
415 if out_t_part is not None:
429 out_t_parts.append(out_t_part)
416 out_t_parts.append(out_t_part)
430
417
431 if lraw is not None:
418 if lraw is not None:
432 isp.push(lraw)
419 isp.push(lraw)
433 raw_parts.append(lraw)
420 raw_parts.append(lraw)
434
421
435 out_raw = isp.source_raw
422 out_raw = isp.source_raw
436 out = isp.source_reset()
423 out = isp.source_reset()
437 out_t = '\n'.join(out_t_parts).rstrip()
424 out_t = '\n'.join(out_t_parts).rstrip()
438 raw = '\n'.join(raw_parts).rstrip()
425 raw = '\n'.join(raw_parts).rstrip()
439 self.assertEqual(out.rstrip(), out_t)
426 self.assertEqual(out.rstrip(), out_t)
440 self.assertEqual(out_raw.rstrip(), raw)
427 self.assertEqual(out_raw.rstrip(), raw)
441
428
442 def test_syntax_multiline_cell(self):
429 def test_syntax_multiline_cell(self):
443 isp = self.isp
430 isp = self.isp
444 for example in syntax_ml.values():
431 for example in syntax_ml.values():
445
432
446 out_t_parts = []
433 out_t_parts = []
447 for line_pairs in example:
434 for line_pairs in example:
448 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
435 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
449 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
436 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
450 out = isp.transform_cell(raw)
437 out = isp.transform_cell(raw)
451 # Match ignoring trailing whitespace
438 # Match ignoring trailing whitespace
452 self.assertEqual(out.rstrip(), out_t.rstrip())
439 self.assertEqual(out.rstrip(), out_t.rstrip())
453
440
454 def test_cellmagic_preempt(self):
441 def test_cellmagic_preempt(self):
455 isp = self.isp
442 isp = self.isp
456 for raw, name, line, cell in [
443 for raw, name, line, cell in [
457 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
444 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
458 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
445 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
459 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
446 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
460 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
447 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
461 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
448 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
462 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
449 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
463 ]:
450 ]:
464 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
451 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
465 name, line, cell
452 name, line, cell
466 )
453 )
467 out = isp.transform_cell(raw)
454 out = isp.transform_cell(raw)
468 self.assertEqual(out.rstrip(), expected.rstrip())
455 self.assertEqual(out.rstrip(), expected.rstrip())
469
456
470
457 def test_multiline_passthrough(self):
458 isp = self.isp
459 class CommentTransformer(InputTransformer):
460 def __init__(self):
461 self._lines = []
462
463 def push(self, line):
464 self._lines.append(line + '#')
465
466 def reset(self):
467 text = '\n'.join(self._lines)
468 self._lines = []
469 return text
470
471 isp.physical_line_transforms.insert(0, CommentTransformer())
472
473 for raw, expected in [
474 ("a=5", "a=5#"),
475 ("%ls foo", "get_ipython().magic(%r)" % u'ls foo#'),
476 ("!ls foo\n%ls bar", "get_ipython().system(%r)\nget_ipython().magic(%r)" % (
477 u'ls foo#', u'ls bar#'
478 )),
479 ("1\n2\n3\n%ls foo\n4\n5", "1#\n2#\n3#\nget_ipython().magic(%r)\n4#\n5#" % u'ls foo#'),
480 ]:
481 out = isp.transform_cell(raw)
482 self.assertEqual(out.rstrip(), expected.rstrip())
471
483
472 #-----------------------------------------------------------------------------
484 #-----------------------------------------------------------------------------
473 # Main - use as a script, mostly for developer experiments
485 # Main - use as a script, mostly for developer experiments
474 #-----------------------------------------------------------------------------
486 #-----------------------------------------------------------------------------
475
487
476 if __name__ == '__main__':
488 if __name__ == '__main__':
477 # A simple demo for interactive experimentation. This code will not get
489 # A simple demo for interactive experimentation. This code will not get
478 # picked up by any test suite.
490 # picked up by any test suite.
479 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
491 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
480
492
481 # configure here the syntax to use, prompt and whether to autoindent
493 # configure here the syntax to use, prompt and whether to autoindent
482 #isp, start_prompt = InputSplitter(), '>>> '
494 #isp, start_prompt = InputSplitter(), '>>> '
483 isp, start_prompt = IPythonInputSplitter(), 'In> '
495 isp, start_prompt = IPythonInputSplitter(), 'In> '
484
496
485 autoindent = True
497 autoindent = True
486 #autoindent = False
498 #autoindent = False
487
499
488 try:
500 try:
489 while True:
501 while True:
490 prompt = start_prompt
502 prompt = start_prompt
491 while isp.push_accepts_more():
503 while isp.push_accepts_more():
492 indent = ' '*isp.indent_spaces
504 indent = ' '*isp.indent_spaces
493 if autoindent:
505 if autoindent:
494 line = indent + input(prompt+indent)
506 line = indent + input(prompt+indent)
495 else:
507 else:
496 line = input(prompt)
508 line = input(prompt)
497 isp.push(line)
509 isp.push(line)
498 prompt = '... '
510 prompt = '... '
499
511
500 # Here we just return input so we can use it in a test suite, but a
512 # Here we just return input so we can use it in a test suite, but a
501 # real interpreter would instead send it for execution somewhere.
513 # real interpreter would instead send it for execution somewhere.
502 #src = isp.source; raise EOFError # dbg
514 #src = isp.source; raise EOFError # dbg
503 raw = isp.source_raw
515 raw = isp.source_raw
504 src = isp.source_reset()
516 src = isp.source_reset()
505 print('Input source was:\n', src)
517 print('Input source was:\n', src)
506 print('Raw source was:\n', raw)
518 print('Raw source was:\n', raw)
507 except EOFError:
519 except EOFError:
508 print('Bye')
520 print('Bye')
509
521
510 # Tests for cell magics support
522 # Tests for cell magics support
511
523
512 def test_last_blank():
524 def test_last_blank():
513 nt.assert_false(isp.last_blank(''))
525 nt.assert_false(isp.last_blank(''))
514 nt.assert_false(isp.last_blank('abc'))
526 nt.assert_false(isp.last_blank('abc'))
515 nt.assert_false(isp.last_blank('abc\n'))
527 nt.assert_false(isp.last_blank('abc\n'))
516 nt.assert_false(isp.last_blank('abc\na'))
528 nt.assert_false(isp.last_blank('abc\na'))
517
529
518 nt.assert_true(isp.last_blank('\n'))
530 nt.assert_true(isp.last_blank('\n'))
519 nt.assert_true(isp.last_blank('\n '))
531 nt.assert_true(isp.last_blank('\n '))
520 nt.assert_true(isp.last_blank('abc\n '))
532 nt.assert_true(isp.last_blank('abc\n '))
521 nt.assert_true(isp.last_blank('abc\n\n'))
533 nt.assert_true(isp.last_blank('abc\n\n'))
522 nt.assert_true(isp.last_blank('abc\nd\n\n'))
534 nt.assert_true(isp.last_blank('abc\nd\n\n'))
523 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
535 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
524 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
536 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
525
537
526
538
527 def test_last_two_blanks():
539 def test_last_two_blanks():
528 nt.assert_false(isp.last_two_blanks(''))
540 nt.assert_false(isp.last_two_blanks(''))
529 nt.assert_false(isp.last_two_blanks('abc'))
541 nt.assert_false(isp.last_two_blanks('abc'))
530 nt.assert_false(isp.last_two_blanks('abc\n'))
542 nt.assert_false(isp.last_two_blanks('abc\n'))
531 nt.assert_false(isp.last_two_blanks('abc\n\na'))
543 nt.assert_false(isp.last_two_blanks('abc\n\na'))
532 nt.assert_false(isp.last_two_blanks('abc\n \n'))
544 nt.assert_false(isp.last_two_blanks('abc\n \n'))
533 nt.assert_false(isp.last_two_blanks('abc\n\n'))
545 nt.assert_false(isp.last_two_blanks('abc\n\n'))
534
546
535 nt.assert_true(isp.last_two_blanks('\n\n'))
547 nt.assert_true(isp.last_two_blanks('\n\n'))
536 nt.assert_true(isp.last_two_blanks('\n\n '))
548 nt.assert_true(isp.last_two_blanks('\n\n '))
537 nt.assert_true(isp.last_two_blanks('\n \n'))
549 nt.assert_true(isp.last_two_blanks('\n \n'))
538 nt.assert_true(isp.last_two_blanks('abc\n\n '))
550 nt.assert_true(isp.last_two_blanks('abc\n\n '))
539 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
551 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
540 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
552 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
541 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
553 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
542 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
554 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
543 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
555 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
544 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
556 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
545
557
546
558
547 class CellMagicsCommon(object):
559 class CellMagicsCommon(object):
548
560
549 def test_whole_cell(self):
561 def test_whole_cell(self):
550 src = "%%cellm line\nbody\n"
562 src = "%%cellm line\nbody\n"
551 out = self.sp.transform_cell(src)
563 out = self.sp.transform_cell(src)
552 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
564 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
553 nt.assert_equal(out, py3compat.u_format(ref))
565 nt.assert_equal(out, py3compat.u_format(ref))
554
566
555 def test_cellmagic_help(self):
567 def test_cellmagic_help(self):
556 self.sp.push('%%cellm?')
568 self.sp.push('%%cellm?')
557 nt.assert_false(self.sp.push_accepts_more())
569 nt.assert_false(self.sp.push_accepts_more())
558
570
559 def tearDown(self):
571 def tearDown(self):
560 self.sp.reset()
572 self.sp.reset()
561
573
562
574
563 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
575 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
564 sp = isp.IPythonInputSplitter(line_input_checker=False)
576 sp = isp.IPythonInputSplitter(line_input_checker=False)
565
577
566 def test_incremental(self):
578 def test_incremental(self):
567 sp = self.sp
579 sp = self.sp
568 sp.push('%%cellm firstline\n')
580 sp.push('%%cellm firstline\n')
569 nt.assert_true(sp.push_accepts_more()) #1
581 nt.assert_true(sp.push_accepts_more()) #1
570 sp.push('line2\n')
582 sp.push('line2\n')
571 nt.assert_true(sp.push_accepts_more()) #2
583 nt.assert_true(sp.push_accepts_more()) #2
572 sp.push('\n')
584 sp.push('\n')
573 # This should accept a blank line and carry on until the cell is reset
585 # This should accept a blank line and carry on until the cell is reset
574 nt.assert_true(sp.push_accepts_more()) #3
586 nt.assert_true(sp.push_accepts_more()) #3
575
587
576 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
588 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
577 sp = isp.IPythonInputSplitter(line_input_checker=True)
589 sp = isp.IPythonInputSplitter(line_input_checker=True)
578
590
579 def test_incremental(self):
591 def test_incremental(self):
580 sp = self.sp
592 sp = self.sp
581 sp.push('%%cellm line2\n')
593 sp.push('%%cellm line2\n')
582 nt.assert_true(sp.push_accepts_more()) #1
594 nt.assert_true(sp.push_accepts_more()) #1
583 sp.push('\n')
595 sp.push('\n')
584 # In this case, a blank line should end the cell magic
596 # In this case, a blank line should end the cell magic
585 nt.assert_false(sp.push_accepts_more()) #2
597 nt.assert_false(sp.push_accepts_more()) #2
General Comments 0
You need to be logged in to leave comments. Login now