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