##// END OF EJS Templates
Fix for \ at end of comment, and add tests
Thomas Kluyver -
Show More
@@ -1,712 +1,713 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 # Input mode
251 251 input_mode = 'line'
252 252
253 253 # Private attributes
254 254
255 255 # List with lines of input accumulated so far
256 256 _buffer = None
257 257 # Command compiler
258 258 _compile = None
259 259 # Mark when input has changed indentation all the way back to flush-left
260 260 _full_dedent = False
261 261 # Boolean indicating whether the current block is complete
262 262 _is_complete = None
263 263
264 264 def __init__(self, input_mode=None):
265 265 """Create a new InputSplitter instance.
266 266
267 267 Parameters
268 268 ----------
269 269 input_mode : str
270 270
271 271 One of ['line', 'cell']; default is 'line'.
272 272
273 273 The input_mode parameter controls how new inputs are used when fed via
274 274 the :meth:`push` method:
275 275
276 276 - 'line': meant for line-oriented clients, inputs are appended one at a
277 277 time to the internal buffer and the whole buffer is compiled.
278 278
279 279 - 'cell': meant for clients that can edit multi-line 'cells' of text at
280 280 a time. A cell can contain one or more blocks that can be compile in
281 281 'single' mode by Python. In this mode, each new input new input
282 282 completely replaces all prior inputs. Cell mode is thus equivalent
283 283 to prepending a full reset() to every push() call.
284 284 """
285 285 self._buffer = []
286 286 self._compile = codeop.CommandCompiler()
287 287 self.encoding = get_input_encoding()
288 288 self.input_mode = InputSplitter.input_mode if input_mode is None \
289 289 else input_mode
290 290
291 291 def reset(self):
292 292 """Reset the input buffer and associated state."""
293 293 self.indent_spaces = 0
294 294 self._buffer[:] = []
295 295 self.source = ''
296 296 self.code = None
297 297 self._is_complete = False
298 298 self._full_dedent = False
299 299
300 300 def source_reset(self):
301 301 """Return the input source and perform a full reset.
302 302 """
303 303 out = self.source
304 304 self.reset()
305 305 return out
306 306
307 307 def push(self, lines):
308 308 """Push one or more lines of input.
309 309
310 310 This stores the given lines and returns a status code indicating
311 311 whether the code forms a complete Python block or not.
312 312
313 313 Any exceptions generated in compilation are swallowed, but if an
314 314 exception was produced, the method returns True.
315 315
316 316 Parameters
317 317 ----------
318 318 lines : string
319 319 One or more lines of Python input.
320 320
321 321 Returns
322 322 -------
323 323 is_complete : boolean
324 324 True if the current input source (the result of the current input
325 325 plus prior inputs) forms a complete Python execution block. Note that
326 326 this value is also stored as a private attribute (``_is_complete``), so it
327 327 can be queried at any time.
328 328 """
329 329 if self.input_mode == 'cell':
330 330 self.reset()
331 331
332 332 self._store(lines)
333 333 source = self.source
334 334
335 335 # Before calling _compile(), reset the code object to None so that if an
336 336 # exception is raised in compilation, we don't mislead by having
337 337 # inconsistent code/source attributes.
338 338 self.code, self._is_complete = None, None
339 339
340 340 # Honor termination lines properly
341 341 if source.endswith('\\\n'):
342 342 return False
343 343
344 344 self._update_indent(lines)
345 345 try:
346 346 self.code = self._compile(source, symbol="exec")
347 347 # Invalid syntax can produce any of a number of different errors from
348 348 # inside the compiler, so we have to catch them all. Syntax errors
349 349 # immediately produce a 'ready' block, so the invalid Python can be
350 350 # sent to the kernel for evaluation with possible ipython
351 351 # special-syntax conversion.
352 352 except (SyntaxError, OverflowError, ValueError, TypeError,
353 353 MemoryError):
354 354 self._is_complete = True
355 355 else:
356 356 # Compilation didn't produce any exceptions (though it may not have
357 357 # given a complete code object)
358 358 self._is_complete = self.code is not None
359 359
360 360 return self._is_complete
361 361
362 362 def push_accepts_more(self):
363 363 """Return whether a block of interactive input can accept more input.
364 364
365 365 This method is meant to be used by line-oriented frontends, who need to
366 366 guess whether a block is complete or not based solely on prior and
367 367 current input lines. The InputSplitter considers it has a complete
368 368 interactive block and will not accept more input only when either a
369 369 SyntaxError is raised, or *all* of the following are true:
370 370
371 371 1. The input compiles to a complete statement.
372 372
373 373 2. The indentation level is flush-left (because if we are indented,
374 374 like inside a function definition or for loop, we need to keep
375 375 reading new input).
376 376
377 377 3. There is one extra line consisting only of whitespace.
378 378
379 379 Because of condition #3, this method should be used only by
380 380 *line-oriented* frontends, since it means that intermediate blank lines
381 381 are not allowed in function definitions (or any other indented block).
382 382
383 383 If the current input produces a syntax error, this method immediately
384 384 returns False but does *not* raise the syntax error exception, as
385 385 typically clients will want to send invalid syntax to an execution
386 386 backend which might convert the invalid syntax into valid Python via
387 387 one of the dynamic IPython mechanisms.
388 388 """
389 389
390 390 # With incomplete input, unconditionally accept more
391 391 if not self._is_complete:
392 392 return True
393 393
394 394 # If we already have complete input and we're flush left, the answer
395 395 # depends. In line mode, if there hasn't been any indentation,
396 396 # that's it. If we've come back from some indentation, we need
397 397 # the blank final line to finish.
398 398 # In cell mode, we need to check how many blocks the input so far
399 399 # compiles into, because if there's already more than one full
400 400 # independent block of input, then the client has entered full
401 401 # 'cell' mode and is feeding lines that each is complete. In this
402 402 # case we should then keep accepting. The Qt terminal-like console
403 403 # does precisely this, to provide the convenience of terminal-like
404 404 # input of single expressions, but allowing the user (with a
405 405 # separate keystroke) to switch to 'cell' mode and type multiple
406 406 # expressions in one shot.
407 407 if self.indent_spaces==0:
408 408 if self.input_mode=='line':
409 409 if not self._full_dedent:
410 410 return False
411 411 else:
412 412 try:
413 413 code_ast = ast.parse(u''.join(self._buffer))
414 414 except Exception:
415 415 return False
416 416 else:
417 417 if len(code_ast.body) == 1:
418 418 return False
419 419
420 420 # When input is complete, then termination is marked by an extra blank
421 421 # line at the end.
422 422 last_line = self.source.splitlines()[-1]
423 423 return bool(last_line and not last_line.isspace())
424 424
425 425 #------------------------------------------------------------------------
426 426 # Private interface
427 427 #------------------------------------------------------------------------
428 428
429 429 def _find_indent(self, line):
430 430 """Compute the new indentation level for a single line.
431 431
432 432 Parameters
433 433 ----------
434 434 line : str
435 435 A single new line of non-whitespace, non-comment Python input.
436 436
437 437 Returns
438 438 -------
439 439 indent_spaces : int
440 440 New value for the indent level (it may be equal to self.indent_spaces
441 441 if indentation doesn't change.
442 442
443 443 full_dedent : boolean
444 444 Whether the new line causes a full flush-left dedent.
445 445 """
446 446 indent_spaces = self.indent_spaces
447 447 full_dedent = self._full_dedent
448 448
449 449 inisp = num_ini_spaces(line)
450 450 if inisp < indent_spaces:
451 451 indent_spaces = inisp
452 452 if indent_spaces <= 0:
453 453 #print 'Full dedent in text',self.source # dbg
454 454 full_dedent = True
455 455
456 456 if line.rstrip()[-1] == ':':
457 457 indent_spaces += 4
458 458 elif dedent_re.match(line):
459 459 indent_spaces -= 4
460 460 if indent_spaces <= 0:
461 461 full_dedent = True
462 462
463 463 # Safety
464 464 if indent_spaces < 0:
465 465 indent_spaces = 0
466 466 #print 'safety' # dbg
467 467
468 468 return indent_spaces, full_dedent
469 469
470 470 def _update_indent(self, lines):
471 471 for line in remove_comments(lines).splitlines():
472 472 if line and not line.isspace():
473 473 self.indent_spaces, self._full_dedent = self._find_indent(line)
474 474
475 475 def _store(self, lines, buffer=None, store='source'):
476 476 """Store one or more lines of input.
477 477
478 478 If input lines are not newline-terminated, a newline is automatically
479 479 appended."""
480 480
481 481 if buffer is None:
482 482 buffer = self._buffer
483 483
484 484 if lines.endswith('\n'):
485 485 buffer.append(lines)
486 486 else:
487 487 buffer.append(lines+'\n')
488 488 setattr(self, store, self._set_source(buffer))
489 489
490 490 def _set_source(self, buffer):
491 491 return u''.join(buffer)
492 492
493 493
494 494 class IPythonInputSplitter(InputSplitter):
495 495 """An input splitter that recognizes all of IPython's special syntax."""
496 496
497 497 # String with raw, untransformed input.
498 498 source_raw = ''
499 499
500 500 # Flag to track when a transformer has stored input that it hasn't given
501 501 # back yet.
502 502 transformer_accumulating = False
503 503
504 504 # Flag to track when assemble_python_lines has stored input that it hasn't
505 505 # given back yet.
506 506 within_python_line = False
507 507
508 508 # Private attributes
509 509
510 510 # List with lines of raw input accumulated so far.
511 511 _buffer_raw = None
512 512
513 513 def __init__(self, input_mode=None, physical_line_transforms=None,
514 514 logical_line_transforms=None, python_line_transforms=None):
515 515 super(IPythonInputSplitter, self).__init__(input_mode)
516 516 self._buffer_raw = []
517 517 self._validate = True
518 518
519 519 self.physical_line_transforms = physical_line_transforms or \
520 520 [leading_indent(),
521 521 classic_prompt(),
522 522 ipy_prompt(),
523 523 ]
524 524
525 525 self.assemble_logical_lines = assemble_logical_lines()
526 526 self.logical_line_transforms = logical_line_transforms or \
527 527 [cellmagic(),
528 528 help_end(),
529 529 escaped_commands(),
530 530 assign_from_magic(),
531 531 assign_from_system(),
532 532 ]
533 533
534 534 self.assemble_python_lines = assemble_python_lines()
535 535 self.python_line_transforms = python_line_transforms or []
536 536
537 537 @property
538 538 def transforms(self):
539 539 "Quick access to all transformers."
540 540 return self.physical_line_transforms + \
541 541 [self.assemble_logical_lines] + self.logical_line_transforms + \
542 542 [self.assemble_python_lines] + self.python_line_transforms
543 543
544 544 @property
545 545 def transforms_in_use(self):
546 546 """Transformers, excluding logical line transformers if we're in a
547 547 Python line."""
548 t = self.physical_line_transforms + [self.assemble_logical_lines]
548 t = self.physical_line_transforms[:]
549 549 if not self.within_python_line:
550 t += self.logical_line_transforms
550 t += [self.assemble_logical_lines] + self.logical_line_transforms
551 551 return t + [self.assemble_python_lines] + self.python_line_transforms
552 552
553 553 def reset(self):
554 554 """Reset the input buffer and associated state."""
555 555 super(IPythonInputSplitter, self).reset()
556 556 self._buffer_raw[:] = []
557 557 self.source_raw = ''
558 558 self.transformer_accumulating = False
559 self.within_python_line = False
559 560 for t in self.transforms:
560 561 t.reset()
561 562
562 563 def flush_transformers(self):
563 564 def _flush(transform, out):
564 565 if out is not None:
565 566 tmp = transform.push(out)
566 567 return tmp or transform.reset() or None
567 568 else:
568 569 return transform.reset() or None
569 570
570 571 out = None
571 572 for t in self.transforms_in_use:
572 573 out = _flush(t, out)
573 574
574 575 if out is not None:
575 576 self._store(out)
576 577
577 578 def source_raw_reset(self):
578 579 """Return input and raw source and perform a full reset.
579 580 """
580 581 self.flush_transformers()
581 582 out = self.source
582 583 out_r = self.source_raw
583 584 self.reset()
584 585 return out, out_r
585 586
586 587 def source_reset(self):
587 588 self.flush_transformers()
588 589 return super(IPythonInputSplitter, self).source_reset()
589 590
590 591 def push_accepts_more(self):
591 592 if self.transformer_accumulating:
592 593 return True
593 594 else:
594 595 return super(IPythonInputSplitter, self).push_accepts_more()
595 596
596 597 def transform_cell(self, cell):
597 598 """Process and translate a cell of input.
598 599 """
599 600 self.reset()
600 601 self.push(cell)
601 602 return self.source_reset()
602 603
603 604 def push(self, lines):
604 605 """Push one or more lines of IPython input.
605 606
606 607 This stores the given lines and returns a status code indicating
607 608 whether the code forms a complete Python block or not, after processing
608 609 all input lines for special IPython syntax.
609 610
610 611 Any exceptions generated in compilation are swallowed, but if an
611 612 exception was produced, the method returns True.
612 613
613 614 Parameters
614 615 ----------
615 616 lines : string
616 617 One or more lines of Python input.
617 618
618 619 Returns
619 620 -------
620 621 is_complete : boolean
621 622 True if the current input source (the result of the current input
622 623 plus prior inputs) forms a complete Python execution block. Note that
623 624 this value is also stored as a private attribute (_is_complete), so it
624 625 can be queried at any time.
625 626 """
626 627
627 628 # We must ensure all input is pure unicode
628 629 lines = cast_unicode(lines, self.encoding)
629 630
630 631 # ''.splitlines() --> [], but we need to push the empty line to transformers
631 632 lines_list = lines.splitlines()
632 633 if not lines_list:
633 634 lines_list = ['']
634 635
635 636 # Transform logic
636 637 #
637 638 # We only apply the line transformers to the input if we have either no
638 639 # input yet, or complete input, or if the last line of the buffer ends
639 640 # with ':' (opening an indented block). This prevents the accidental
640 641 # transformation of escapes inside multiline expressions like
641 642 # triple-quoted strings or parenthesized expressions.
642 643 #
643 644 # The last heuristic, while ugly, ensures that the first line of an
644 645 # indented block is correctly transformed.
645 646 #
646 647 # FIXME: try to find a cleaner approach for this last bit.
647 648
648 649 # If we were in 'block' mode, since we're going to pump the parent
649 650 # class by hand line by line, we need to temporarily switch out to
650 651 # 'line' mode, do a single manual reset and then feed the lines one
651 652 # by one. Note that this only matters if the input has more than one
652 653 # line.
653 654 changed_input_mode = False
654 655
655 656 if self.input_mode == 'cell':
656 657 self.reset()
657 658 changed_input_mode = True
658 659 saved_input_mode = 'cell'
659 660 self.input_mode = 'line'
660 661
661 662 # Store raw source before applying any transformations to it. Note
662 663 # that this must be done *after* the reset() call that would otherwise
663 664 # flush the buffer.
664 665 self._store(lines, self._buffer_raw, 'source_raw')
665 666
666 667 try:
667 668 for line in lines_list:
668 669 out = self.push_line(line)
669 670 finally:
670 671 if changed_input_mode:
671 672 self.input_mode = saved_input_mode
672 673
673 674 return out
674 675
675 676 def push_line(self, line):
676 677 buf = self._buffer
677 678
678 679 def _accumulating(dbg):
679 680 #print(dbg)
680 681 self.transformer_accumulating = True
681 682 return False
682 683
683 684 for transformer in self.physical_line_transforms:
684 685 line = transformer.push(line)
685 686 if line is None:
686 687 return _accumulating(transformer)
687 688
688 line = self.assemble_logical_lines.push(line)
689 if line is None:
690 return _accumulating('acc logical line')
691
692 689 if not self.within_python_line:
690 line = self.assemble_logical_lines.push(line)
691 if line is None:
692 return _accumulating('acc logical line')
693
693 694 for transformer in self.logical_line_transforms:
694 695 line = transformer.push(line)
695 696 if line is None:
696 697 return _accumulating(transformer)
697 698
698 699 line = self.assemble_python_lines.push(line)
699 700 if line is None:
700 701 self.within_python_line = True
701 702 return _accumulating('acc python line')
702 703 else:
703 704 self.within_python_line = False
704 705
705 706 for transformer in self.python_line_transforms:
706 707 line = transformer.push(line)
707 708 if line is None:
708 709 return _accumulating(transformer)
709 710
710 711 #print("transformers clear") #debug
711 712 self.transformer_accumulating = False
712 713 return super(IPythonInputSplitter, self).push(line)
@@ -1,434 +1,436 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 63 def transformer_factory():
64 64 return cls(func)
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 87 def __init__(self, coro):
88 88 # Prime it
89 89 self.coro = coro()
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 parts.append(line.rstrip('\\'))
184 if not line.endswith('\\'):
183 if line.endswith('\\') and (not has_comment(line)):
184 parts.append(line[:-1])
185 line = (yield None) # Get another line
186 else:
187 parts.append(line)
185 188 break
186 line = (yield None)
187 189
188 190 # Output
189 line = ' '.join(parts)
191 line = ''.join(parts)
190 192
191 193 # Utilities
192 194 def _make_help_call(target, esc, lspace, next_input=None):
193 195 """Prepares a pinfo(2)/psearch call from a target name and the escape
194 196 (i.e. ? or ??)"""
195 197 method = 'pinfo2' if esc == '??' \
196 198 else 'psearch' if '*' in target \
197 199 else 'pinfo'
198 200 arg = " ".join([method, target])
199 201 if next_input is None:
200 202 return '%sget_ipython().magic(%r)' % (lspace, arg)
201 203 else:
202 204 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
203 205 (lspace, next_input, arg)
204 206
205 207 # These define the transformations for the different escape characters.
206 208 def _tr_system(line_info):
207 209 "Translate lines escaped with: !"
208 210 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
209 211 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
210 212
211 213 def _tr_system2(line_info):
212 214 "Translate lines escaped with: !!"
213 215 cmd = line_info.line.lstrip()[2:]
214 216 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
215 217
216 218 def _tr_help(line_info):
217 219 "Translate lines escaped with: ?/??"
218 220 # A naked help line should just fire the intro help screen
219 221 if not line_info.line[1:]:
220 222 return 'get_ipython().show_usage()'
221 223
222 224 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
223 225
224 226 def _tr_magic(line_info):
225 227 "Translate lines escaped with: %"
226 228 tpl = '%sget_ipython().magic(%r)'
227 229 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
228 230 return tpl % (line_info.pre, cmd)
229 231
230 232 def _tr_quote(line_info):
231 233 "Translate lines escaped with: ,"
232 234 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
233 235 '", "'.join(line_info.the_rest.split()) )
234 236
235 237 def _tr_quote2(line_info):
236 238 "Translate lines escaped with: ;"
237 239 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
238 240 line_info.the_rest)
239 241
240 242 def _tr_paren(line_info):
241 243 "Translate lines escaped with: /"
242 244 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
243 245 ", ".join(line_info.the_rest.split()))
244 246
245 247 tr = { ESC_SHELL : _tr_system,
246 248 ESC_SH_CAP : _tr_system2,
247 249 ESC_HELP : _tr_help,
248 250 ESC_HELP2 : _tr_help,
249 251 ESC_MAGIC : _tr_magic,
250 252 ESC_QUOTE : _tr_quote,
251 253 ESC_QUOTE2 : _tr_quote2,
252 254 ESC_PAREN : _tr_paren }
253 255
254 256 @StatelessInputTransformer.wrap
255 257 def escaped_commands(line):
256 258 """Transform escaped commands - %magic, !system, ?help + various autocalls.
257 259 """
258 260 if not line or line.isspace():
259 261 return line
260 262 lineinf = LineInfo(line)
261 263 if lineinf.esc not in tr:
262 264 return line
263 265
264 266 return tr[lineinf.esc](lineinf)
265 267
266 268 _initial_space_re = re.compile(r'\s*')
267 269
268 270 _help_end_re = re.compile(r"""(%{0,2}
269 271 [a-zA-Z_*][\w*]* # Variable name
270 272 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
271 273 )
272 274 (\?\??)$ # ? or ??""",
273 275 re.VERBOSE)
274 276
275 277 def has_comment(src):
276 278 """Indicate whether an input line has (i.e. ends in, or is) a comment.
277 279
278 280 This uses tokenize, so it can distinguish comments from # inside strings.
279 281
280 282 Parameters
281 283 ----------
282 284 src : string
283 285 A single line input string.
284 286
285 287 Returns
286 288 -------
287 289 comment : bool
288 290 True if source has a comment.
289 291 """
290 292 readline = StringIO(src).readline
291 293 toktypes = set()
292 294 try:
293 295 for t in generate_tokens(readline):
294 296 toktypes.add(t[0])
295 297 except TokenError:
296 298 pass
297 299 return(tokenize2.COMMENT in toktypes)
298 300
299 301
300 302 @StatelessInputTransformer.wrap
301 303 def help_end(line):
302 304 """Translate lines with ?/?? at the end"""
303 305 m = _help_end_re.search(line)
304 306 if m is None or has_comment(line):
305 307 return line
306 308 target = m.group(1)
307 309 esc = m.group(3)
308 310 lspace = _initial_space_re.match(line).group(0)
309 311
310 312 # If we're mid-command, put it back on the next prompt for the user.
311 313 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
312 314
313 315 return _make_help_call(target, esc, lspace, next_input)
314 316
315 317
316 318 @CoroutineInputTransformer.wrap
317 319 def cellmagic():
318 320 """Captures & transforms cell magics.
319 321
320 322 After a cell magic is started, this stores up any lines it gets until it is
321 323 reset (sent None).
322 324 """
323 325 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
324 326 cellmagic_help_re = re.compile('%%\w+\?')
325 327 line = ''
326 328 while True:
327 329 line = (yield line)
328 330 if (not line) or (not line.startswith(ESC_MAGIC2)):
329 331 continue
330 332
331 333 if cellmagic_help_re.match(line):
332 334 # This case will be handled by help_end
333 335 continue
334 336
335 337 first = line
336 338 body = []
337 339 line = (yield None)
338 340 while (line is not None) and (line.strip() != ''):
339 341 body.append(line)
340 342 line = (yield None)
341 343
342 344 # Output
343 345 magic_name, _, first = first.partition(' ')
344 346 magic_name = magic_name.lstrip(ESC_MAGIC2)
345 347 line = tpl % (magic_name, first, u'\n'.join(body))
346 348
347 349
348 350 def _strip_prompts(prompt1_re, prompt2_re):
349 351 """Remove matching input prompts from a block of input."""
350 352 line = ''
351 353 while True:
352 354 line = (yield line)
353 355
354 356 if line is None:
355 357 continue
356 358
357 359 m = prompt1_re.match(line)
358 360 if m:
359 361 while m:
360 362 line = (yield line[len(m.group(0)):])
361 363 if line is None:
362 364 break
363 365 m = prompt2_re.match(line)
364 366 else:
365 367 # Prompts not in input - wait for reset
366 368 while line is not None:
367 369 line = (yield line)
368 370
369 371 @CoroutineInputTransformer.wrap
370 372 def classic_prompt():
371 373 """Strip the >>>/... prompts of the Python interactive shell."""
372 374 prompt1_re = re.compile(r'^(>>> )')
373 375 prompt2_re = re.compile(r'^(>>> |^\.\.\. )')
374 376 return _strip_prompts(prompt1_re, prompt2_re)
375 377
376 378 @CoroutineInputTransformer.wrap
377 379 def ipy_prompt():
378 380 """Strip IPython's In [1]:/...: prompts."""
379 381 prompt1_re = re.compile(r'^In \[\d+\]: ')
380 382 prompt2_re = re.compile(r'^(In \[\d+\]: |^\ \ \ \.\.\.+: )')
381 383 return _strip_prompts(prompt1_re, prompt2_re)
382 384
383 385
384 386 @CoroutineInputTransformer.wrap
385 387 def leading_indent():
386 388 """Remove leading indentation.
387 389
388 390 If the first line starts with a spaces or tabs, the same whitespace will be
389 391 removed from each following line until it is reset.
390 392 """
391 393 space_re = re.compile(r'^[ \t]+')
392 394 line = ''
393 395 while True:
394 396 line = (yield line)
395 397
396 398 if line is None:
397 399 continue
398 400
399 401 m = space_re.match(line)
400 402 if m:
401 403 space = m.group(0)
402 404 while line is not None:
403 405 if line.startswith(space):
404 406 line = line[len(space):]
405 407 line = (yield line)
406 408 else:
407 409 # No leading spaces - wait for reset
408 410 while line is not None:
409 411 line = (yield line)
410 412
411 413
412 414 assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
413 415 r'\s*=\s*!\s*(?P<cmd>.*)')
414 416 assign_system_template = '%s = get_ipython().getoutput(%r)'
415 417 @StatelessInputTransformer.wrap
416 418 def assign_from_system(line):
417 419 """Transform assignment from system commands (e.g. files = !ls)"""
418 420 m = assign_system_re.match(line)
419 421 if m is None:
420 422 return line
421 423
422 424 return assign_system_template % m.group('lhs', 'cmd')
423 425
424 426 assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
425 427 r'\s*=\s*%\s*(?P<cmd>.*)')
426 428 assign_magic_template = '%s = get_ipython().magic(%r)'
427 429 @StatelessInputTransformer.wrap
428 430 def assign_from_magic(line):
429 431 """Transform assignment from magic commands (e.g. a = %who_ls)"""
430 432 m = assign_magic_re.match(line)
431 433 if m is None:
432 434 return line
433 435
434 436 return assign_magic_template % m.group('lhs', 'cmd')
@@ -1,394 +1,403 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 multiline_datastructure =
193 multiline_datastructure_prompt =
194 194 [ [('>>> a = [1,','a = [1,'),
195 195 ('... 2]','2]'),
196 196 ],
197 197 ],
198
199 multiline_datastructure =
200 [ [('b = ("%s"', None),
201 ('# comment', None),
202 ('%foo )', 'b = ("%s"\n# comment\n%foo )'),
203 ],
204 ],
198 205
199 206 leading_indent =
200 207 [ [(' print "hi"','print "hi"'),
201 208 ],
202 209 [(' for a in range(5):','for a in range(5):'),
203 210 (' a*2',' a*2'),
204 211 ],
205 212 [(' a="""','a="""'),
206 213 (' 123"""','123"""'),
207 214 ],
208 215 [('a="""','a="""'),
209 216 (' 123"""',' 123"""'),
210 217 ],
211 218 ],
212 219
213 220 cellmagic =
214 221 [ [(u'%%foo a', None),
215 222 (None, u_fmt("get_ipython().run_cell_magic({u}'foo', {u}'a', {u}'')")),
216 223 ],
217 224 [(u'%%bar 123', None),
218 225 (u'hello', None),
219 226 (u'', u_fmt("get_ipython().run_cell_magic({u}'bar', {u}'123', {u}'hello')")),
220 227 ],
221 228 ],
222 229
223 230 escaped =
224 231 [ [('%abc def \\', None),
225 ('ghi', u_fmt("get_ipython().magic({u}'abc def ghi')")),
232 ('ghi', u_fmt("get_ipython().magic({u}'abc def ghi')")),
226 233 ],
227 234 [('%abc def \\', None),
228 235 ('ghi\\', None),
229 (None, u_fmt("get_ipython().magic({u}'abc def ghi')")),
236 (None, u_fmt("get_ipython().magic({u}'abc def ghi')")),
230 237 ],
231 238 ],
232 239
233 240 assign_magic =
234 241 [ [(u'a = %bc de \\', None),
235 (u'fg', u_fmt("a = get_ipython().magic({u}'bc de fg')")),
242 (u'fg', u_fmt("a = get_ipython().magic({u}'bc de fg')")),
236 243 ],
237 244 [(u'a = %bc de \\', None),
238 245 (u'fg\\', None),
239 (None, u_fmt("a = get_ipython().magic({u}'bc de fg')")),
246 (None, u_fmt("a = get_ipython().magic({u}'bc de fg')")),
240 247 ],
241 248 ],
242 249
243 250 assign_system =
244 251 [ [(u'a = !bc de \\', None),
245 (u'fg', u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
252 (u'fg', u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
246 253 ],
247 254 [(u'a = !bc de \\', None),
248 255 (u'fg\\', None),
249 (None, u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
256 (None, u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
250 257 ],
251 258 ],
252 259 )
253 260
254 261
255 262 def test_assign_system():
256 263 tt.check_pairs(transform_and_reset(ipt.assign_from_system), syntax['assign_system'])
257 264
258 265 def test_assign_magic():
259 266 tt.check_pairs(transform_and_reset(ipt.assign_from_magic), syntax['assign_magic'])
260 267
261 268 def test_classic_prompt():
262 269 tt.check_pairs(transform_and_reset(ipt.classic_prompt), syntax['classic_prompt'])
263 270 for example in syntax_ml['classic_prompt']:
264 271 transform_checker(example, ipt.classic_prompt)
265 for example in syntax_ml['multiline_datastructure']:
272 for example in syntax_ml['multiline_datastructure_prompt']:
266 273 transform_checker(example, ipt.classic_prompt)
267 274
268 275
269 276 def test_ipy_prompt():
270 277 tt.check_pairs(transform_and_reset(ipt.ipy_prompt), syntax['ipy_prompt'])
271 278 for example in syntax_ml['ipy_prompt']:
272 279 transform_checker(example, ipt.ipy_prompt)
273 280
274 281 def test_assemble_logical_lines():
275 282 tests = \
276 283 [ [(u"a = \\", None),
277 (u"123", u"a = 123"),
284 (u"123", u"a = 123"),
278 285 ],
279 286 [(u"a = \\", None), # Test resetting when within a multi-line string
280 287 (u"12 *\\", None),
281 (None, u"a = 12 *"),
288 (None, u"a = 12 *"),
289 ],
290 [(u"# foo\\", u"# foo\\"), # Comments can't be continued like this
282 291 ],
283 292 ]
284 293 for example in tests:
285 294 transform_checker(example, ipt.assemble_logical_lines)
286 295
287 296 def test_assemble_python_lines():
288 297 tests = \
289 298 [ [(u"a = '''", None),
290 299 (u"abc'''", u"a = '''\nabc'''"),
291 300 ],
292 301 [(u"a = '''", None), # Test resetting when within a multi-line string
293 302 (u"def", None),
294 303 (None, u"a = '''\ndef"),
295 304 ],
296 305 [(u"a = [1,", None),
297 306 (u"2]", u"a = [1,\n2]"),
298 307 ],
299 308 [(u"a = [1,", None), # Test resetting when within a multi-line string
300 309 (u"2,", None),
301 310 (None, u"a = [1,\n2,"),
302 311 ],
303 ]
312 ] + syntax_ml['multiline_datastructure']
304 313 for example in tests:
305 314 transform_checker(example, ipt.assemble_python_lines)
306 315
307 316
308 317 def test_help_end():
309 318 tt.check_pairs(transform_and_reset(ipt.help_end), syntax['end_help'])
310 319
311 320 def test_escaped_noesc():
312 321 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_noesc'])
313 322
314 323
315 324 def test_escaped_shell():
316 325 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_shell'])
317 326
318 327
319 328 def test_escaped_help():
320 329 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_help'])
321 330
322 331
323 332 def test_escaped_magic():
324 333 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_magic'])
325 334
326 335
327 336 def test_escaped_quote():
328 337 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_quote'])
329 338
330 339
331 340 def test_escaped_quote2():
332 341 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_quote2'])
333 342
334 343
335 344 def test_escaped_paren():
336 345 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_paren'])
337 346
338 347
339 348 def test_cellmagic():
340 349 for example in syntax_ml['cellmagic']:
341 350 transform_checker(example, ipt.cellmagic)
342 351
343 352 def test_has_comment():
344 353 tests = [('text', False),
345 354 ('text #comment', True),
346 355 ('text #comment\n', True),
347 356 ('#comment', True),
348 357 ('#comment\n', True),
349 358 ('a = "#string"', False),
350 359 ('a = "#string" # comment', True),
351 360 ('a #comment not "string"', True),
352 361 ]
353 362 tt.check_pairs(ipt.has_comment, tests)
354 363
355 364 @ipt.TokenInputTransformer.wrap
356 365 def decistmt(tokens):
357 366 """Substitute Decimals for floats in a string of statements.
358 367
359 368 Based on an example from the tokenize module docs.
360 369 """
361 370 result = []
362 371 for toknum, tokval, _, _, _ in tokens:
363 372 if toknum == tokenize.NUMBER and '.' in tokval: # replace NUMBER tokens
364 373 for newtok in [
365 374 (tokenize.NAME, 'Decimal'),
366 375 (tokenize.OP, '('),
367 376 (tokenize.STRING, repr(tokval)),
368 377 (tokenize.OP, ')')
369 378 ]:
370 379 yield newtok
371 380 else:
372 381 yield (toknum, tokval)
373 382
374 383
375 384
376 385 def test_token_input_transformer():
377 386 tests = [(u'1.2', u_fmt(u"Decimal ({u}'1.2')")),
378 387 (u'"1.2"', u'"1.2"'),
379 388 ]
380 389 tt.check_pairs(transform_and_reset(decistmt), tests)
381 390 ml_tests = \
382 391 [ [(u"a = 1.2; b = '''x", None),
383 392 (u"y'''", u_fmt(u"a =Decimal ({u}'1.2');b ='''x\ny'''")),
384 393 ],
385 394 [(u"a = [1.2,", None),
386 395 (u"3]", u_fmt(u"a =[Decimal ({u}'1.2'),\n3 ]")),
387 396 ],
388 397 [(u"a = '''foo", None), # Test resetting when within a multi-line string
389 398 (u"bar", None),
390 399 (None, u"a = '''foo\nbar"),
391 400 ],
392 401 ]
393 402 for example in ml_tests:
394 403 transform_checker(example, decistmt)
General Comments 0
You need to be logged in to leave comments. Login now