##// END OF EJS Templates
Revised input transformation framework.
Thomas Kluyver -
Show More
@@ -1,651 +1,712 b''
1 1 """Analysis of text input into executable blocks.
2 2
3 3 The main class in this module, :class:`InputSplitter`, is designed to break
4 4 input from either interactive, line-by-line environments or block-based ones,
5 5 into standalone blocks that can be executed by Python as 'single' statements
6 6 (thus triggering sys.displayhook).
7 7
8 8 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
9 9 with full support for the extended IPython syntax (magics, system calls, etc).
10 10
11 11 For more details, see the class docstring below.
12 12
13 13 Syntax Transformations
14 14 ----------------------
15 15
16 16 One of the main jobs of the code in this file is to apply all syntax
17 17 transformations that make up 'the IPython language', i.e. magics, shell
18 18 escapes, etc. All transformations should be implemented as *fully stateless*
19 19 entities, that simply take one line as their input and return a line.
20 20 Internally for implementation purposes they may be a normal function or a
21 21 callable object, but the only input they receive will be a single line and they
22 22 should only return a line, without holding any data-dependent state between
23 23 calls.
24 24
25 25 As an example, the EscapedTransformer is a class so we can more clearly group
26 26 together the functionality of dispatching to individual functions based on the
27 27 starting escape character, but the only method for public use is its call
28 28 method.
29 29
30 30
31 31 ToDo
32 32 ----
33 33
34 34 - Should we make push() actually raise an exception once push_accepts_more()
35 35 returns False?
36 36
37 37 - Naming cleanups. The tr_* names aren't the most elegant, though now they are
38 38 at least just attributes of a class so not really very exposed.
39 39
40 40 - Think about the best way to support dynamic things: automagic, autocall,
41 41 macros, etc.
42 42
43 43 - Think of a better heuristic for the application of the transforms in
44 44 IPythonInputSplitter.push() than looking at the buffer ending in ':'. Idea:
45 45 track indentation change events (indent, dedent, nothing) and apply them only
46 46 if the indentation went up, but not otherwise.
47 47
48 48 - Think of the cleanest way for supporting user-specified transformations (the
49 49 user prefilters we had before).
50 50
51 51 Authors
52 52 -------
53 53
54 54 * Fernando Perez
55 55 * Brian Granger
56 56 """
57 57 #-----------------------------------------------------------------------------
58 58 # Copyright (C) 2010 The IPython Development Team
59 59 #
60 60 # Distributed under the terms of the BSD License. The full license is in
61 61 # the file COPYING, distributed as part of this software.
62 62 #-----------------------------------------------------------------------------
63 63
64 64 #-----------------------------------------------------------------------------
65 65 # Imports
66 66 #-----------------------------------------------------------------------------
67 67 # stdlib
68 68 import ast
69 69 import codeop
70 70 import re
71 71 import sys
72 72
73 73 # IPython modules
74 74 from IPython.core.splitinput import split_user_input, LineInfo
75 75 from IPython.utils.py3compat import cast_unicode
76 76 from IPython.core.inputtransformer import (leading_indent,
77 77 classic_prompt,
78 78 ipy_prompt,
79 79 cellmagic,
80 80 assemble_logical_lines,
81 81 help_end,
82 82 escaped_transformer,
83 83 assign_from_magic,
84 84 assign_from_system,
85 assemble_python_lines,
85 86 )
86 87
87 88 # Temporary!
88 89 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
89 90 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
90 91 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
91 92
92 93 #-----------------------------------------------------------------------------
93 94 # Utilities
94 95 #-----------------------------------------------------------------------------
95 96
96 97 # FIXME: These are general-purpose utilities that later can be moved to the
97 98 # general ward. Kept here for now because we're being very strict about test
98 99 # coverage with this code, and this lets us ensure that we keep 100% coverage
99 100 # while developing.
100 101
101 102 # compiled regexps for autoindent management
102 103 dedent_re = re.compile('|'.join([
103 104 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
104 105 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
105 106 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
106 107 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
107 108 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
108 109 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
109 110 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
110 111 ]))
111 112 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
112 113
113 114 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
114 115 # before pure comments
115 116 comment_line_re = re.compile('^\s*\#')
116 117
117 118
118 119 def num_ini_spaces(s):
119 120 """Return the number of initial spaces in a string.
120 121
121 122 Note that tabs are counted as a single space. For now, we do *not* support
122 123 mixing of tabs and spaces in the user's input.
123 124
124 125 Parameters
125 126 ----------
126 127 s : string
127 128
128 129 Returns
129 130 -------
130 131 n : int
131 132 """
132 133
133 134 ini_spaces = ini_spaces_re.match(s)
134 135 if ini_spaces:
135 136 return ini_spaces.end()
136 137 else:
137 138 return 0
138 139
139 140 def last_blank(src):
140 141 """Determine if the input source ends in a blank.
141 142
142 143 A blank is either a newline or a line consisting of whitespace.
143 144
144 145 Parameters
145 146 ----------
146 147 src : string
147 148 A single or multiline string.
148 149 """
149 150 if not src: return False
150 151 ll = src.splitlines()[-1]
151 152 return (ll == '') or ll.isspace()
152 153
153 154
154 155 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
155 156 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
156 157
157 158 def last_two_blanks(src):
158 159 """Determine if the input source ends in two blanks.
159 160
160 161 A blank is either a newline or a line consisting of whitespace.
161 162
162 163 Parameters
163 164 ----------
164 165 src : string
165 166 A single or multiline string.
166 167 """
167 168 if not src: return False
168 169 # The logic here is tricky: I couldn't get a regexp to work and pass all
169 170 # the tests, so I took a different approach: split the source by lines,
170 171 # grab the last two and prepend '###\n' as a stand-in for whatever was in
171 172 # the body before the last two lines. Then, with that structure, it's
172 173 # possible to analyze with two regexps. Not the most elegant solution, but
173 174 # it works. If anyone tries to change this logic, make sure to validate
174 175 # the whole test suite first!
175 176 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
176 177 return (bool(last_two_blanks_re.match(new_src)) or
177 178 bool(last_two_blanks_re2.match(new_src)) )
178 179
179 180
180 181 def remove_comments(src):
181 182 """Remove all comments from input source.
182 183
183 184 Note: comments are NOT recognized inside of strings!
184 185
185 186 Parameters
186 187 ----------
187 188 src : string
188 189 A single or multiline input string.
189 190
190 191 Returns
191 192 -------
192 193 String with all Python comments removed.
193 194 """
194 195
195 196 return re.sub('#.*', '', src)
196 197
197 198
198 199 def get_input_encoding():
199 200 """Return the default standard input encoding.
200 201
201 202 If sys.stdin has no encoding, 'ascii' is returned."""
202 203 # There are strange environments for which sys.stdin.encoding is None. We
203 204 # ensure that a valid encoding is returned.
204 205 encoding = getattr(sys.stdin, 'encoding', None)
205 206 if encoding is None:
206 207 encoding = 'ascii'
207 208 return encoding
208 209
209 210 #-----------------------------------------------------------------------------
210 211 # Classes and functions for normal Python syntax handling
211 212 #-----------------------------------------------------------------------------
212 213
213 214 class InputSplitter(object):
214 215 """An object that can accumulate lines of Python source before execution.
215 216
216 217 This object is designed to be fed python source line-by-line, using
217 218 :meth:`push`. It will return on each push whether the currently pushed
218 219 code could be executed already. In addition, it provides a method called
219 220 :meth:`push_accepts_more` that can be used to query whether more input
220 221 can be pushed into a single interactive block.
221 222
222 223 This is a simple example of how an interactive terminal-based client can use
223 224 this tool::
224 225
225 226 isp = InputSplitter()
226 227 while isp.push_accepts_more():
227 228 indent = ' '*isp.indent_spaces
228 229 prompt = '>>> ' + indent
229 230 line = indent + raw_input(prompt)
230 231 isp.push(line)
231 232 print 'Input source was:\n', isp.source_reset(),
232 233 """
233 234 # Number of spaces of indentation computed from input that has been pushed
234 235 # so far. This is the attributes callers should query to get the current
235 236 # indentation level, in order to provide auto-indent facilities.
236 237 indent_spaces = 0
237 238 # String, indicating the default input encoding. It is computed by default
238 239 # at initialization time via get_input_encoding(), but it can be reset by a
239 240 # client with specific knowledge of the encoding.
240 241 encoding = ''
241 242 # String where the current full source input is stored, properly encoded.
242 243 # Reading this attribute is the normal way of querying the currently pushed
243 244 # source code, that has been properly encoded.
244 245 source = ''
245 246 # Code object corresponding to the current source. It is automatically
246 247 # synced to the source, so it can be queried at any time to obtain the code
247 248 # object; it will be None if the source doesn't compile to valid Python.
248 249 code = None
249 250 # Input mode
250 251 input_mode = 'line'
251 252
252 253 # Private attributes
253 254
254 255 # List with lines of input accumulated so far
255 256 _buffer = None
256 257 # Command compiler
257 258 _compile = None
258 259 # Mark when input has changed indentation all the way back to flush-left
259 260 _full_dedent = False
260 261 # Boolean indicating whether the current block is complete
261 262 _is_complete = None
262 263
263 264 def __init__(self, input_mode=None):
264 265 """Create a new InputSplitter instance.
265 266
266 267 Parameters
267 268 ----------
268 269 input_mode : str
269 270
270 271 One of ['line', 'cell']; default is 'line'.
271 272
272 273 The input_mode parameter controls how new inputs are used when fed via
273 274 the :meth:`push` method:
274 275
275 276 - 'line': meant for line-oriented clients, inputs are appended one at a
276 277 time to the internal buffer and the whole buffer is compiled.
277 278
278 279 - 'cell': meant for clients that can edit multi-line 'cells' of text at
279 280 a time. A cell can contain one or more blocks that can be compile in
280 281 'single' mode by Python. In this mode, each new input new input
281 282 completely replaces all prior inputs. Cell mode is thus equivalent
282 283 to prepending a full reset() to every push() call.
283 284 """
284 285 self._buffer = []
285 286 self._compile = codeop.CommandCompiler()
286 287 self.encoding = get_input_encoding()
287 288 self.input_mode = InputSplitter.input_mode if input_mode is None \
288 289 else input_mode
289 290
290 291 def reset(self):
291 292 """Reset the input buffer and associated state."""
292 293 self.indent_spaces = 0
293 294 self._buffer[:] = []
294 295 self.source = ''
295 296 self.code = None
296 297 self._is_complete = False
297 298 self._full_dedent = False
298 299
299 300 def source_reset(self):
300 301 """Return the input source and perform a full reset.
301 302 """
302 303 out = self.source
303 304 self.reset()
304 305 return out
305 306
306 307 def push(self, lines):
307 308 """Push one or more lines of input.
308 309
309 310 This stores the given lines and returns a status code indicating
310 311 whether the code forms a complete Python block or not.
311 312
312 313 Any exceptions generated in compilation are swallowed, but if an
313 314 exception was produced, the method returns True.
314 315
315 316 Parameters
316 317 ----------
317 318 lines : string
318 319 One or more lines of Python input.
319 320
320 321 Returns
321 322 -------
322 323 is_complete : boolean
323 324 True if the current input source (the result of the current input
324 325 plus prior inputs) forms a complete Python execution block. Note that
325 326 this value is also stored as a private attribute (``_is_complete``), so it
326 327 can be queried at any time.
327 328 """
328 329 if self.input_mode == 'cell':
329 330 self.reset()
330 331
331 332 self._store(lines)
332 333 source = self.source
333 334
334 335 # Before calling _compile(), reset the code object to None so that if an
335 336 # exception is raised in compilation, we don't mislead by having
336 337 # inconsistent code/source attributes.
337 338 self.code, self._is_complete = None, None
338 339
339 340 # Honor termination lines properly
340 341 if source.endswith('\\\n'):
341 342 return False
342 343
343 344 self._update_indent(lines)
344 345 try:
345 346 self.code = self._compile(source, symbol="exec")
346 347 # Invalid syntax can produce any of a number of different errors from
347 348 # inside the compiler, so we have to catch them all. Syntax errors
348 349 # immediately produce a 'ready' block, so the invalid Python can be
349 350 # sent to the kernel for evaluation with possible ipython
350 351 # special-syntax conversion.
351 352 except (SyntaxError, OverflowError, ValueError, TypeError,
352 353 MemoryError):
353 354 self._is_complete = True
354 355 else:
355 356 # Compilation didn't produce any exceptions (though it may not have
356 357 # given a complete code object)
357 358 self._is_complete = self.code is not None
358 359
359 360 return self._is_complete
360 361
361 362 def push_accepts_more(self):
362 363 """Return whether a block of interactive input can accept more input.
363 364
364 365 This method is meant to be used by line-oriented frontends, who need to
365 366 guess whether a block is complete or not based solely on prior and
366 367 current input lines. The InputSplitter considers it has a complete
367 368 interactive block and will not accept more input only when either a
368 369 SyntaxError is raised, or *all* of the following are true:
369 370
370 371 1. The input compiles to a complete statement.
371 372
372 373 2. The indentation level is flush-left (because if we are indented,
373 374 like inside a function definition or for loop, we need to keep
374 375 reading new input).
375 376
376 377 3. There is one extra line consisting only of whitespace.
377 378
378 379 Because of condition #3, this method should be used only by
379 380 *line-oriented* frontends, since it means that intermediate blank lines
380 381 are not allowed in function definitions (or any other indented block).
381 382
382 383 If the current input produces a syntax error, this method immediately
383 384 returns False but does *not* raise the syntax error exception, as
384 385 typically clients will want to send invalid syntax to an execution
385 386 backend which might convert the invalid syntax into valid Python via
386 387 one of the dynamic IPython mechanisms.
387 388 """
388 389
389 390 # With incomplete input, unconditionally accept more
390 391 if not self._is_complete:
391 392 return True
392 393
393 394 # If we already have complete input and we're flush left, the answer
394 395 # depends. In line mode, if there hasn't been any indentation,
395 396 # that's it. If we've come back from some indentation, we need
396 397 # the blank final line to finish.
397 398 # In cell mode, we need to check how many blocks the input so far
398 399 # compiles into, because if there's already more than one full
399 400 # independent block of input, then the client has entered full
400 401 # 'cell' mode and is feeding lines that each is complete. In this
401 402 # case we should then keep accepting. The Qt terminal-like console
402 403 # does precisely this, to provide the convenience of terminal-like
403 404 # input of single expressions, but allowing the user (with a
404 405 # separate keystroke) to switch to 'cell' mode and type multiple
405 406 # expressions in one shot.
406 407 if self.indent_spaces==0:
407 408 if self.input_mode=='line':
408 409 if not self._full_dedent:
409 410 return False
410 411 else:
411 412 try:
412 413 code_ast = ast.parse(u''.join(self._buffer))
413 414 except Exception:
414 415 return False
415 416 else:
416 417 if len(code_ast.body) == 1:
417 418 return False
418 419
419 420 # When input is complete, then termination is marked by an extra blank
420 421 # line at the end.
421 422 last_line = self.source.splitlines()[-1]
422 423 return bool(last_line and not last_line.isspace())
423 424
424 425 #------------------------------------------------------------------------
425 426 # Private interface
426 427 #------------------------------------------------------------------------
427 428
428 429 def _find_indent(self, line):
429 430 """Compute the new indentation level for a single line.
430 431
431 432 Parameters
432 433 ----------
433 434 line : str
434 435 A single new line of non-whitespace, non-comment Python input.
435 436
436 437 Returns
437 438 -------
438 439 indent_spaces : int
439 440 New value for the indent level (it may be equal to self.indent_spaces
440 441 if indentation doesn't change.
441 442
442 443 full_dedent : boolean
443 444 Whether the new line causes a full flush-left dedent.
444 445 """
445 446 indent_spaces = self.indent_spaces
446 447 full_dedent = self._full_dedent
447 448
448 449 inisp = num_ini_spaces(line)
449 450 if inisp < indent_spaces:
450 451 indent_spaces = inisp
451 452 if indent_spaces <= 0:
452 453 #print 'Full dedent in text',self.source # dbg
453 454 full_dedent = True
454 455
455 456 if line.rstrip()[-1] == ':':
456 457 indent_spaces += 4
457 458 elif dedent_re.match(line):
458 459 indent_spaces -= 4
459 460 if indent_spaces <= 0:
460 461 full_dedent = True
461 462
462 463 # Safety
463 464 if indent_spaces < 0:
464 465 indent_spaces = 0
465 466 #print 'safety' # dbg
466 467
467 468 return indent_spaces, full_dedent
468 469
469 470 def _update_indent(self, lines):
470 471 for line in remove_comments(lines).splitlines():
471 472 if line and not line.isspace():
472 473 self.indent_spaces, self._full_dedent = self._find_indent(line)
473 474
474 475 def _store(self, lines, buffer=None, store='source'):
475 476 """Store one or more lines of input.
476 477
477 478 If input lines are not newline-terminated, a newline is automatically
478 479 appended."""
479 480
480 481 if buffer is None:
481 482 buffer = self._buffer
482 483
483 484 if lines.endswith('\n'):
484 485 buffer.append(lines)
485 486 else:
486 487 buffer.append(lines+'\n')
487 488 setattr(self, store, self._set_source(buffer))
488 489
489 490 def _set_source(self, buffer):
490 491 return u''.join(buffer)
491 492
492 493
493 494 class IPythonInputSplitter(InputSplitter):
494 495 """An input splitter that recognizes all of IPython's special syntax."""
495 496
496 497 # String with raw, untransformed input.
497 498 source_raw = ''
498 499
499 500 # Flag to track when a transformer has stored input that it hasn't given
500 501 # back yet.
501 502 transformer_accumulating = False
503
504 # Flag to track when assemble_python_lines has stored input that it hasn't
505 # given back yet.
506 within_python_line = False
502 507
503 508 # Private attributes
504 509
505 510 # List with lines of raw input accumulated so far.
506 511 _buffer_raw = None
507 512
508 def __init__(self, input_mode=None, transforms=None):
513 def __init__(self, input_mode=None, physical_line_transforms=None,
514 logical_line_transforms=None, python_line_transforms=None):
509 515 super(IPythonInputSplitter, self).__init__(input_mode)
510 516 self._buffer_raw = []
511 517 self._validate = True
512 if transforms is not None:
513 self.transforms = transforms
514 else:
515 self.transforms = [leading_indent(),
516 classic_prompt(),
517 ipy_prompt(),
518 cellmagic(),
519 assemble_logical_lines(),
520 help_end(),
521 escaped_transformer(),
522 assign_from_magic(),
523 assign_from_system(),
524 ]
518
519 self.physical_line_transforms = physical_line_transforms or \
520 [leading_indent(),
521 classic_prompt(),
522 ipy_prompt(),
523 ]
524
525 self.assemble_logical_lines = assemble_logical_lines()
526 self.logical_line_transforms = logical_line_transforms or \
527 [cellmagic(),
528 help_end(),
529 escaped_transformer(),
530 assign_from_magic(),
531 assign_from_system(),
532 ]
533
534 self.assemble_python_lines = assemble_python_lines()
535 self.python_line_transforms = python_line_transforms or []
536
537 @property
538 def transforms(self):
539 "Quick access to all transformers."
540 return self.physical_line_transforms + \
541 [self.assemble_logical_lines] + self.logical_line_transforms + \
542 [self.assemble_python_lines] + self.python_line_transforms
543
544 @property
545 def transforms_in_use(self):
546 """Transformers, excluding logical line transformers if we're in a
547 Python line."""
548 t = self.physical_line_transforms + [self.assemble_logical_lines]
549 if not self.within_python_line:
550 t += self.logical_line_transforms
551 return t + [self.assemble_python_lines] + self.python_line_transforms
525 552
526 553 def reset(self):
527 554 """Reset the input buffer and associated state."""
528 555 super(IPythonInputSplitter, self).reset()
529 556 self._buffer_raw[:] = []
530 557 self.source_raw = ''
531 558 self.transformer_accumulating = False
532 559 for t in self.transforms:
533 560 t.reset()
534 561
535 562 def flush_transformers(self):
563 def _flush(transform, out):
564 if out is not None:
565 tmp = transform.push(out)
566 return tmp or transform.reset() or None
567 else:
568 return transform.reset() or None
569
536 570 out = None
537 for t in self.transforms:
538 tmp = t.reset()
539 if tmp:
540 out = tmp
541 if out:
571 for t in self.transforms_in_use:
572 out = _flush(t, out)
573
574 if out is not None:
542 575 self._store(out)
543 576
544 577 def source_raw_reset(self):
545 578 """Return input and raw source and perform a full reset.
546 579 """
547 580 self.flush_transformers()
548 581 out = self.source
549 582 out_r = self.source_raw
550 583 self.reset()
551 584 return out, out_r
552 585
553 586 def source_reset(self):
554 587 self.flush_transformers()
555 588 return super(IPythonInputSplitter, self).source_reset()
556 589
557 590 def push_accepts_more(self):
558 591 if self.transformer_accumulating:
559 592 return True
560 593 else:
561 594 return super(IPythonInputSplitter, self).push_accepts_more()
562 595
563 596 def transform_cell(self, cell):
564 597 """Process and translate a cell of input.
565 598 """
566 599 self.reset()
567 600 self.push(cell)
568 601 return self.source_reset()
569 602
570 603 def push(self, lines):
571 604 """Push one or more lines of IPython input.
572 605
573 606 This stores the given lines and returns a status code indicating
574 607 whether the code forms a complete Python block or not, after processing
575 608 all input lines for special IPython syntax.
576 609
577 610 Any exceptions generated in compilation are swallowed, but if an
578 611 exception was produced, the method returns True.
579 612
580 613 Parameters
581 614 ----------
582 615 lines : string
583 616 One or more lines of Python input.
584 617
585 618 Returns
586 619 -------
587 620 is_complete : boolean
588 621 True if the current input source (the result of the current input
589 622 plus prior inputs) forms a complete Python execution block. Note that
590 623 this value is also stored as a private attribute (_is_complete), so it
591 624 can be queried at any time.
592 625 """
593 626
594 627 # We must ensure all input is pure unicode
595 628 lines = cast_unicode(lines, self.encoding)
596 629
597 630 # ''.splitlines() --> [], but we need to push the empty line to transformers
598 631 lines_list = lines.splitlines()
599 632 if not lines_list:
600 633 lines_list = ['']
601 634
602 635 # Transform logic
603 636 #
604 637 # We only apply the line transformers to the input if we have either no
605 638 # input yet, or complete input, or if the last line of the buffer ends
606 639 # with ':' (opening an indented block). This prevents the accidental
607 640 # transformation of escapes inside multiline expressions like
608 641 # triple-quoted strings or parenthesized expressions.
609 642 #
610 643 # The last heuristic, while ugly, ensures that the first line of an
611 644 # indented block is correctly transformed.
612 645 #
613 646 # FIXME: try to find a cleaner approach for this last bit.
614 647
615 648 # If we were in 'block' mode, since we're going to pump the parent
616 649 # class by hand line by line, we need to temporarily switch out to
617 650 # 'line' mode, do a single manual reset and then feed the lines one
618 651 # by one. Note that this only matters if the input has more than one
619 652 # line.
620 653 changed_input_mode = False
621 654
622 655 if self.input_mode == 'cell':
623 656 self.reset()
624 657 changed_input_mode = True
625 658 saved_input_mode = 'cell'
626 659 self.input_mode = 'line'
627 660
628 661 # Store raw source before applying any transformations to it. Note
629 662 # that this must be done *after* the reset() call that would otherwise
630 663 # flush the buffer.
631 664 self._store(lines, self._buffer_raw, 'source_raw')
632 665
633 666 try:
634 667 for line in lines_list:
635 668 out = self.push_line(line)
636 669 finally:
637 670 if changed_input_mode:
638 671 self.input_mode = saved_input_mode
639 672
640 673 return out
641 674
642 675 def push_line(self, line):
643 676 buf = self._buffer
644 for transformer in self.transforms:
677
678 def _accumulating(dbg):
679 #print(dbg)
680 self.transformer_accumulating = True
681 return False
682
683 for transformer in self.physical_line_transforms:
684 line = transformer.push(line)
685 if line is None:
686 return _accumulating(transformer)
687
688 line = self.assemble_logical_lines.push(line)
689 if line is None:
690 return _accumulating('acc logical line')
691
692 if not self.within_python_line:
693 for transformer in self.logical_line_transforms:
694 line = transformer.push(line)
695 if line is None:
696 return _accumulating(transformer)
697
698 line = self.assemble_python_lines.push(line)
699 if line is None:
700 self.within_python_line = True
701 return _accumulating('acc python line')
702 else:
703 self.within_python_line = False
704
705 for transformer in self.python_line_transforms:
645 706 line = transformer.push(line)
646 707 if line is None:
647 self.transformer_accumulating = True
648 return False
708 return _accumulating(transformer)
649 709
710 #print("transformers clear") #debug
650 711 self.transformer_accumulating = False
651 712 return super(IPythonInputSplitter, self).push(line)
@@ -1,454 +1,487 b''
1 1 import abc
2 2 import functools
3 3 import re
4 4 from StringIO import StringIO
5 5 import tokenize
6 6
7 7 try:
8 8 generate_tokens = tokenize.generate_tokens
9 9 except AttributeError:
10 10 # Python 3. Note that we use the undocumented _tokenize because it expects
11 11 # strings, not bytes. See also Python issue #9969.
12 12 generate_tokens = tokenize._tokenize
13 13
14 14 from IPython.core.splitinput import split_user_input, LineInfo
15 15 from IPython.utils.untokenize import untokenize
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Globals
19 19 #-----------------------------------------------------------------------------
20 20
21 21 # The escape sequences that define the syntax transformations IPython will
22 22 # apply to user input. These can NOT be just changed here: many regular
23 23 # expressions and other parts of the code may use their hardcoded values, and
24 24 # for all intents and purposes they constitute the 'IPython syntax', so they
25 25 # should be considered fixed.
26 26
27 27 ESC_SHELL = '!' # Send line to underlying system shell
28 28 ESC_SH_CAP = '!!' # Send line to system shell and capture output
29 29 ESC_HELP = '?' # Find information about object
30 30 ESC_HELP2 = '??' # Find extra-detailed information about object
31 31 ESC_MAGIC = '%' # Call magic function
32 32 ESC_MAGIC2 = '%%' # Call cell-magic function
33 33 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
34 34 ESC_QUOTE2 = ';' # Quote all args as a single string, call
35 35 ESC_PAREN = '/' # Call first argument with rest of line as arguments
36 36
37 37 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
38 38 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
39 39 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
40 40
41 41
42 42 class InputTransformer(object):
43 43 """Abstract base class for line-based input transformers."""
44 44 __metaclass__ = abc.ABCMeta
45 45
46 46 @abc.abstractmethod
47 47 def push(self, line):
48 48 """Send a line of input to the transformer, returning the transformed
49 49 input or None if the transformer is waiting for more input.
50 50
51 51 Must be overridden by subclasses.
52 52 """
53 53 pass
54 54
55 55 @abc.abstractmethod
56 56 def reset(self):
57 57 """Return, transformed any lines that the transformer has accumulated,
58 58 and reset its internal state.
59 59
60 60 Must be overridden by subclasses.
61 61 """
62 62 pass
63 63
64 64 # Set this to True to allow the transformer to act on lines inside strings.
65 65 look_in_string = False
66 66
67 67 @classmethod
68 68 def wrap(cls, func):
69 69 """Can be used by subclasses as a decorator, to return a factory that
70 70 will allow instantiation with the decorated object.
71 71 """
72 72 @functools.wraps(func)
73 73 def transformer_factory():
74 74 transformer = cls(func)
75 75 if getattr(transformer_factory, 'look_in_string', False):
76 76 transformer.look_in_string = True
77 77 return transformer
78 78
79 79 return transformer_factory
80 80
81 81 class StatelessInputTransformer(InputTransformer):
82 82 """Wrapper for a stateless input transformer implemented as a function."""
83 83 def __init__(self, func):
84 84 self.func = func
85 85
86 86 def __repr__(self):
87 87 return "StatelessInputTransformer(func={!r})".format(self.func)
88 88
89 89 def push(self, line):
90 90 """Send a line of input to the transformer, returning the
91 91 transformed input."""
92 92 return self.func(line)
93 93
94 94 def reset(self):
95 95 """No-op - exists for compatibility."""
96 96 pass
97 97
98 98 class CoroutineInputTransformer(InputTransformer):
99 99 """Wrapper for an input transformer implemented as a coroutine."""
100 100 def __init__(self, coro):
101 101 # Prime it
102 102 self.coro = coro()
103 103 next(self.coro)
104 104
105 105 def __repr__(self):
106 106 return "CoroutineInputTransformer(coro={!r})".format(self.coro)
107 107
108 108 def push(self, line):
109 109 """Send a line of input to the transformer, returning the
110 110 transformed input or None if the transformer is waiting for more
111 111 input.
112 112 """
113 113 return self.coro.send(line)
114 114
115 115 def reset(self):
116 116 """Return, transformed any lines that the transformer has
117 117 accumulated, and reset its internal state.
118 118 """
119 119 return self.coro.send(None)
120 120
121 121 class TokenInputTransformer(InputTransformer):
122 122 """Wrapper for a token-based input transformer.
123 123
124 124 func should accept a list of tokens (5-tuples, see tokenize docs), and
125 125 return an iterable which can be passed to tokenize.untokenize().
126 126 """
127 127 def __init__(self, func):
128 128 self.func = func
129 129 self.current_line = ""
130 self.line_used= False
130 self.line_used = False
131 131 self.reset_tokenizer()
132 132
133 133 def reset_tokenizer(self):
134 134 self.tokenizer = generate_tokens(self.get_line)
135 135
136 136 def get_line(self):
137 137 if self.line_used:
138 138 raise tokenize.TokenError
139 139 self.line_used = True
140 140 return self.current_line
141 141
142 142 def push(self, line):
143 143 self.current_line += line + "\n"
144 if self.current_line.isspace():
145 return self.reset()
146
144 147 self.line_used = False
145 148 tokens = []
149 stop_at_NL = False
146 150 try:
147 151 for intok in self.tokenizer:
148 152 tokens.append(intok)
149 if intok[0] in (tokenize.NEWLINE, tokenize.NL):
153 t = intok[0]
154 if t == tokenize.NEWLINE or (stop_at_NL and t == tokenize.NL):
150 155 # Stop before we try to pull a line we don't have yet
151 156 break
157 elif t in (tokenize.COMMENT, tokenize.ERRORTOKEN):
158 stop_at_NL = True
152 159 except tokenize.TokenError:
153 160 # Multi-line statement - stop and try again with the next line
154 161 self.reset_tokenizer()
155 162 return None
156 163
164 return self.output(tokens)
165
166 def output(self, tokens):
157 167 self.current_line = ""
158 168 self.reset_tokenizer()
159 169 return untokenize(self.func(tokens)).rstrip('\n')
160 170
161 171 def reset(self):
162 172 l = self.current_line
163 173 self.current_line = ""
174 self.reset_tokenizer()
164 175 if l:
165 176 return l.rstrip('\n')
166 177
167 @TokenInputTransformer.wrap
168 def assemble_logical_lines(tokens):
169 return tokens
178 class assemble_python_lines(TokenInputTransformer):
179 def __init__(self):
180 super(assemble_python_lines, self).__init__(None)
181
182 def output(self, tokens):
183 return self.reset()
184
185 @CoroutineInputTransformer.wrap
186 def assemble_logical_lines():
187 """Join lines following explicit line continuations (\)"""
188 line = ''
189 while True:
190 line = (yield line)
191 if not line or line.isspace():
192 continue
193
194 parts = []
195 while line is not None:
196 parts.append(line.rstrip('\\'))
197 if not line.endswith('\\'):
198 break
199 line = (yield None)
200
201 # Output
202 line = ' '.join(parts)
170 203
171 204 # Utilities
172 205 def _make_help_call(target, esc, lspace, next_input=None):
173 206 """Prepares a pinfo(2)/psearch call from a target name and the escape
174 207 (i.e. ? or ??)"""
175 208 method = 'pinfo2' if esc == '??' \
176 209 else 'psearch' if '*' in target \
177 210 else 'pinfo'
178 211 arg = " ".join([method, target])
179 212 if next_input is None:
180 213 return '%sget_ipython().magic(%r)' % (lspace, arg)
181 214 else:
182 215 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
183 216 (lspace, next_input, arg)
184 217
185 218 @CoroutineInputTransformer.wrap
186 219 def escaped_transformer():
187 220 """Translate lines beginning with one of IPython's escape characters.
188 221
189 222 This is stateful to allow magic commands etc. to be continued over several
190 223 lines using explicit line continuations (\ at the end of a line).
191 224 """
192 225
193 226 # These define the transformations for the different escape characters.
194 227 def _tr_system(line_info):
195 228 "Translate lines escaped with: !"
196 229 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
197 230 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
198 231
199 232 def _tr_system2(line_info):
200 233 "Translate lines escaped with: !!"
201 234 cmd = line_info.line.lstrip()[2:]
202 235 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
203 236
204 237 def _tr_help(line_info):
205 238 "Translate lines escaped with: ?/??"
206 239 # A naked help line should just fire the intro help screen
207 240 if not line_info.line[1:]:
208 241 return 'get_ipython().show_usage()'
209 242
210 243 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
211 244
212 245 def _tr_magic(line_info):
213 246 "Translate lines escaped with: %"
214 247 tpl = '%sget_ipython().magic(%r)'
215 248 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
216 249 return tpl % (line_info.pre, cmd)
217 250
218 251 def _tr_quote(line_info):
219 252 "Translate lines escaped with: ,"
220 253 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
221 254 '", "'.join(line_info.the_rest.split()) )
222 255
223 256 def _tr_quote2(line_info):
224 257 "Translate lines escaped with: ;"
225 258 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
226 259 line_info.the_rest)
227 260
228 261 def _tr_paren(line_info):
229 262 "Translate lines escaped with: /"
230 263 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
231 264 ", ".join(line_info.the_rest.split()))
232 265
233 266 tr = { ESC_SHELL : _tr_system,
234 267 ESC_SH_CAP : _tr_system2,
235 268 ESC_HELP : _tr_help,
236 269 ESC_HELP2 : _tr_help,
237 270 ESC_MAGIC : _tr_magic,
238 271 ESC_QUOTE : _tr_quote,
239 272 ESC_QUOTE2 : _tr_quote2,
240 273 ESC_PAREN : _tr_paren }
241 274
242 275 line = ''
243 276 while True:
244 277 line = (yield line)
245 278 if not line or line.isspace():
246 279 continue
247 280 lineinf = LineInfo(line)
248 281 if lineinf.esc not in tr:
249 282 continue
250 283
251 284 parts = []
252 285 while line is not None:
253 286 parts.append(line.rstrip('\\'))
254 287 if not line.endswith('\\'):
255 288 break
256 289 line = (yield None)
257 290
258 291 # Output
259 292 lineinf = LineInfo(' '.join(parts))
260 293 line = tr[lineinf.esc](lineinf)
261 294
262 295 _initial_space_re = re.compile(r'\s*')
263 296
264 297 _help_end_re = re.compile(r"""(%{0,2}
265 298 [a-zA-Z_*][\w*]* # Variable name
266 299 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
267 300 )
268 301 (\?\??)$ # ? or ??""",
269 302 re.VERBOSE)
270 303
271 304 def has_comment(src):
272 305 """Indicate whether an input line has (i.e. ends in, or is) a comment.
273 306
274 307 This uses tokenize, so it can distinguish comments from # inside strings.
275 308
276 309 Parameters
277 310 ----------
278 311 src : string
279 312 A single line input string.
280 313
281 314 Returns
282 315 -------
283 316 Boolean: True if source has a comment.
284 317 """
285 318 readline = StringIO(src).readline
286 319 toktypes = set()
287 320 try:
288 321 for t in tokenize.generate_tokens(readline):
289 322 toktypes.add(t[0])
290 323 except tokenize.TokenError:
291 324 pass
292 325 return(tokenize.COMMENT in toktypes)
293 326
294 327
295 328 @StatelessInputTransformer.wrap
296 329 def help_end(line):
297 330 """Translate lines with ?/?? at the end"""
298 331 m = _help_end_re.search(line)
299 332 if m is None or has_comment(line):
300 333 return line
301 334 target = m.group(1)
302 335 esc = m.group(3)
303 336 lspace = _initial_space_re.match(line).group(0)
304 337
305 338 # If we're mid-command, put it back on the next prompt for the user.
306 339 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
307 340
308 341 return _make_help_call(target, esc, lspace, next_input)
309 342
310 343
311 344 @CoroutineInputTransformer.wrap
312 345 def cellmagic():
313 346 """Captures & transforms cell magics.
314 347
315 348 After a cell magic is started, this stores up any lines it gets until it is
316 349 reset (sent None).
317 350 """
318 351 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
319 352 cellmagic_help_re = re.compile('%%\w+\?')
320 353 line = ''
321 354 while True:
322 355 line = (yield line)
323 356 if (not line) or (not line.startswith(ESC_MAGIC2)):
324 357 continue
325 358
326 359 if cellmagic_help_re.match(line):
327 360 # This case will be handled by help_end
328 361 continue
329 362
330 363 first = line
331 364 body = []
332 365 line = (yield None)
333 366 while (line is not None) and (line.strip() != ''):
334 367 body.append(line)
335 368 line = (yield None)
336 369
337 370 # Output
338 371 magic_name, _, first = first.partition(' ')
339 372 magic_name = magic_name.lstrip(ESC_MAGIC2)
340 373 line = tpl % (magic_name, first, u'\n'.join(body))
341 374
342 375
343 376 def _strip_prompts(prompt1_re, prompt2_re):
344 377 """Remove matching input prompts from a block of input."""
345 378 line = ''
346 379 while True:
347 380 line = (yield line)
348 381
349 382 if line is None:
350 383 continue
351 384
352 385 m = prompt1_re.match(line)
353 386 if m:
354 387 while m:
355 388 line = (yield line[len(m.group(0)):])
356 389 if line is None:
357 390 break
358 391 m = prompt2_re.match(line)
359 392 else:
360 393 # Prompts not in input - wait for reset
361 394 while line is not None:
362 395 line = (yield line)
363 396
364 397 @CoroutineInputTransformer.wrap
365 398 def classic_prompt():
366 399 """Strip the >>>/... prompts of the Python interactive shell."""
367 400 prompt1_re = re.compile(r'^(>>> )')
368 401 prompt2_re = re.compile(r'^(>>> |^\.\.\. )')
369 402 return _strip_prompts(prompt1_re, prompt2_re)
370 403
371 404 classic_prompt.look_in_string = True
372 405
373 406 @CoroutineInputTransformer.wrap
374 407 def ipy_prompt():
375 408 """Strip IPython's In [1]:/...: prompts."""
376 409 prompt1_re = re.compile(r'^In \[\d+\]: ')
377 410 prompt2_re = re.compile(r'^(In \[\d+\]: |^\ \ \ \.\.\.+: )')
378 411 return _strip_prompts(prompt1_re, prompt2_re)
379 412
380 413 ipy_prompt.look_in_string = True
381 414
382 415
383 416 @CoroutineInputTransformer.wrap
384 417 def leading_indent():
385 418 """Remove leading indentation.
386 419
387 420 If the first line starts with a spaces or tabs, the same whitespace will be
388 421 removed from each following line until it is reset.
389 422 """
390 423 space_re = re.compile(r'^[ \t]+')
391 424 line = ''
392 425 while True:
393 426 line = (yield line)
394 427
395 428 if line is None:
396 429 continue
397 430
398 431 m = space_re.match(line)
399 432 if m:
400 433 space = m.group(0)
401 434 while line is not None:
402 435 if line.startswith(space):
403 436 line = line[len(space):]
404 437 line = (yield line)
405 438 else:
406 439 # No leading spaces - wait for reset
407 440 while line is not None:
408 441 line = (yield line)
409 442
410 443 leading_indent.look_in_string = True
411 444
412 445
413 446 def _special_assignment(assignment_re, template):
414 447 """Transform assignment from system & magic commands.
415 448
416 449 This is stateful so that it can handle magic commands continued on several
417 450 lines.
418 451 """
419 452 line = ''
420 453 while True:
421 454 line = (yield line)
422 455 if not line or line.isspace():
423 456 continue
424 457
425 458 m = assignment_re.match(line)
426 459 if not m:
427 460 continue
428 461
429 462 parts = []
430 463 while line is not None:
431 464 parts.append(line.rstrip('\\'))
432 465 if not line.endswith('\\'):
433 466 break
434 467 line = (yield None)
435 468
436 469 # Output
437 470 whole = assignment_re.match(' '.join(parts))
438 471 line = template % (whole.group('lhs'), whole.group('cmd'))
439 472
440 473 @CoroutineInputTransformer.wrap
441 474 def assign_from_system():
442 475 """Transform assignment from system commands (e.g. files = !ls)"""
443 476 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
444 477 r'\s*=\s*!\s*(?P<cmd>.*)')
445 478 template = '%s = get_ipython().getoutput(%r)'
446 479 return _special_assignment(assignment_re, template)
447 480
448 481 @CoroutineInputTransformer.wrap
449 482 def assign_from_magic():
450 483 """Transform assignment from magic commands (e.g. a = %who_ls)"""
451 484 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
452 485 r'\s*=\s*%\s*(?P<cmd>.*)')
453 486 template = '%s = get_ipython().magic(%r)'
454 487 return _special_assignment(assignment_re, template)
@@ -1,625 +1,626 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the inputsplitter module.
3 3
4 4 Authors
5 5 -------
6 6 * Fernando Perez
7 7 * Robert Kern
8 8 """
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2010-2011 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19 # stdlib
20 20 import unittest
21 21 import sys
22 22
23 23 # Third party
24 24 import nose.tools as nt
25 25
26 26 # Our own
27 27 from IPython.core import inputsplitter as isp
28 28 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
29 29 from IPython.testing import tools as tt
30 30 from IPython.utils import py3compat
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Semi-complete examples (also used as tests)
34 34 #-----------------------------------------------------------------------------
35 35
36 36 # Note: at the bottom, there's a slightly more complete version of this that
37 37 # can be useful during development of code here.
38 38
39 39 def mini_interactive_loop(input_func):
40 40 """Minimal example of the logic of an interactive interpreter loop.
41 41
42 42 This serves as an example, and it is used by the test system with a fake
43 43 raw_input that simulates interactive input."""
44 44
45 45 from IPython.core.inputsplitter import InputSplitter
46 46
47 47 isp = InputSplitter()
48 48 # In practice, this input loop would be wrapped in an outside loop to read
49 49 # input indefinitely, until some exit/quit command was issued. Here we
50 50 # only illustrate the basic inner loop.
51 51 while isp.push_accepts_more():
52 52 indent = ' '*isp.indent_spaces
53 53 prompt = '>>> ' + indent
54 54 line = indent + input_func(prompt)
55 55 isp.push(line)
56 56
57 57 # Here we just return input so we can use it in a test suite, but a real
58 58 # interpreter would instead send it for execution somewhere.
59 59 src = isp.source_reset()
60 60 #print 'Input source was:\n', src # dbg
61 61 return src
62 62
63 63 #-----------------------------------------------------------------------------
64 64 # Test utilities, just for local use
65 65 #-----------------------------------------------------------------------------
66 66
67 67 def assemble(block):
68 68 """Assemble a block into multi-line sub-blocks."""
69 69 return ['\n'.join(sub_block)+'\n' for sub_block in block]
70 70
71 71
72 72 def pseudo_input(lines):
73 73 """Return a function that acts like raw_input but feeds the input list."""
74 74 ilines = iter(lines)
75 75 def raw_in(prompt):
76 76 try:
77 77 return next(ilines)
78 78 except StopIteration:
79 79 return ''
80 80 return raw_in
81 81
82 82 #-----------------------------------------------------------------------------
83 83 # Tests
84 84 #-----------------------------------------------------------------------------
85 85 def test_spaces():
86 86 tests = [('', 0),
87 87 (' ', 1),
88 88 ('\n', 0),
89 89 (' \n', 1),
90 90 ('x', 0),
91 91 (' x', 1),
92 92 (' x',2),
93 93 (' x',4),
94 94 # Note: tabs are counted as a single whitespace!
95 95 ('\tx', 1),
96 96 ('\t x', 2),
97 97 ]
98 98 tt.check_pairs(isp.num_ini_spaces, tests)
99 99
100 100
101 101 def test_remove_comments():
102 102 tests = [('text', 'text'),
103 103 ('text # comment', 'text '),
104 104 ('text # comment\n', 'text \n'),
105 105 ('text # comment \n', 'text \n'),
106 106 ('line # c \nline\n','line \nline\n'),
107 107 ('line # c \nline#c2 \nline\nline #c\n\n',
108 108 'line \nline\nline\nline \n\n'),
109 109 ]
110 110 tt.check_pairs(isp.remove_comments, tests)
111 111
112 112
113 113 def test_get_input_encoding():
114 114 encoding = isp.get_input_encoding()
115 115 nt.assert_true(isinstance(encoding, basestring))
116 116 # simple-minded check that at least encoding a simple string works with the
117 117 # encoding we got.
118 118 nt.assert_equal(u'test'.encode(encoding), b'test')
119 119
120 120
121 121 class NoInputEncodingTestCase(unittest.TestCase):
122 122 def setUp(self):
123 123 self.old_stdin = sys.stdin
124 124 class X: pass
125 125 fake_stdin = X()
126 126 sys.stdin = fake_stdin
127 127
128 128 def test(self):
129 129 # Verify that if sys.stdin has no 'encoding' attribute we do the right
130 130 # thing
131 131 enc = isp.get_input_encoding()
132 132 self.assertEqual(enc, 'ascii')
133 133
134 134 def tearDown(self):
135 135 sys.stdin = self.old_stdin
136 136
137 137
138 138 class InputSplitterTestCase(unittest.TestCase):
139 139 def setUp(self):
140 140 self.isp = isp.InputSplitter()
141 141
142 142 def test_reset(self):
143 143 isp = self.isp
144 144 isp.push('x=1')
145 145 isp.reset()
146 146 self.assertEqual(isp._buffer, [])
147 147 self.assertEqual(isp.indent_spaces, 0)
148 148 self.assertEqual(isp.source, '')
149 149 self.assertEqual(isp.code, None)
150 150 self.assertEqual(isp._is_complete, False)
151 151
152 152 def test_source(self):
153 153 self.isp._store('1')
154 154 self.isp._store('2')
155 155 self.assertEqual(self.isp.source, '1\n2\n')
156 156 self.assertTrue(len(self.isp._buffer)>0)
157 157 self.assertEqual(self.isp.source_reset(), '1\n2\n')
158 158 self.assertEqual(self.isp._buffer, [])
159 159 self.assertEqual(self.isp.source, '')
160 160
161 161 def test_indent(self):
162 162 isp = self.isp # shorthand
163 163 isp.push('x=1')
164 164 self.assertEqual(isp.indent_spaces, 0)
165 165 isp.push('if 1:\n x=1')
166 166 self.assertEqual(isp.indent_spaces, 4)
167 167 isp.push('y=2\n')
168 168 self.assertEqual(isp.indent_spaces, 0)
169 169
170 170 def test_indent2(self):
171 171 # In cell mode, inputs must be fed in whole blocks, so skip this test
172 172 if self.isp.input_mode == 'cell': return
173 173
174 174 isp = self.isp
175 175 isp.push('if 1:')
176 176 self.assertEqual(isp.indent_spaces, 4)
177 177 isp.push(' x=1')
178 178 self.assertEqual(isp.indent_spaces, 4)
179 179 # Blank lines shouldn't change the indent level
180 180 isp.push(' '*2)
181 181 self.assertEqual(isp.indent_spaces, 4)
182 182
183 183 def test_indent3(self):
184 184 # In cell mode, inputs must be fed in whole blocks, so skip this test
185 185 if self.isp.input_mode == 'cell': return
186 186
187 187 isp = self.isp
188 188 # When a multiline statement contains parens or multiline strings, we
189 189 # shouldn't get confused.
190 190 isp.push("if 1:")
191 191 isp.push(" x = (1+\n 2)")
192 192 self.assertEqual(isp.indent_spaces, 4)
193 193
194 194 def test_indent4(self):
195 195 # In cell mode, inputs must be fed in whole blocks, so skip this test
196 196 if self.isp.input_mode == 'cell': return
197 197
198 198 isp = self.isp
199 199 # whitespace after ':' should not screw up indent level
200 200 isp.push('if 1: \n x=1')
201 201 self.assertEqual(isp.indent_spaces, 4)
202 202 isp.push('y=2\n')
203 203 self.assertEqual(isp.indent_spaces, 0)
204 204 isp.push('if 1:\t\n x=1')
205 205 self.assertEqual(isp.indent_spaces, 4)
206 206 isp.push('y=2\n')
207 207 self.assertEqual(isp.indent_spaces, 0)
208 208
209 209 def test_dedent_pass(self):
210 210 isp = self.isp # shorthand
211 211 # should NOT cause dedent
212 212 isp.push('if 1:\n passes = 5')
213 213 self.assertEqual(isp.indent_spaces, 4)
214 214 isp.push('if 1:\n pass')
215 215 self.assertEqual(isp.indent_spaces, 0)
216 216 isp.push('if 1:\n pass ')
217 217 self.assertEqual(isp.indent_spaces, 0)
218 218
219 219 def test_dedent_break(self):
220 220 isp = self.isp # shorthand
221 221 # should NOT cause dedent
222 222 isp.push('while 1:\n breaks = 5')
223 223 self.assertEqual(isp.indent_spaces, 4)
224 224 isp.push('while 1:\n break')
225 225 self.assertEqual(isp.indent_spaces, 0)
226 226 isp.push('while 1:\n break ')
227 227 self.assertEqual(isp.indent_spaces, 0)
228 228
229 229 def test_dedent_continue(self):
230 230 isp = self.isp # shorthand
231 231 # should NOT cause dedent
232 232 isp.push('while 1:\n continues = 5')
233 233 self.assertEqual(isp.indent_spaces, 4)
234 234 isp.push('while 1:\n continue')
235 235 self.assertEqual(isp.indent_spaces, 0)
236 236 isp.push('while 1:\n continue ')
237 237 self.assertEqual(isp.indent_spaces, 0)
238 238
239 239 def test_dedent_raise(self):
240 240 isp = self.isp # shorthand
241 241 # should NOT cause dedent
242 242 isp.push('if 1:\n raised = 4')
243 243 self.assertEqual(isp.indent_spaces, 4)
244 244 isp.push('if 1:\n raise TypeError()')
245 245 self.assertEqual(isp.indent_spaces, 0)
246 246 isp.push('if 1:\n raise')
247 247 self.assertEqual(isp.indent_spaces, 0)
248 248 isp.push('if 1:\n raise ')
249 249 self.assertEqual(isp.indent_spaces, 0)
250 250
251 251 def test_dedent_return(self):
252 252 isp = self.isp # shorthand
253 253 # should NOT cause dedent
254 254 isp.push('if 1:\n returning = 4')
255 255 self.assertEqual(isp.indent_spaces, 4)
256 256 isp.push('if 1:\n return 5 + 493')
257 257 self.assertEqual(isp.indent_spaces, 0)
258 258 isp.push('if 1:\n return')
259 259 self.assertEqual(isp.indent_spaces, 0)
260 260 isp.push('if 1:\n return ')
261 261 self.assertEqual(isp.indent_spaces, 0)
262 262 isp.push('if 1:\n return(0)')
263 263 self.assertEqual(isp.indent_spaces, 0)
264 264
265 265 def test_push(self):
266 266 isp = self.isp
267 267 self.assertTrue(isp.push('x=1'))
268 268
269 269 def test_push2(self):
270 270 isp = self.isp
271 271 self.assertFalse(isp.push('if 1:'))
272 272 for line in [' x=1', '# a comment', ' y=2']:
273 print(line)
273 274 self.assertTrue(isp.push(line))
274 275
275 276 def test_push3(self):
276 277 isp = self.isp
277 278 isp.push('if True:')
278 279 isp.push(' a = 1')
279 280 self.assertFalse(isp.push('b = [1,'))
280 281
281 282 def test_replace_mode(self):
282 283 isp = self.isp
283 284 isp.input_mode = 'cell'
284 285 isp.push('x=1')
285 286 self.assertEqual(isp.source, 'x=1\n')
286 287 isp.push('x=2')
287 288 self.assertEqual(isp.source, 'x=2\n')
288 289
289 290 def test_push_accepts_more(self):
290 291 isp = self.isp
291 292 isp.push('x=1')
292 293 self.assertFalse(isp.push_accepts_more())
293 294
294 295 def test_push_accepts_more2(self):
295 296 # In cell mode, inputs must be fed in whole blocks, so skip this test
296 297 if self.isp.input_mode == 'cell': return
297 298
298 299 isp = self.isp
299 300 isp.push('if 1:')
300 301 self.assertTrue(isp.push_accepts_more())
301 302 isp.push(' x=1')
302 303 self.assertTrue(isp.push_accepts_more())
303 304 isp.push('')
304 305 self.assertFalse(isp.push_accepts_more())
305 306
306 307 def test_push_accepts_more3(self):
307 308 isp = self.isp
308 309 isp.push("x = (2+\n3)")
309 310 self.assertFalse(isp.push_accepts_more())
310 311
311 312 def test_push_accepts_more4(self):
312 313 # In cell mode, inputs must be fed in whole blocks, so skip this test
313 314 if self.isp.input_mode == 'cell': return
314 315
315 316 isp = self.isp
316 317 # When a multiline statement contains parens or multiline strings, we
317 318 # shouldn't get confused.
318 319 # FIXME: we should be able to better handle de-dents in statements like
319 320 # multiline strings and multiline expressions (continued with \ or
320 321 # parens). Right now we aren't handling the indentation tracking quite
321 322 # correctly with this, though in practice it may not be too much of a
322 323 # problem. We'll need to see.
323 324 isp.push("if 1:")
324 325 isp.push(" x = (2+")
325 326 isp.push(" 3)")
326 327 self.assertTrue(isp.push_accepts_more())
327 328 isp.push(" y = 3")
328 329 self.assertTrue(isp.push_accepts_more())
329 330 isp.push('')
330 331 self.assertFalse(isp.push_accepts_more())
331 332
332 333 def test_push_accepts_more5(self):
333 334 # In cell mode, inputs must be fed in whole blocks, so skip this test
334 335 if self.isp.input_mode == 'cell': return
335 336
336 337 isp = self.isp
337 338 isp.push('try:')
338 339 isp.push(' a = 5')
339 340 isp.push('except:')
340 341 isp.push(' raise')
341 342 self.assertTrue(isp.push_accepts_more())
342 343
343 344 def test_continuation(self):
344 345 isp = self.isp
345 346 isp.push("import os, \\")
346 347 self.assertTrue(isp.push_accepts_more())
347 348 isp.push("sys")
348 349 self.assertFalse(isp.push_accepts_more())
349 350
350 351 def test_syntax_error(self):
351 352 isp = self.isp
352 353 # Syntax errors immediately produce a 'ready' block, so the invalid
353 354 # Python can be sent to the kernel for evaluation with possible ipython
354 355 # special-syntax conversion.
355 356 isp.push('run foo')
356 357 self.assertFalse(isp.push_accepts_more())
357 358
358 359 def test_unicode(self):
359 360 self.isp.push(u"PΓ©rez")
360 361 self.isp.push(u'\xc3\xa9')
361 362 self.isp.push(u"u'\xc3\xa9'")
362 363
363 364 def test_line_continuation(self):
364 365 """ Test issue #2108."""
365 366 isp = self.isp
366 367 # A blank line after a line continuation should not accept more
367 368 isp.push("1 \\\n\n")
368 369 self.assertFalse(isp.push_accepts_more())
369 370 # Whitespace after a \ is a SyntaxError. The only way to test that
370 371 # here is to test that push doesn't accept more (as with
371 372 # test_syntax_error() above).
372 373 isp.push(r"1 \ ")
373 374 self.assertFalse(isp.push_accepts_more())
374 375 # Even if the line is continuable (c.f. the regular Python
375 376 # interpreter)
376 377 isp.push(r"(1 \ ")
377 378 self.assertFalse(isp.push_accepts_more())
378 379
379 380 class InteractiveLoopTestCase(unittest.TestCase):
380 381 """Tests for an interactive loop like a python shell.
381 382 """
382 383 def check_ns(self, lines, ns):
383 384 """Validate that the given input lines produce the resulting namespace.
384 385
385 386 Note: the input lines are given exactly as they would be typed in an
386 387 auto-indenting environment, as mini_interactive_loop above already does
387 388 auto-indenting and prepends spaces to the input.
388 389 """
389 390 src = mini_interactive_loop(pseudo_input(lines))
390 391 test_ns = {}
391 392 exec src in test_ns
392 393 # We can't check that the provided ns is identical to the test_ns,
393 394 # because Python fills test_ns with extra keys (copyright, etc). But
394 395 # we can check that the given dict is *contained* in test_ns
395 396 for k,v in ns.iteritems():
396 397 self.assertEqual(test_ns[k], v)
397 398
398 399 def test_simple(self):
399 400 self.check_ns(['x=1'], dict(x=1))
400 401
401 402 def test_simple2(self):
402 403 self.check_ns(['if 1:', 'x=2'], dict(x=2))
403 404
404 405 def test_xy(self):
405 406 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
406 407
407 408 def test_abc(self):
408 409 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
409 410
410 411 def test_multi(self):
411 412 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
412 413
413 414
414 415 def test_LineInfo():
415 416 """Simple test for LineInfo construction and str()"""
416 417 linfo = isp.LineInfo(' %cd /home')
417 418 nt.assert_equal(str(linfo), 'LineInfo [ |%|cd|/home]')
418 419
419 420
420 421
421 422
422 423 class IPythonInputTestCase(InputSplitterTestCase):
423 424 """By just creating a new class whose .isp is a different instance, we
424 425 re-run the same test battery on the new input splitter.
425 426
426 427 In addition, this runs the tests over the syntax and syntax_ml dicts that
427 428 were tested by individual functions, as part of the OO interface.
428 429
429 430 It also makes some checks on the raw buffer storage.
430 431 """
431 432
432 433 def setUp(self):
433 434 self.isp = isp.IPythonInputSplitter(input_mode='line')
434 435
435 436 def test_syntax(self):
436 437 """Call all single-line syntax tests from the main object"""
437 438 isp = self.isp
438 439 for example in syntax.itervalues():
439 440 for raw, out_t in example:
440 441 if raw.startswith(' '):
441 442 continue
442 443
443 444 isp.push(raw+'\n')
444 445 out, out_raw = isp.source_raw_reset()
445 446 self.assertEqual(out.rstrip(), out_t,
446 447 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
447 448 self.assertEqual(out_raw.rstrip(), raw.rstrip())
448 449
449 450 def test_syntax_multiline(self):
450 451 isp = self.isp
451 452 for example in syntax_ml.itervalues():
452 453 for line_pairs in example:
453 454 out_t_parts = []
454 455 raw_parts = []
455 456 for lraw, out_t_part in line_pairs:
456 457 if out_t_part is not None:
457 458 out_t_parts.append(out_t_part)
458 459
459 460 if lraw is not None:
460 461 isp.push(lraw)
461 462 raw_parts.append(lraw)
462 463
463 464 out, out_raw = isp.source_raw_reset()
464 465 out_t = '\n'.join(out_t_parts).rstrip()
465 466 raw = '\n'.join(raw_parts).rstrip()
466 467 self.assertEqual(out.rstrip(), out_t)
467 468 self.assertEqual(out_raw.rstrip(), raw)
468 469
469 470
470 471 class BlockIPythonInputTestCase(IPythonInputTestCase):
471 472
472 473 # Deactivate tests that don't make sense for the block mode
473 474 test_push3 = test_split = lambda s: None
474 475
475 476 def setUp(self):
476 477 self.isp = isp.IPythonInputSplitter(input_mode='cell')
477 478
478 479 def test_syntax_multiline(self):
479 480 isp = self.isp
480 481 for example in syntax_ml.itervalues():
481 482 raw_parts = []
482 483 out_t_parts = []
483 484 for line_pairs in example:
484 485 raw_parts, out_t_parts = zip(*line_pairs)
485 486
486 487 raw = '\n'.join(r for r in raw_parts if r is not None)
487 488 out_t = '\n'.join(o for o in out_t_parts if o is not None)
488 489
489 490 isp.push(raw)
490 491 out, out_raw = isp.source_raw_reset()
491 492 # Match ignoring trailing whitespace
492 493 self.assertEqual(out.rstrip(), out_t.rstrip())
493 494 self.assertEqual(out_raw.rstrip(), raw.rstrip())
494 495
495 496 def test_syntax_multiline_cell(self):
496 497 isp = self.isp
497 498 for example in syntax_ml.itervalues():
498 499
499 500 out_t_parts = []
500 501 for line_pairs in example:
501 502 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
502 503 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
503 504 out = isp.transform_cell(raw)
504 505 # Match ignoring trailing whitespace
505 506 self.assertEqual(out.rstrip(), out_t.rstrip())
506 507
507 508 #-----------------------------------------------------------------------------
508 509 # Main - use as a script, mostly for developer experiments
509 510 #-----------------------------------------------------------------------------
510 511
511 512 if __name__ == '__main__':
512 513 # A simple demo for interactive experimentation. This code will not get
513 514 # picked up by any test suite.
514 515 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
515 516
516 517 # configure here the syntax to use, prompt and whether to autoindent
517 518 #isp, start_prompt = InputSplitter(), '>>> '
518 519 isp, start_prompt = IPythonInputSplitter(), 'In> '
519 520
520 521 autoindent = True
521 522 #autoindent = False
522 523
523 524 try:
524 525 while True:
525 526 prompt = start_prompt
526 527 while isp.push_accepts_more():
527 528 indent = ' '*isp.indent_spaces
528 529 if autoindent:
529 530 line = indent + raw_input(prompt+indent)
530 531 else:
531 532 line = raw_input(prompt)
532 533 isp.push(line)
533 534 prompt = '... '
534 535
535 536 # Here we just return input so we can use it in a test suite, but a
536 537 # real interpreter would instead send it for execution somewhere.
537 538 #src = isp.source; raise EOFError # dbg
538 539 src, raw = isp.source_raw_reset()
539 540 print 'Input source was:\n', src
540 541 print 'Raw source was:\n', raw
541 542 except EOFError:
542 543 print 'Bye'
543 544
544 545 # Tests for cell magics support
545 546
546 547 def test_last_blank():
547 548 nt.assert_false(isp.last_blank(''))
548 549 nt.assert_false(isp.last_blank('abc'))
549 550 nt.assert_false(isp.last_blank('abc\n'))
550 551 nt.assert_false(isp.last_blank('abc\na'))
551 552
552 553 nt.assert_true(isp.last_blank('\n'))
553 554 nt.assert_true(isp.last_blank('\n '))
554 555 nt.assert_true(isp.last_blank('abc\n '))
555 556 nt.assert_true(isp.last_blank('abc\n\n'))
556 557 nt.assert_true(isp.last_blank('abc\nd\n\n'))
557 558 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
558 559 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
559 560
560 561
561 562 def test_last_two_blanks():
562 563 nt.assert_false(isp.last_two_blanks(''))
563 564 nt.assert_false(isp.last_two_blanks('abc'))
564 565 nt.assert_false(isp.last_two_blanks('abc\n'))
565 566 nt.assert_false(isp.last_two_blanks('abc\n\na'))
566 567 nt.assert_false(isp.last_two_blanks('abc\n \n'))
567 568 nt.assert_false(isp.last_two_blanks('abc\n\n'))
568 569
569 570 nt.assert_true(isp.last_two_blanks('\n\n'))
570 571 nt.assert_true(isp.last_two_blanks('\n\n '))
571 572 nt.assert_true(isp.last_two_blanks('\n \n'))
572 573 nt.assert_true(isp.last_two_blanks('abc\n\n '))
573 574 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
574 575 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
575 576 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
576 577 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
577 578 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
578 579 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
579 580
580 581
581 582 class CellMagicsCommon(object):
582 583
583 584 def test_whole_cell(self):
584 585 src = "%%cellm line\nbody\n"
585 586 sp = self.sp
586 587 sp.push(src)
587 588 out = sp.source_reset()
588 589 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
589 590 nt.assert_equal(out, py3compat.u_format(ref))
590 591
591 592 def tearDown(self):
592 593 self.sp.reset()
593 594
594 595
595 596 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
596 597 sp = isp.IPythonInputSplitter(input_mode='cell')
597 598
598 599 def test_incremental(self):
599 600 sp = self.sp
600 601 src = '%%cellm line2\n'
601 602 sp.push(src)
602 603 nt.assert_true(sp.push_accepts_more()) #1
603 604 src += '\n'
604 605 sp.push(src)
605 606 # Note: if we ever change the logic to allow full blank lines (see
606 607 # _handle_cell_magic), then the following test should change to true
607 608 nt.assert_false(sp.push_accepts_more()) #2
608 609 # By now, even with full blanks allowed, a second blank should signal
609 610 # the end. For now this test is only a redundancy safety, but don't
610 611 # delete it in case we change our mind and the previous one goes to
611 612 # true.
612 613 src += '\n'
613 614 sp.push(src)
614 615 nt.assert_false(sp.push_accepts_more()) #3
615 616
616 617
617 618 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
618 619 sp = isp.IPythonInputSplitter(input_mode='line')
619 620
620 621 def test_incremental(self):
621 622 sp = self.sp
622 623 sp.push('%%cellm line2\n')
623 624 nt.assert_true(sp.push_accepts_more()) #1
624 625 sp.push('\n')
625 626 nt.assert_false(sp.push_accepts_more()) #2
@@ -1,368 +1,368 b''
1 1 import tokenize
2 2 import unittest
3 3 import nose.tools as nt
4 4
5 5 from IPython.testing import tools as tt
6 6 from IPython.utils import py3compat
7 7 u_fmt = py3compat.u_format
8 8
9 9 from IPython.core import inputtransformer as ipt
10 10
11 11 def transform_and_reset(transformer):
12 12 transformer = transformer()
13 13 def transform(inp):
14 14 try:
15 15 return transformer.push(inp)
16 16 finally:
17 17 transformer.reset()
18 18
19 19 return transform
20 20
21 21 # Transformer tests
22 22 def transform_checker(tests, transformer):
23 23 """Utility to loop over test inputs"""
24 24 transformer = transformer()
25 25 try:
26 26 for inp, tr in tests:
27 27 if inp is None:
28 28 out = transformer.reset()
29 29 else:
30 30 out = transformer.push(inp)
31 31 nt.assert_equal(out, tr)
32 32 finally:
33 33 transformer.reset()
34 34
35 35 # Data for all the syntax tests in the form of lists of pairs of
36 36 # raw/transformed input. We store it here as a global dict so that we can use
37 37 # it both within single-function tests and also to validate the behavior of the
38 38 # larger objects
39 39
40 40 syntax = \
41 41 dict(assign_system =
42 42 [(i,py3compat.u_format(o)) for i,o in \
43 43 [(u'a =! ls', "a = get_ipython().getoutput({u}'ls')"),
44 44 (u'b = !ls', "b = get_ipython().getoutput({u}'ls')"),
45 45 ('x=1', 'x=1'), # normal input is unmodified
46 46 (' ',' '), # blank lines are kept intact
47 47 ]],
48 48
49 49 assign_magic =
50 50 [(i,py3compat.u_format(o)) for i,o in \
51 51 [(u'a =% who', "a = get_ipython().magic({u}'who')"),
52 52 (u'b = %who', "b = get_ipython().magic({u}'who')"),
53 53 ('x=1', 'x=1'), # normal input is unmodified
54 54 (' ',' '), # blank lines are kept intact
55 55 ]],
56 56
57 57 classic_prompt =
58 58 [('>>> x=1', 'x=1'),
59 59 ('x=1', 'x=1'), # normal input is unmodified
60 60 (' ', ' '), # blank lines are kept intact
61 61 ],
62 62
63 63 ipy_prompt =
64 64 [('In [1]: x=1', 'x=1'),
65 65 ('x=1', 'x=1'), # normal input is unmodified
66 66 (' ',' '), # blank lines are kept intact
67 67 ],
68 68
69 69 # Tests for the escape transformer to leave normal code alone
70 70 escaped_noesc =
71 71 [ (' ', ' '),
72 72 ('x=1', 'x=1'),
73 73 ],
74 74
75 75 # System calls
76 76 escaped_shell =
77 77 [(i,py3compat.u_format(o)) for i,o in \
78 78 [ (u'!ls', "get_ipython().system({u}'ls')"),
79 79 # Double-escape shell, this means to capture the output of the
80 80 # subprocess and return it
81 81 (u'!!ls', "get_ipython().getoutput({u}'ls')"),
82 82 ]],
83 83
84 84 # Help/object info
85 85 escaped_help =
86 86 [(i,py3compat.u_format(o)) for i,o in \
87 87 [ (u'?', 'get_ipython().show_usage()'),
88 88 (u'?x1', "get_ipython().magic({u}'pinfo x1')"),
89 89 (u'??x2', "get_ipython().magic({u}'pinfo2 x2')"),
90 90 (u'?a.*s', "get_ipython().magic({u}'psearch a.*s')"),
91 91 (u'?%hist1', "get_ipython().magic({u}'pinfo %hist1')"),
92 92 (u'?%%hist2', "get_ipython().magic({u}'pinfo %%hist2')"),
93 93 (u'?abc = qwe', "get_ipython().magic({u}'pinfo abc')"),
94 94 ]],
95 95
96 96 end_help =
97 97 [(i,py3compat.u_format(o)) for i,o in \
98 98 [ (u'x3?', "get_ipython().magic({u}'pinfo x3')"),
99 99 (u'x4??', "get_ipython().magic({u}'pinfo2 x4')"),
100 100 (u'%hist1?', "get_ipython().magic({u}'pinfo %hist1')"),
101 101 (u'%hist2??', "get_ipython().magic({u}'pinfo2 %hist2')"),
102 102 (u'%%hist3?', "get_ipython().magic({u}'pinfo %%hist3')"),
103 103 (u'%%hist4??', "get_ipython().magic({u}'pinfo2 %%hist4')"),
104 104 (u'f*?', "get_ipython().magic({u}'psearch f*')"),
105 105 (u'ax.*aspe*?', "get_ipython().magic({u}'psearch ax.*aspe*')"),
106 106 (u'a = abc?', "get_ipython().set_next_input({u}'a = abc');"
107 107 "get_ipython().magic({u}'pinfo abc')"),
108 108 (u'a = abc.qe??', "get_ipython().set_next_input({u}'a = abc.qe');"
109 109 "get_ipython().magic({u}'pinfo2 abc.qe')"),
110 110 (u'a = *.items?', "get_ipython().set_next_input({u}'a = *.items');"
111 111 "get_ipython().magic({u}'psearch *.items')"),
112 112 (u'plot(a?', "get_ipython().set_next_input({u}'plot(a');"
113 113 "get_ipython().magic({u}'pinfo a')"),
114 114 (u'a*2 #comment?', 'a*2 #comment?'),
115 115 ]],
116 116
117 117 # Explicit magic calls
118 118 escaped_magic =
119 119 [(i,py3compat.u_format(o)) for i,o in \
120 120 [ (u'%cd', "get_ipython().magic({u}'cd')"),
121 121 (u'%cd /home', "get_ipython().magic({u}'cd /home')"),
122 122 # Backslashes need to be escaped.
123 123 (u'%cd C:\\User', "get_ipython().magic({u}'cd C:\\\\User')"),
124 124 (u' %magic', " get_ipython().magic({u}'magic')"),
125 125 ]],
126 126
127 127 # Quoting with separate arguments
128 128 escaped_quote =
129 129 [ (',f', 'f("")'),
130 130 (',f x', 'f("x")'),
131 131 (' ,f y', ' f("y")'),
132 132 (',f a b', 'f("a", "b")'),
133 133 ],
134 134
135 135 # Quoting with single argument
136 136 escaped_quote2 =
137 137 [ (';f', 'f("")'),
138 138 (';f x', 'f("x")'),
139 139 (' ;f y', ' f("y")'),
140 140 (';f a b', 'f("a b")'),
141 141 ],
142 142
143 143 # Simply apply parens
144 144 escaped_paren =
145 145 [ ('/f', 'f()'),
146 146 ('/f x', 'f(x)'),
147 147 (' /f y', ' f(y)'),
148 148 ('/f a b', 'f(a, b)'),
149 149 ],
150 150
151 151 # Check that we transform prompts before other transforms
152 152 mixed =
153 153 [(i,py3compat.u_format(o)) for i,o in \
154 154 [ (u'In [1]: %lsmagic', "get_ipython().magic({u}'lsmagic')"),
155 155 (u'>>> %lsmagic', "get_ipython().magic({u}'lsmagic')"),
156 156 (u'In [2]: !ls', "get_ipython().system({u}'ls')"),
157 157 (u'In [3]: abs?', "get_ipython().magic({u}'pinfo abs')"),
158 158 (u'In [4]: b = %who', "b = get_ipython().magic({u}'who')"),
159 159 ]],
160 160 )
161 161
162 162 # multiline syntax examples. Each of these should be a list of lists, with
163 163 # each entry itself having pairs of raw/transformed input. The union (with
164 164 # '\n'.join() of the transformed inputs is what the splitter should produce
165 165 # when fed the raw lines one at a time via push.
166 166 syntax_ml = \
167 167 dict(classic_prompt =
168 168 [ [('>>> for i in range(10):','for i in range(10):'),
169 169 ('... print i',' print i'),
170 170 ('... ', ''),
171 171 ],
172 172 [('>>> a="""','a="""'),
173 173 ('... 123"""','123"""'),
174 174 ],
175 175 [('a="""','a="""'),
176 176 ('... 123"""','... 123"""'),
177 177 ],
178 178 ],
179 179
180 180 ipy_prompt =
181 181 [ [('In [24]: for i in range(10):','for i in range(10):'),
182 182 (' ....: print i',' print i'),
183 183 (' ....: ', ''),
184 184 ],
185 185 [('In [2]: a="""','a="""'),
186 186 (' ...: 123"""','123"""'),
187 187 ],
188 188 [('a="""','a="""'),
189 189 (' ...: 123"""',' ...: 123"""'),
190 190 ],
191 191 ],
192 192
193 193 multiline_datastructure =
194 194 [ [('>>> a = [1,','a = [1,'),
195 195 ('... 2]','2]'),
196 196 ],
197 197 ],
198 198
199 199 leading_indent =
200 200 [ [(' print "hi"','print "hi"'),
201 201 ],
202 202 [(' for a in range(5):','for a in range(5):'),
203 203 (' a*2',' a*2'),
204 204 ],
205 205 [(' a="""','a="""'),
206 206 (' 123"""','123"""'),
207 207 ],
208 208 [('a="""','a="""'),
209 209 (' 123"""',' 123"""'),
210 210 ],
211 211 ],
212 212
213 213 cellmagic =
214 214 [ [(u'%%foo a', None),
215 215 (None, u_fmt("get_ipython().run_cell_magic({u}'foo', {u}'a', {u}'')")),
216 216 ],
217 217 [(u'%%bar 123', None),
218 218 (u'hello', None),
219 219 (u'', u_fmt("get_ipython().run_cell_magic({u}'bar', {u}'123', {u}'hello')")),
220 220 ],
221 221 ],
222 222
223 223 escaped =
224 224 [ [('%abc def \\', None),
225 225 ('ghi', u_fmt("get_ipython().magic({u}'abc def ghi')")),
226 226 ],
227 227 [('%abc def \\', None),
228 228 ('ghi\\', None),
229 229 (None, u_fmt("get_ipython().magic({u}'abc def ghi')")),
230 230 ],
231 231 ],
232 232
233 233 assign_magic =
234 234 [ [(u'a = %bc de \\', None),
235 235 (u'fg', u_fmt("a = get_ipython().magic({u}'bc de fg')")),
236 236 ],
237 237 [(u'a = %bc de \\', None),
238 238 (u'fg\\', None),
239 239 (None, u_fmt("a = get_ipython().magic({u}'bc de fg')")),
240 240 ],
241 241 ],
242 242
243 243 assign_system =
244 244 [ [(u'a = !bc de \\', None),
245 245 (u'fg', u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
246 246 ],
247 247 [(u'a = !bc de \\', None),
248 248 (u'fg\\', None),
249 249 (None, u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
250 250 ],
251 251 ],
252 252 )
253 253
254 254
255 255 def test_assign_system():
256 256 tt.check_pairs(transform_and_reset(ipt.assign_from_system), syntax['assign_system'])
257 257 for example in syntax_ml['assign_system']:
258 258 transform_checker(example, ipt.assign_from_system)
259 259
260 260 def test_assign_magic():
261 261 tt.check_pairs(transform_and_reset(ipt.assign_from_magic), syntax['assign_magic'])
262 262 for example in syntax_ml['assign_magic']:
263 263 transform_checker(example, ipt.assign_from_magic)
264 264
265 265
266 266 def test_classic_prompt():
267 267 tt.check_pairs(transform_and_reset(ipt.classic_prompt), syntax['classic_prompt'])
268 268 for example in syntax_ml['classic_prompt']:
269 269 transform_checker(example, ipt.classic_prompt)
270 270 for example in syntax_ml['multiline_datastructure']:
271 271 transform_checker(example, ipt.classic_prompt)
272 272
273 273
274 274 def test_ipy_prompt():
275 275 tt.check_pairs(transform_and_reset(ipt.ipy_prompt), syntax['ipy_prompt'])
276 276 for example in syntax_ml['ipy_prompt']:
277 277 transform_checker(example, ipt.ipy_prompt)
278 278
279 279 def test_help_end():
280 280 tt.check_pairs(transform_and_reset(ipt.help_end), syntax['end_help'])
281 281
282 282 def test_escaped_noesc():
283 283 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_noesc'])
284 284
285 285
286 286 def test_escaped_shell():
287 287 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_shell'])
288 288
289 289
290 290 def test_escaped_help():
291 291 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_help'])
292 292
293 293
294 294 def test_escaped_magic():
295 295 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_magic'])
296 296
297 297
298 298 def test_escaped_quote():
299 299 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_quote'])
300 300
301 301
302 302 def test_escaped_quote2():
303 303 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_quote2'])
304 304
305 305
306 306 def test_escaped_paren():
307 307 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_paren'])
308 308
309 309 def test_escaped_multiline():
310 310 for example in syntax_ml['escaped']:
311 311 transform_checker(example, ipt.escaped_transformer)
312 312
313 313 def test_cellmagic():
314 314 for example in syntax_ml['cellmagic']:
315 315 transform_checker(example, ipt.cellmagic)
316 316
317 317 def test_has_comment():
318 318 tests = [('text', False),
319 319 ('text #comment', True),
320 320 ('text #comment\n', True),
321 321 ('#comment', True),
322 322 ('#comment\n', True),
323 323 ('a = "#string"', False),
324 324 ('a = "#string" # comment', True),
325 325 ('a #comment not "string"', True),
326 326 ]
327 327 tt.check_pairs(ipt.has_comment, tests)
328 328
329 329 @ipt.TokenInputTransformer.wrap
330 330 def decistmt(tokens):
331 331 """Substitute Decimals for floats in a string of statements.
332 332
333 333 Based on an example from the tokenize module docs.
334 334 """
335 335 result = []
336 336 for toknum, tokval, _, _, _ in tokens:
337 337 if toknum == tokenize.NUMBER and '.' in tokval: # replace NUMBER tokens
338 338 for newtok in [
339 339 (tokenize.NAME, 'Decimal'),
340 340 (tokenize.OP, '('),
341 341 (tokenize.STRING, repr(tokval)),
342 342 (tokenize.OP, ')')
343 343 ]:
344 344 yield newtok
345 345 else:
346 346 yield (toknum, tokval)
347 347
348 348
349 349
350 350 def test_token_input_transformer():
351 351 tests = [(u'1.2', u_fmt(u"Decimal ({u}'1.2')")),
352 352 (u'"1.2"', u'"1.2"'),
353 353 ]
354 354 tt.check_pairs(transform_and_reset(decistmt), tests)
355 355 ml_tests = \
356 356 [ [(u"a = 1.2; b = '''x", None),
357 357 (u"y'''", u_fmt(u"a =Decimal ({u}'1.2');b ='''x\ny'''")),
358 358 ],
359 [(u"a = [1.2,", u_fmt(u"a =[Decimal ({u}'1.2'),")),
360 (u"3]", u"3 ]"),
359 [(u"a = [1.2,", None),
360 (u"3]", u_fmt(u"a =[Decimal ({u}'1.2'),\n3 ]")),
361 361 ],
362 362 [(u"a = '''foo", None), # Test resetting when within a multi-line string
363 363 (u"bar", None),
364 364 (None, u"a = '''foo\nbar"),
365 365 ],
366 366 ]
367 367 for example in ml_tests:
368 368 transform_checker(example, decistmt)
General Comments 0
You need to be logged in to leave comments. Login now