##// END OF EJS Templates
Fixed whitespace bug when pasting IPython continuation prompt....
Pascal Bugnion -
Show More
@@ -1,549 +1,549 b''
1 1 """Input transformer classes to support IPython special syntax.
2 2
3 3 This includes the machinery to recognise and transform ``%magic`` commands,
4 4 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
5 5 """
6 6 import abc
7 7 import functools
8 8 import re
9 9
10 10 from IPython.core.splitinput import LineInfo
11 11 from IPython.utils import tokenize2
12 12 from IPython.utils.openpy import cookie_comment_re
13 13 from IPython.utils.py3compat import with_metaclass, PY3
14 14 from IPython.utils.tokenize2 import generate_tokens, untokenize, TokenError
15 15
16 16 if PY3:
17 17 from io import StringIO
18 18 else:
19 19 from StringIO import StringIO
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Globals
23 23 #-----------------------------------------------------------------------------
24 24
25 25 # The escape sequences that define the syntax transformations IPython will
26 26 # apply to user input. These can NOT be just changed here: many regular
27 27 # expressions and other parts of the code may use their hardcoded values, and
28 28 # for all intents and purposes they constitute the 'IPython syntax', so they
29 29 # should be considered fixed.
30 30
31 31 ESC_SHELL = '!' # Send line to underlying system shell
32 32 ESC_SH_CAP = '!!' # Send line to system shell and capture output
33 33 ESC_HELP = '?' # Find information about object
34 34 ESC_HELP2 = '??' # Find extra-detailed information about object
35 35 ESC_MAGIC = '%' # Call magic function
36 36 ESC_MAGIC2 = '%%' # Call cell-magic function
37 37 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
38 38 ESC_QUOTE2 = ';' # Quote all args as a single string, call
39 39 ESC_PAREN = '/' # Call first argument with rest of line as arguments
40 40
41 41 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
42 42 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
43 43 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
44 44
45 45
46 46 class InputTransformer(with_metaclass(abc.ABCMeta, object)):
47 47 """Abstract base class for line-based input transformers."""
48 48
49 49 @abc.abstractmethod
50 50 def push(self, line):
51 51 """Send a line of input to the transformer, returning the transformed
52 52 input or None if the transformer is waiting for more input.
53 53
54 54 Must be overridden by subclasses.
55 55
56 56 Implementations may raise ``SyntaxError`` if the input is invalid. No
57 57 other exceptions may be raised.
58 58 """
59 59 pass
60 60
61 61 @abc.abstractmethod
62 62 def reset(self):
63 63 """Return, transformed any lines that the transformer has accumulated,
64 64 and reset its internal state.
65 65
66 66 Must be overridden by subclasses.
67 67 """
68 68 pass
69 69
70 70 @classmethod
71 71 def wrap(cls, func):
72 72 """Can be used by subclasses as a decorator, to return a factory that
73 73 will allow instantiation with the decorated object.
74 74 """
75 75 @functools.wraps(func)
76 76 def transformer_factory(**kwargs):
77 77 return cls(func, **kwargs)
78 78
79 79 return transformer_factory
80 80
81 81 class StatelessInputTransformer(InputTransformer):
82 82 """Wrapper for a stateless input transformer implemented as a function."""
83 83 def __init__(self, func):
84 84 self.func = func
85 85
86 86 def __repr__(self):
87 87 return "StatelessInputTransformer(func={0!r})".format(self.func)
88 88
89 89 def push(self, line):
90 90 """Send a line of input to the transformer, returning the
91 91 transformed input."""
92 92 return self.func(line)
93 93
94 94 def reset(self):
95 95 """No-op - exists for compatibility."""
96 96 pass
97 97
98 98 class CoroutineInputTransformer(InputTransformer):
99 99 """Wrapper for an input transformer implemented as a coroutine."""
100 100 def __init__(self, coro, **kwargs):
101 101 # Prime it
102 102 self.coro = coro(**kwargs)
103 103 next(self.coro)
104 104
105 105 def __repr__(self):
106 106 return "CoroutineInputTransformer(coro={0!r})".format(self.coro)
107 107
108 108 def push(self, line):
109 109 """Send a line of input to the transformer, returning the
110 110 transformed input or None if the transformer is waiting for more
111 111 input.
112 112 """
113 113 return self.coro.send(line)
114 114
115 115 def reset(self):
116 116 """Return, transformed any lines that the transformer has
117 117 accumulated, and reset its internal state.
118 118 """
119 119 return self.coro.send(None)
120 120
121 121 class TokenInputTransformer(InputTransformer):
122 122 """Wrapper for a token-based input transformer.
123 123
124 124 func should accept a list of tokens (5-tuples, see tokenize docs), and
125 125 return an iterable which can be passed to tokenize.untokenize().
126 126 """
127 127 def __init__(self, func):
128 128 self.func = func
129 129 self.current_line = ""
130 130 self.line_used = False
131 131 self.reset_tokenizer()
132 132
133 133 def reset_tokenizer(self):
134 134 self.tokenizer = generate_tokens(self.get_line)
135 135
136 136 def get_line(self):
137 137 if self.line_used:
138 138 raise TokenError
139 139 self.line_used = True
140 140 return self.current_line
141 141
142 142 def push(self, line):
143 143 self.current_line += line + "\n"
144 144 if self.current_line.isspace():
145 145 return self.reset()
146 146
147 147 self.line_used = False
148 148 tokens = []
149 149 stop_at_NL = False
150 150 try:
151 151 for intok in self.tokenizer:
152 152 tokens.append(intok)
153 153 t = intok[0]
154 154 if t == tokenize2.NEWLINE or (stop_at_NL and t == tokenize2.NL):
155 155 # Stop before we try to pull a line we don't have yet
156 156 break
157 157 elif t == tokenize2.ERRORTOKEN:
158 158 stop_at_NL = True
159 159 except TokenError:
160 160 # Multi-line statement - stop and try again with the next line
161 161 self.reset_tokenizer()
162 162 return None
163 163
164 164 return self.output(tokens)
165 165
166 166 def output(self, tokens):
167 167 self.current_line = ""
168 168 self.reset_tokenizer()
169 169 return untokenize(self.func(tokens)).rstrip('\n')
170 170
171 171 def reset(self):
172 172 l = self.current_line
173 173 self.current_line = ""
174 174 self.reset_tokenizer()
175 175 if l:
176 176 return l.rstrip('\n')
177 177
178 178 class assemble_python_lines(TokenInputTransformer):
179 179 def __init__(self):
180 180 super(assemble_python_lines, self).__init__(None)
181 181
182 182 def output(self, tokens):
183 183 return self.reset()
184 184
185 185 @CoroutineInputTransformer.wrap
186 186 def assemble_logical_lines():
187 187 """Join lines following explicit line continuations (\)"""
188 188 line = ''
189 189 while True:
190 190 line = (yield line)
191 191 if not line or line.isspace():
192 192 continue
193 193
194 194 parts = []
195 195 while line is not None:
196 196 if line.endswith('\\') and (not has_comment(line)):
197 197 parts.append(line[:-1])
198 198 line = (yield None) # Get another line
199 199 else:
200 200 parts.append(line)
201 201 break
202 202
203 203 # Output
204 204 line = ''.join(parts)
205 205
206 206 # Utilities
207 207 def _make_help_call(target, esc, lspace, next_input=None):
208 208 """Prepares a pinfo(2)/psearch call from a target name and the escape
209 209 (i.e. ? or ??)"""
210 210 method = 'pinfo2' if esc == '??' \
211 211 else 'psearch' if '*' in target \
212 212 else 'pinfo'
213 213 arg = " ".join([method, target])
214 214 if next_input is None:
215 215 return '%sget_ipython().magic(%r)' % (lspace, arg)
216 216 else:
217 217 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
218 218 (lspace, next_input, arg)
219 219
220 220 # These define the transformations for the different escape characters.
221 221 def _tr_system(line_info):
222 222 "Translate lines escaped with: !"
223 223 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
224 224 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
225 225
226 226 def _tr_system2(line_info):
227 227 "Translate lines escaped with: !!"
228 228 cmd = line_info.line.lstrip()[2:]
229 229 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
230 230
231 231 def _tr_help(line_info):
232 232 "Translate lines escaped with: ?/??"
233 233 # A naked help line should just fire the intro help screen
234 234 if not line_info.line[1:]:
235 235 return 'get_ipython().show_usage()'
236 236
237 237 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
238 238
239 239 def _tr_magic(line_info):
240 240 "Translate lines escaped with: %"
241 241 tpl = '%sget_ipython().magic(%r)'
242 242 if line_info.line.startswith(ESC_MAGIC2):
243 243 return line_info.line
244 244 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
245 245 return tpl % (line_info.pre, cmd)
246 246
247 247 def _tr_quote(line_info):
248 248 "Translate lines escaped with: ,"
249 249 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
250 250 '", "'.join(line_info.the_rest.split()) )
251 251
252 252 def _tr_quote2(line_info):
253 253 "Translate lines escaped with: ;"
254 254 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
255 255 line_info.the_rest)
256 256
257 257 def _tr_paren(line_info):
258 258 "Translate lines escaped with: /"
259 259 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
260 260 ", ".join(line_info.the_rest.split()))
261 261
262 262 tr = { ESC_SHELL : _tr_system,
263 263 ESC_SH_CAP : _tr_system2,
264 264 ESC_HELP : _tr_help,
265 265 ESC_HELP2 : _tr_help,
266 266 ESC_MAGIC : _tr_magic,
267 267 ESC_QUOTE : _tr_quote,
268 268 ESC_QUOTE2 : _tr_quote2,
269 269 ESC_PAREN : _tr_paren }
270 270
271 271 @StatelessInputTransformer.wrap
272 272 def escaped_commands(line):
273 273 """Transform escaped commands - %magic, !system, ?help + various autocalls.
274 274 """
275 275 if not line or line.isspace():
276 276 return line
277 277 lineinf = LineInfo(line)
278 278 if lineinf.esc not in tr:
279 279 return line
280 280
281 281 return tr[lineinf.esc](lineinf)
282 282
283 283 _initial_space_re = re.compile(r'\s*')
284 284
285 285 _help_end_re = re.compile(r"""(%{0,2}
286 286 [a-zA-Z_*][\w*]* # Variable name
287 287 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
288 288 )
289 289 (\?\??)$ # ? or ??
290 290 """,
291 291 re.VERBOSE)
292 292
293 293 # Extra pseudotokens for multiline strings and data structures
294 294 _MULTILINE_STRING = object()
295 295 _MULTILINE_STRUCTURE = object()
296 296
297 297 def _line_tokens(line):
298 298 """Helper for has_comment and ends_in_comment_or_string."""
299 299 readline = StringIO(line).readline
300 300 toktypes = set()
301 301 try:
302 302 for t in generate_tokens(readline):
303 303 toktypes.add(t[0])
304 304 except TokenError as e:
305 305 # There are only two cases where a TokenError is raised.
306 306 if 'multi-line string' in e.args[0]:
307 307 toktypes.add(_MULTILINE_STRING)
308 308 else:
309 309 toktypes.add(_MULTILINE_STRUCTURE)
310 310 return toktypes
311 311
312 312 def has_comment(src):
313 313 """Indicate whether an input line has (i.e. ends in, or is) a comment.
314 314
315 315 This uses tokenize, so it can distinguish comments from # inside strings.
316 316
317 317 Parameters
318 318 ----------
319 319 src : string
320 320 A single line input string.
321 321
322 322 Returns
323 323 -------
324 324 comment : bool
325 325 True if source has a comment.
326 326 """
327 327 return (tokenize2.COMMENT in _line_tokens(src))
328 328
329 329 def ends_in_comment_or_string(src):
330 330 """Indicates whether or not an input line ends in a comment or within
331 331 a multiline string.
332 332
333 333 Parameters
334 334 ----------
335 335 src : string
336 336 A single line input string.
337 337
338 338 Returns
339 339 -------
340 340 comment : bool
341 341 True if source ends in a comment or multiline string.
342 342 """
343 343 toktypes = _line_tokens(src)
344 344 return (tokenize2.COMMENT in toktypes) or (_MULTILINE_STRING in toktypes)
345 345
346 346
347 347 @StatelessInputTransformer.wrap
348 348 def help_end(line):
349 349 """Translate lines with ?/?? at the end"""
350 350 m = _help_end_re.search(line)
351 351 if m is None or ends_in_comment_or_string(line):
352 352 return line
353 353 target = m.group(1)
354 354 esc = m.group(3)
355 355 lspace = _initial_space_re.match(line).group(0)
356 356
357 357 # If we're mid-command, put it back on the next prompt for the user.
358 358 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
359 359
360 360 return _make_help_call(target, esc, lspace, next_input)
361 361
362 362
363 363 @CoroutineInputTransformer.wrap
364 364 def cellmagic(end_on_blank_line=False):
365 365 """Captures & transforms cell magics.
366 366
367 367 After a cell magic is started, this stores up any lines it gets until it is
368 368 reset (sent None).
369 369 """
370 370 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
371 371 cellmagic_help_re = re.compile('%%\w+\?')
372 372 line = ''
373 373 while True:
374 374 line = (yield line)
375 375 # consume leading empty lines
376 376 while not line:
377 377 line = (yield line)
378 378
379 379 if not line.startswith(ESC_MAGIC2):
380 380 # This isn't a cell magic, idle waiting for reset then start over
381 381 while line is not None:
382 382 line = (yield line)
383 383 continue
384 384
385 385 if cellmagic_help_re.match(line):
386 386 # This case will be handled by help_end
387 387 continue
388 388
389 389 first = line
390 390 body = []
391 391 line = (yield None)
392 392 while (line is not None) and \
393 393 ((line.strip() != '') or not end_on_blank_line):
394 394 body.append(line)
395 395 line = (yield None)
396 396
397 397 # Output
398 398 magic_name, _, first = first.partition(' ')
399 399 magic_name = magic_name.lstrip(ESC_MAGIC2)
400 400 line = tpl % (magic_name, first, u'\n'.join(body))
401 401
402 402
403 403 def _strip_prompts(prompt_re, initial_re=None):
404 404 """Remove matching input prompts from a block of input.
405 405
406 406 Parameters
407 407 ----------
408 408 prompt_re : regular expression
409 409 A regular expression matching any input prompt (including continuation)
410 410 initial_re : regular expression, optional
411 411 A regular expression matching only the initial prompt, but not continuation.
412 412 If no initial expression is given, prompt_re will be used everywhere.
413 413 Used mainly for plain Python prompts, where the continuation prompt
414 414 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
415 415
416 416 If initial_re and prompt_re differ,
417 417 only initial_re will be tested against the first line.
418 418 If any prompt is found on the first two lines,
419 419 prompts will be stripped from the rest of the block.
420 420 """
421 421 if initial_re is None:
422 422 initial_re = prompt_re
423 423 line = ''
424 424 while True:
425 425 line = (yield line)
426 426
427 427 # First line of cell
428 428 if line is None:
429 429 continue
430 430 out, n1 = initial_re.subn('', line, count=1)
431 431 line = (yield out)
432 432
433 433 if line is None:
434 434 continue
435 435 # check for any prompt on the second line of the cell,
436 436 # because people often copy from just after the first prompt,
437 437 # so we might not see it in the first line.
438 438 out, n2 = prompt_re.subn('', line, count=1)
439 439 line = (yield out)
440 440
441 441 if n1 or n2:
442 442 # Found a prompt in the first two lines - check for it in
443 443 # the rest of the cell as well.
444 444 while line is not None:
445 445 line = (yield prompt_re.sub('', line, count=1))
446 446
447 447 else:
448 448 # Prompts not in input - wait for reset
449 449 while line is not None:
450 450 line = (yield line)
451 451
452 452 @CoroutineInputTransformer.wrap
453 453 def classic_prompt():
454 454 """Strip the >>>/... prompts of the Python interactive shell."""
455 455 # FIXME: non-capturing version (?:...) usable?
456 456 prompt_re = re.compile(r'^(>>>|\.\.\.)( |$)')
457 457 initial_re = re.compile(r'^>>>( |$)')
458 458 return _strip_prompts(prompt_re, initial_re)
459 459
460 460 @CoroutineInputTransformer.wrap
461 461 def ipy_prompt():
462 462 """Strip IPython's In [1]:/...: prompts."""
463 463 # FIXME: non-capturing version (?:...) usable?
464 prompt_re = re.compile(r'^(In \[\d+\]: |\s*\.{3,}: )')
464 prompt_re = re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)')
465 465 return _strip_prompts(prompt_re)
466 466
467 467
468 468 @CoroutineInputTransformer.wrap
469 469 def leading_indent():
470 470 """Remove leading indentation.
471 471
472 472 If the first line starts with a spaces or tabs, the same whitespace will be
473 473 removed from each following line until it is reset.
474 474 """
475 475 space_re = re.compile(r'^[ \t]+')
476 476 line = ''
477 477 while True:
478 478 line = (yield line)
479 479
480 480 if line is None:
481 481 continue
482 482
483 483 m = space_re.match(line)
484 484 if m:
485 485 space = m.group(0)
486 486 while line is not None:
487 487 if line.startswith(space):
488 488 line = line[len(space):]
489 489 line = (yield line)
490 490 else:
491 491 # No leading spaces - wait for reset
492 492 while line is not None:
493 493 line = (yield line)
494 494
495 495
496 496 @CoroutineInputTransformer.wrap
497 497 def strip_encoding_cookie():
498 498 """Remove encoding comment if found in first two lines
499 499
500 500 If the first or second line has the `# coding: utf-8` comment,
501 501 it will be removed.
502 502 """
503 503 line = ''
504 504 while True:
505 505 line = (yield line)
506 506 # check comment on first two lines
507 507 for i in range(2):
508 508 if line is None:
509 509 break
510 510 if cookie_comment_re.match(line):
511 511 line = (yield "")
512 512 else:
513 513 line = (yield line)
514 514
515 515 # no-op on the rest of the cell
516 516 while line is not None:
517 517 line = (yield line)
518 518
519 519 _assign_pat = \
520 520 r'''(?P<lhs>(\s*)
521 521 ([\w\.]+) # Initial identifier
522 522 (\s*,\s*
523 523 \*?[\w\.]+)* # Further identifiers for unpacking
524 524 \s*?,? # Trailing comma
525 525 )
526 526 \s*=\s*
527 527 '''
528 528
529 529 assign_system_re = re.compile(r'{}!\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
530 530 assign_system_template = '%s = get_ipython().getoutput(%r)'
531 531 @StatelessInputTransformer.wrap
532 532 def assign_from_system(line):
533 533 """Transform assignment from system commands (e.g. files = !ls)"""
534 534 m = assign_system_re.match(line)
535 535 if m is None:
536 536 return line
537 537
538 538 return assign_system_template % m.group('lhs', 'cmd')
539 539
540 540 assign_magic_re = re.compile(r'{}%\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
541 541 assign_magic_template = '%s = get_ipython().magic(%r)'
542 542 @StatelessInputTransformer.wrap
543 543 def assign_from_magic(line):
544 544 """Transform assignment from magic commands (e.g. a = %who_ls)"""
545 545 m = assign_magic_re.match(line)
546 546 if m is None:
547 547 return line
548 548
549 549 return assign_magic_template % m.group('lhs', 'cmd')
@@ -1,497 +1,502 b''
1 1 import tokenize
2 2 import nose.tools as nt
3 3
4 4 from IPython.testing import tools as tt
5 5 from IPython.utils import py3compat
6 6 u_fmt = py3compat.u_format
7 7
8 8 from IPython.core import inputtransformer as ipt
9 9
10 10 def transform_and_reset(transformer):
11 11 transformer = transformer()
12 12 def transform(inp):
13 13 try:
14 14 return transformer.push(inp)
15 15 finally:
16 16 transformer.reset()
17 17
18 18 return transform
19 19
20 20 # Transformer tests
21 21 def transform_checker(tests, transformer, **kwargs):
22 22 """Utility to loop over test inputs"""
23 23 transformer = transformer(**kwargs)
24 24 try:
25 25 for inp, tr in tests:
26 26 if inp is None:
27 27 out = transformer.reset()
28 28 else:
29 29 out = transformer.push(inp)
30 30 nt.assert_equal(out, tr)
31 31 finally:
32 32 transformer.reset()
33 33
34 34 # Data for all the syntax tests in the form of lists of pairs of
35 35 # raw/transformed input. We store it here as a global dict so that we can use
36 36 # it both within single-function tests and also to validate the behavior of the
37 37 # larger objects
38 38
39 39 syntax = \
40 40 dict(assign_system =
41 41 [(i,py3compat.u_format(o)) for i,o in \
42 42 [(u'a =! ls', "a = get_ipython().getoutput({u}'ls')"),
43 43 (u'b = !ls', "b = get_ipython().getoutput({u}'ls')"),
44 44 (u'c= !ls', "c = get_ipython().getoutput({u}'ls')"),
45 45 (u'd == !ls', u'd == !ls'), # Invalid syntax, but we leave == alone.
46 46 ('x=1', 'x=1'), # normal input is unmodified
47 47 (' ',' '), # blank lines are kept intact
48 48 # Tuple unpacking
49 49 (u"a, b = !echo 'a\\nb'", u"a, b = get_ipython().getoutput({u}\"echo 'a\\\\nb'\")"),
50 50 (u"a,= !echo 'a'", u"a, = get_ipython().getoutput({u}\"echo 'a'\")"),
51 51 (u"a, *bc = !echo 'a\\nb\\nc'", u"a, *bc = get_ipython().getoutput({u}\"echo 'a\\\\nb\\\\nc'\")"),
52 52 # Tuple unpacking with regular Python expressions, not our syntax.
53 53 (u"a, b = range(2)", u"a, b = range(2)"),
54 54 (u"a, = range(1)", u"a, = range(1)"),
55 55 (u"a, *bc = range(3)", u"a, *bc = range(3)"),
56 56 ]],
57 57
58 58 assign_magic =
59 59 [(i,py3compat.u_format(o)) for i,o in \
60 60 [(u'a =% who', "a = get_ipython().magic({u}'who')"),
61 61 (u'b = %who', "b = get_ipython().magic({u}'who')"),
62 62 (u'c= %ls', "c = get_ipython().magic({u}'ls')"),
63 63 (u'd == %ls', u'd == %ls'), # Invalid syntax, but we leave == alone.
64 64 ('x=1', 'x=1'), # normal input is unmodified
65 65 (' ',' '), # blank lines are kept intact
66 66 (u"a, b = %foo", u"a, b = get_ipython().magic({u}'foo')"),
67 67 ]],
68 68
69 69 classic_prompt =
70 70 [('>>> x=1', 'x=1'),
71 71 ('x=1', 'x=1'), # normal input is unmodified
72 72 (' ', ' '), # blank lines are kept intact
73 73 ],
74 74
75 75 ipy_prompt =
76 76 [('In [1]: x=1', 'x=1'),
77 77 ('x=1', 'x=1'), # normal input is unmodified
78 78 (' ',' '), # blank lines are kept intact
79 79 ],
80 80
81 81 strip_encoding_cookie =
82 82 [
83 83 ('# -*- encoding: utf-8 -*-', ''),
84 84 ('# coding: latin-1', ''),
85 85 ],
86 86
87 87
88 88 # Tests for the escape transformer to leave normal code alone
89 89 escaped_noesc =
90 90 [ (' ', ' '),
91 91 ('x=1', 'x=1'),
92 92 ],
93 93
94 94 # System calls
95 95 escaped_shell =
96 96 [(i,py3compat.u_format(o)) for i,o in \
97 97 [ (u'!ls', "get_ipython().system({u}'ls')"),
98 98 # Double-escape shell, this means to capture the output of the
99 99 # subprocess and return it
100 100 (u'!!ls', "get_ipython().getoutput({u}'ls')"),
101 101 ]],
102 102
103 103 # Help/object info
104 104 escaped_help =
105 105 [(i,py3compat.u_format(o)) for i,o in \
106 106 [ (u'?', 'get_ipython().show_usage()'),
107 107 (u'?x1', "get_ipython().magic({u}'pinfo x1')"),
108 108 (u'??x2', "get_ipython().magic({u}'pinfo2 x2')"),
109 109 (u'?a.*s', "get_ipython().magic({u}'psearch a.*s')"),
110 110 (u'?%hist1', "get_ipython().magic({u}'pinfo %hist1')"),
111 111 (u'?%%hist2', "get_ipython().magic({u}'pinfo %%hist2')"),
112 112 (u'?abc = qwe', "get_ipython().magic({u}'pinfo abc')"),
113 113 ]],
114 114
115 115 end_help =
116 116 [(i,py3compat.u_format(o)) for i,o in \
117 117 [ (u'x3?', "get_ipython().magic({u}'pinfo x3')"),
118 118 (u'x4??', "get_ipython().magic({u}'pinfo2 x4')"),
119 119 (u'%hist1?', "get_ipython().magic({u}'pinfo %hist1')"),
120 120 (u'%hist2??', "get_ipython().magic({u}'pinfo2 %hist2')"),
121 121 (u'%%hist3?', "get_ipython().magic({u}'pinfo %%hist3')"),
122 122 (u'%%hist4??', "get_ipython().magic({u}'pinfo2 %%hist4')"),
123 123 (u'f*?', "get_ipython().magic({u}'psearch f*')"),
124 124 (u'ax.*aspe*?', "get_ipython().magic({u}'psearch ax.*aspe*')"),
125 125 (u'a = abc?', "get_ipython().set_next_input({u}'a = abc');"
126 126 "get_ipython().magic({u}'pinfo abc')"),
127 127 (u'a = abc.qe??', "get_ipython().set_next_input({u}'a = abc.qe');"
128 128 "get_ipython().magic({u}'pinfo2 abc.qe')"),
129 129 (u'a = *.items?', "get_ipython().set_next_input({u}'a = *.items');"
130 130 "get_ipython().magic({u}'psearch *.items')"),
131 131 (u'plot(a?', "get_ipython().set_next_input({u}'plot(a');"
132 132 "get_ipython().magic({u}'pinfo a')"),
133 133 (u'a*2 #comment?', 'a*2 #comment?'),
134 134 ]],
135 135
136 136 # Explicit magic calls
137 137 escaped_magic =
138 138 [(i,py3compat.u_format(o)) for i,o in \
139 139 [ (u'%cd', "get_ipython().magic({u}'cd')"),
140 140 (u'%cd /home', "get_ipython().magic({u}'cd /home')"),
141 141 # Backslashes need to be escaped.
142 142 (u'%cd C:\\User', "get_ipython().magic({u}'cd C:\\\\User')"),
143 143 (u' %magic', " get_ipython().magic({u}'magic')"),
144 144 ]],
145 145
146 146 # Quoting with separate arguments
147 147 escaped_quote =
148 148 [ (',f', 'f("")'),
149 149 (',f x', 'f("x")'),
150 150 (' ,f y', ' f("y")'),
151 151 (',f a b', 'f("a", "b")'),
152 152 ],
153 153
154 154 # Quoting with single argument
155 155 escaped_quote2 =
156 156 [ (';f', 'f("")'),
157 157 (';f x', 'f("x")'),
158 158 (' ;f y', ' f("y")'),
159 159 (';f a b', 'f("a b")'),
160 160 ],
161 161
162 162 # Simply apply parens
163 163 escaped_paren =
164 164 [ ('/f', 'f()'),
165 165 ('/f x', 'f(x)'),
166 166 (' /f y', ' f(y)'),
167 167 ('/f a b', 'f(a, b)'),
168 168 ],
169 169
170 170 # Check that we transform prompts before other transforms
171 171 mixed =
172 172 [(i,py3compat.u_format(o)) for i,o in \
173 173 [ (u'In [1]: %lsmagic', "get_ipython().magic({u}'lsmagic')"),
174 174 (u'>>> %lsmagic', "get_ipython().magic({u}'lsmagic')"),
175 175 (u'In [2]: !ls', "get_ipython().system({u}'ls')"),
176 176 (u'In [3]: abs?', "get_ipython().magic({u}'pinfo abs')"),
177 177 (u'In [4]: b = %who', "b = get_ipython().magic({u}'who')"),
178 178 ]],
179 179 )
180 180
181 181 # multiline syntax examples. Each of these should be a list of lists, with
182 182 # each entry itself having pairs of raw/transformed input. The union (with
183 183 # '\n'.join() of the transformed inputs is what the splitter should produce
184 184 # when fed the raw lines one at a time via push.
185 185 syntax_ml = \
186 186 dict(classic_prompt =
187 187 [ [('>>> for i in range(10):','for i in range(10):'),
188 188 ('... print i',' print i'),
189 189 ('... ', ''),
190 190 ],
191 191 [('>>> a="""','a="""'),
192 192 ('... 123"""','123"""'),
193 193 ],
194 194 [('a="""','a="""'),
195 195 ('... 123','123'),
196 196 ('... 456"""','456"""'),
197 197 ],
198 198 [('a="""','a="""'),
199 199 ('>>> 123','123'),
200 200 ('... 456"""','456"""'),
201 201 ],
202 202 [('a="""','a="""'),
203 203 ('123','123'),
204 204 ('... 456"""','... 456"""'),
205 205 ],
206 206 [('....__class__','....__class__'),
207 207 ],
208 208 [('a=5', 'a=5'),
209 209 ('...', ''),
210 210 ],
211 211 [('>>> def f(x):', 'def f(x):'),
212 212 ('...', ''),
213 213 ('... return x', ' return x'),
214 214 ],
215 215 [('board = """....', 'board = """....'),
216 216 ('....', '....'),
217 217 ('...."""', '...."""'),
218 218 ],
219 219 ],
220 220
221 221 ipy_prompt =
222 222 [ [('In [24]: for i in range(10):','for i in range(10):'),
223 223 (' ....: print i',' print i'),
224 224 (' ....: ', ''),
225 225 ],
226 226 [('In [24]: for i in range(10):','for i in range(10):'),
227 227 # Qt console prompts expand with spaces, not dots
228 228 (' ...: print i',' print i'),
229 229 (' ...: ', ''),
230 230 ],
231 231 [('In [24]: for i in range(10):','for i in range(10):'),
232 232 # Sometimes whitespace preceding '...' has been removed (issue #6674)
233 233 ('...: print i',' print i'),
234 234 ('...: ', ''),
235 235 ],
236 [('In [24]: for i in range(10):','for i in range(10):'),
237 # Space after last continuation prompt has been removed (issue #6674)
238 ('...: print i',' print i'),
239 ('...:', ''),
240 ],
236 241 [('In [2]: a="""','a="""'),
237 242 (' ...: 123"""','123"""'),
238 243 ],
239 244 [('a="""','a="""'),
240 245 (' ...: 123','123'),
241 246 (' ...: 456"""','456"""'),
242 247 ],
243 248 [('a="""','a="""'),
244 249 ('In [1]: 123','123'),
245 250 (' ...: 456"""','456"""'),
246 251 ],
247 252 [('a="""','a="""'),
248 253 ('123','123'),
249 254 (' ...: 456"""',' ...: 456"""'),
250 255 ],
251 256 ],
252 257
253 258 strip_encoding_cookie =
254 259 [
255 260 [
256 261 ('# -*- coding: utf-8 -*-', ''),
257 262 ('foo', 'foo'),
258 263 ],
259 264 [
260 265 ('#!/usr/bin/env python', '#!/usr/bin/env python'),
261 266 ('# -*- coding: latin-1 -*-', ''),
262 267 # only the first-two lines
263 268 ('# -*- coding: latin-1 -*-', '# -*- coding: latin-1 -*-'),
264 269 ],
265 270 ],
266 271
267 272 multiline_datastructure_prompt =
268 273 [ [('>>> a = [1,','a = [1,'),
269 274 ('... 2]','2]'),
270 275 ],
271 276 ],
272 277
273 278 multiline_datastructure =
274 279 [ [('b = ("%s"', None),
275 280 ('# comment', None),
276 281 ('%foo )', 'b = ("%s"\n# comment\n%foo )'),
277 282 ],
278 283 ],
279 284
280 285 multiline_string =
281 286 [ [("'''foo?", None),
282 287 ("bar'''", "'''foo?\nbar'''"),
283 288 ],
284 289 ],
285 290
286 291 leading_indent =
287 292 [ [(' print "hi"','print "hi"'),
288 293 ],
289 294 [(' for a in range(5):','for a in range(5):'),
290 295 (' a*2',' a*2'),
291 296 ],
292 297 [(' a="""','a="""'),
293 298 (' 123"""','123"""'),
294 299 ],
295 300 [('a="""','a="""'),
296 301 (' 123"""',' 123"""'),
297 302 ],
298 303 ],
299 304
300 305 cellmagic =
301 306 [ [(u'%%foo a', None),
302 307 (None, u_fmt("get_ipython().run_cell_magic({u}'foo', {u}'a', {u}'')")),
303 308 ],
304 309 [(u'%%bar 123', None),
305 310 (u'hello', None),
306 311 (None , u_fmt("get_ipython().run_cell_magic({u}'bar', {u}'123', {u}'hello')")),
307 312 ],
308 313 [(u'a=5', 'a=5'),
309 314 (u'%%cellmagic', '%%cellmagic'),
310 315 ],
311 316 ],
312 317
313 318 escaped =
314 319 [ [('%abc def \\', None),
315 320 ('ghi', u_fmt("get_ipython().magic({u}'abc def ghi')")),
316 321 ],
317 322 [('%abc def \\', None),
318 323 ('ghi\\', None),
319 324 (None, u_fmt("get_ipython().magic({u}'abc def ghi')")),
320 325 ],
321 326 ],
322 327
323 328 assign_magic =
324 329 [ [(u'a = %bc de \\', None),
325 330 (u'fg', u_fmt("a = get_ipython().magic({u}'bc de fg')")),
326 331 ],
327 332 [(u'a = %bc de \\', None),
328 333 (u'fg\\', None),
329 334 (None, u_fmt("a = get_ipython().magic({u}'bc de fg')")),
330 335 ],
331 336 ],
332 337
333 338 assign_system =
334 339 [ [(u'a = !bc de \\', None),
335 340 (u'fg', u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
336 341 ],
337 342 [(u'a = !bc de \\', None),
338 343 (u'fg\\', None),
339 344 (None, u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
340 345 ],
341 346 ],
342 347 )
343 348
344 349
345 350 def test_assign_system():
346 351 tt.check_pairs(transform_and_reset(ipt.assign_from_system), syntax['assign_system'])
347 352
348 353 def test_assign_magic():
349 354 tt.check_pairs(transform_and_reset(ipt.assign_from_magic), syntax['assign_magic'])
350 355
351 356 def test_classic_prompt():
352 357 tt.check_pairs(transform_and_reset(ipt.classic_prompt), syntax['classic_prompt'])
353 358 for example in syntax_ml['classic_prompt']:
354 359 transform_checker(example, ipt.classic_prompt)
355 360 for example in syntax_ml['multiline_datastructure_prompt']:
356 361 transform_checker(example, ipt.classic_prompt)
357 362
358 363
359 364 def test_ipy_prompt():
360 365 tt.check_pairs(transform_and_reset(ipt.ipy_prompt), syntax['ipy_prompt'])
361 366 for example in syntax_ml['ipy_prompt']:
362 367 transform_checker(example, ipt.ipy_prompt)
363 368
364 369 def test_coding_cookie():
365 370 tt.check_pairs(transform_and_reset(ipt.strip_encoding_cookie), syntax['strip_encoding_cookie'])
366 371 for example in syntax_ml['strip_encoding_cookie']:
367 372 transform_checker(example, ipt.strip_encoding_cookie)
368 373
369 374 def test_assemble_logical_lines():
370 375 tests = \
371 376 [ [(u"a = \\", None),
372 377 (u"123", u"a = 123"),
373 378 ],
374 379 [(u"a = \\", None), # Test resetting when within a multi-line string
375 380 (u"12 *\\", None),
376 381 (None, u"a = 12 *"),
377 382 ],
378 383 [(u"# foo\\", u"# foo\\"), # Comments can't be continued like this
379 384 ],
380 385 ]
381 386 for example in tests:
382 387 transform_checker(example, ipt.assemble_logical_lines)
383 388
384 389 def test_assemble_python_lines():
385 390 tests = \
386 391 [ [(u"a = '''", None),
387 392 (u"abc'''", u"a = '''\nabc'''"),
388 393 ],
389 394 [(u"a = '''", None), # Test resetting when within a multi-line string
390 395 (u"def", None),
391 396 (None, u"a = '''\ndef"),
392 397 ],
393 398 [(u"a = [1,", None),
394 399 (u"2]", u"a = [1,\n2]"),
395 400 ],
396 401 [(u"a = [1,", None), # Test resetting when within a multi-line string
397 402 (u"2,", None),
398 403 (None, u"a = [1,\n2,"),
399 404 ],
400 405 ] + syntax_ml['multiline_datastructure']
401 406 for example in tests:
402 407 transform_checker(example, ipt.assemble_python_lines)
403 408
404 409
405 410 def test_help_end():
406 411 tt.check_pairs(transform_and_reset(ipt.help_end), syntax['end_help'])
407 412
408 413 def test_escaped_noesc():
409 414 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_noesc'])
410 415
411 416
412 417 def test_escaped_shell():
413 418 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_shell'])
414 419
415 420
416 421 def test_escaped_help():
417 422 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_help'])
418 423
419 424
420 425 def test_escaped_magic():
421 426 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_magic'])
422 427
423 428
424 429 def test_escaped_quote():
425 430 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_quote'])
426 431
427 432
428 433 def test_escaped_quote2():
429 434 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_quote2'])
430 435
431 436
432 437 def test_escaped_paren():
433 438 tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_paren'])
434 439
435 440
436 441 def test_cellmagic():
437 442 for example in syntax_ml['cellmagic']:
438 443 transform_checker(example, ipt.cellmagic)
439 444
440 445 line_example = [(u'%%bar 123', None),
441 446 (u'hello', None),
442 447 (u'' , u_fmt("get_ipython().run_cell_magic({u}'bar', {u}'123', {u}'hello')")),
443 448 ]
444 449 transform_checker(line_example, ipt.cellmagic, end_on_blank_line=True)
445 450
446 451 def test_has_comment():
447 452 tests = [('text', False),
448 453 ('text #comment', True),
449 454 ('text #comment\n', True),
450 455 ('#comment', True),
451 456 ('#comment\n', True),
452 457 ('a = "#string"', False),
453 458 ('a = "#string" # comment', True),
454 459 ('a #comment not "string"', True),
455 460 ]
456 461 tt.check_pairs(ipt.has_comment, tests)
457 462
458 463 @ipt.TokenInputTransformer.wrap
459 464 def decistmt(tokens):
460 465 """Substitute Decimals for floats in a string of statements.
461 466
462 467 Based on an example from the tokenize module docs.
463 468 """
464 469 result = []
465 470 for toknum, tokval, _, _, _ in tokens:
466 471 if toknum == tokenize.NUMBER and '.' in tokval: # replace NUMBER tokens
467 472 for newtok in [
468 473 (tokenize.NAME, 'Decimal'),
469 474 (tokenize.OP, '('),
470 475 (tokenize.STRING, repr(tokval)),
471 476 (tokenize.OP, ')')
472 477 ]:
473 478 yield newtok
474 479 else:
475 480 yield (toknum, tokval)
476 481
477 482
478 483
479 484 def test_token_input_transformer():
480 485 tests = [(u'1.2', u_fmt(u"Decimal ({u}'1.2')")),
481 486 (u'"1.2"', u'"1.2"'),
482 487 ]
483 488 tt.check_pairs(transform_and_reset(decistmt), tests)
484 489 ml_tests = \
485 490 [ [(u"a = 1.2; b = '''x", None),
486 491 (u"y'''", u_fmt(u"a =Decimal ({u}'1.2');b ='''x\ny'''")),
487 492 ],
488 493 [(u"a = [1.2,", None),
489 494 (u"3]", u_fmt(u"a =[Decimal ({u}'1.2'),\n3 ]")),
490 495 ],
491 496 [(u"a = '''foo", None), # Test resetting when within a multi-line string
492 497 (u"bar", None),
493 498 (None, u"a = '''foo\nbar"),
494 499 ],
495 500 ]
496 501 for example in ml_tests:
497 502 transform_checker(example, decistmt)
General Comments 0
You need to be logged in to leave comments. Login now