##// END OF EJS Templates
Only allow '>>>' prompt without space afterwards if line is blank...
Thomas Kluyver -
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 prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)')
457 initial_re = re.compile(r'^(>>> ?)')
456 prompt_re = re.compile(r'^(>>>|\.\.\.)( |$)')
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 464 prompt_re = re.compile(r'^(In \[\d+\]: |\ {3,}\.{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,585 +1,585 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the inputsplitter module.
3 3
4 4 Authors
5 5 -------
6 6 * Fernando Perez
7 7 * Robert Kern
8 8 """
9 9 from __future__ import print_function
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (C) 2010-2011 The IPython Development Team
12 12 #
13 13 # Distributed under the terms of the BSD License. The full license is in
14 14 # the file COPYING, distributed as part of this software.
15 15 #-----------------------------------------------------------------------------
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Imports
19 19 #-----------------------------------------------------------------------------
20 20 # stdlib
21 21 import unittest
22 22 import sys
23 23
24 24 # Third party
25 25 import nose.tools as nt
26 26
27 27 # Our own
28 28 from IPython.core import inputsplitter as isp
29 29 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
30 30 from IPython.testing import tools as tt
31 31 from IPython.utils import py3compat
32 32 from IPython.utils.py3compat import string_types, input
33 33
34 34 #-----------------------------------------------------------------------------
35 35 # Semi-complete examples (also used as tests)
36 36 #-----------------------------------------------------------------------------
37 37
38 38 # Note: at the bottom, there's a slightly more complete version of this that
39 39 # can be useful during development of code here.
40 40
41 41 def mini_interactive_loop(input_func):
42 42 """Minimal example of the logic of an interactive interpreter loop.
43 43
44 44 This serves as an example, and it is used by the test system with a fake
45 45 raw_input that simulates interactive input."""
46 46
47 47 from IPython.core.inputsplitter import InputSplitter
48 48
49 49 isp = InputSplitter()
50 50 # In practice, this input loop would be wrapped in an outside loop to read
51 51 # input indefinitely, until some exit/quit command was issued. Here we
52 52 # only illustrate the basic inner loop.
53 53 while isp.push_accepts_more():
54 54 indent = ' '*isp.indent_spaces
55 55 prompt = '>>> ' + indent
56 56 line = indent + input_func(prompt)
57 57 isp.push(line)
58 58
59 59 # Here we just return input so we can use it in a test suite, but a real
60 60 # interpreter would instead send it for execution somewhere.
61 61 src = isp.source_reset()
62 62 #print 'Input source was:\n', src # dbg
63 63 return src
64 64
65 65 #-----------------------------------------------------------------------------
66 66 # Test utilities, just for local use
67 67 #-----------------------------------------------------------------------------
68 68
69 69 def assemble(block):
70 70 """Assemble a block into multi-line sub-blocks."""
71 71 return ['\n'.join(sub_block)+'\n' for sub_block in block]
72 72
73 73
74 74 def pseudo_input(lines):
75 75 """Return a function that acts like raw_input but feeds the input list."""
76 76 ilines = iter(lines)
77 77 def raw_in(prompt):
78 78 try:
79 79 return next(ilines)
80 80 except StopIteration:
81 81 return ''
82 82 return raw_in
83 83
84 84 #-----------------------------------------------------------------------------
85 85 # Tests
86 86 #-----------------------------------------------------------------------------
87 87 def test_spaces():
88 88 tests = [('', 0),
89 89 (' ', 1),
90 90 ('\n', 0),
91 91 (' \n', 1),
92 92 ('x', 0),
93 93 (' x', 1),
94 94 (' x',2),
95 95 (' x',4),
96 96 # Note: tabs are counted as a single whitespace!
97 97 ('\tx', 1),
98 98 ('\t x', 2),
99 99 ]
100 100 tt.check_pairs(isp.num_ini_spaces, tests)
101 101
102 102
103 103 def test_remove_comments():
104 104 tests = [('text', 'text'),
105 105 ('text # comment', 'text '),
106 106 ('text # comment\n', 'text \n'),
107 107 ('text # comment \n', 'text \n'),
108 108 ('line # c \nline\n','line \nline\n'),
109 109 ('line # c \nline#c2 \nline\nline #c\n\n',
110 110 'line \nline\nline\nline \n\n'),
111 111 ]
112 112 tt.check_pairs(isp.remove_comments, tests)
113 113
114 114
115 115 def test_get_input_encoding():
116 116 encoding = isp.get_input_encoding()
117 117 nt.assert_true(isinstance(encoding, string_types))
118 118 # simple-minded check that at least encoding a simple string works with the
119 119 # encoding we got.
120 120 nt.assert_equal(u'test'.encode(encoding), b'test')
121 121
122 122
123 123 class NoInputEncodingTestCase(unittest.TestCase):
124 124 def setUp(self):
125 125 self.old_stdin = sys.stdin
126 126 class X: pass
127 127 fake_stdin = X()
128 128 sys.stdin = fake_stdin
129 129
130 130 def test(self):
131 131 # Verify that if sys.stdin has no 'encoding' attribute we do the right
132 132 # thing
133 133 enc = isp.get_input_encoding()
134 134 self.assertEqual(enc, 'ascii')
135 135
136 136 def tearDown(self):
137 137 sys.stdin = self.old_stdin
138 138
139 139
140 140 class InputSplitterTestCase(unittest.TestCase):
141 141 def setUp(self):
142 142 self.isp = isp.InputSplitter()
143 143
144 144 def test_reset(self):
145 145 isp = self.isp
146 146 isp.push('x=1')
147 147 isp.reset()
148 148 self.assertEqual(isp._buffer, [])
149 149 self.assertEqual(isp.indent_spaces, 0)
150 150 self.assertEqual(isp.source, '')
151 151 self.assertEqual(isp.code, None)
152 152 self.assertEqual(isp._is_complete, False)
153 153
154 154 def test_source(self):
155 155 self.isp._store('1')
156 156 self.isp._store('2')
157 157 self.assertEqual(self.isp.source, '1\n2\n')
158 158 self.assertTrue(len(self.isp._buffer)>0)
159 159 self.assertEqual(self.isp.source_reset(), '1\n2\n')
160 160 self.assertEqual(self.isp._buffer, [])
161 161 self.assertEqual(self.isp.source, '')
162 162
163 163 def test_indent(self):
164 164 isp = self.isp # shorthand
165 165 isp.push('x=1')
166 166 self.assertEqual(isp.indent_spaces, 0)
167 167 isp.push('if 1:\n x=1')
168 168 self.assertEqual(isp.indent_spaces, 4)
169 169 isp.push('y=2\n')
170 170 self.assertEqual(isp.indent_spaces, 0)
171 171
172 172 def test_indent2(self):
173 173 isp = self.isp
174 174 isp.push('if 1:')
175 175 self.assertEqual(isp.indent_spaces, 4)
176 176 isp.push(' x=1')
177 177 self.assertEqual(isp.indent_spaces, 4)
178 178 # Blank lines shouldn't change the indent level
179 179 isp.push(' '*2)
180 180 self.assertEqual(isp.indent_spaces, 4)
181 181
182 182 def test_indent3(self):
183 183 isp = self.isp
184 184 # When a multiline statement contains parens or multiline strings, we
185 185 # shouldn't get confused.
186 186 isp.push("if 1:")
187 187 isp.push(" x = (1+\n 2)")
188 188 self.assertEqual(isp.indent_spaces, 4)
189 189
190 190 def test_indent4(self):
191 191 isp = self.isp
192 192 # whitespace after ':' should not screw up indent level
193 193 isp.push('if 1: \n x=1')
194 194 self.assertEqual(isp.indent_spaces, 4)
195 195 isp.push('y=2\n')
196 196 self.assertEqual(isp.indent_spaces, 0)
197 197 isp.push('if 1:\t\n x=1')
198 198 self.assertEqual(isp.indent_spaces, 4)
199 199 isp.push('y=2\n')
200 200 self.assertEqual(isp.indent_spaces, 0)
201 201
202 202 def test_dedent_pass(self):
203 203 isp = self.isp # shorthand
204 204 # should NOT cause dedent
205 205 isp.push('if 1:\n passes = 5')
206 206 self.assertEqual(isp.indent_spaces, 4)
207 207 isp.push('if 1:\n pass')
208 208 self.assertEqual(isp.indent_spaces, 0)
209 209 isp.push('if 1:\n pass ')
210 210 self.assertEqual(isp.indent_spaces, 0)
211 211
212 212 def test_dedent_break(self):
213 213 isp = self.isp # shorthand
214 214 # should NOT cause dedent
215 215 isp.push('while 1:\n breaks = 5')
216 216 self.assertEqual(isp.indent_spaces, 4)
217 217 isp.push('while 1:\n break')
218 218 self.assertEqual(isp.indent_spaces, 0)
219 219 isp.push('while 1:\n break ')
220 220 self.assertEqual(isp.indent_spaces, 0)
221 221
222 222 def test_dedent_continue(self):
223 223 isp = self.isp # shorthand
224 224 # should NOT cause dedent
225 225 isp.push('while 1:\n continues = 5')
226 226 self.assertEqual(isp.indent_spaces, 4)
227 227 isp.push('while 1:\n continue')
228 228 self.assertEqual(isp.indent_spaces, 0)
229 229 isp.push('while 1:\n continue ')
230 230 self.assertEqual(isp.indent_spaces, 0)
231 231
232 232 def test_dedent_raise(self):
233 233 isp = self.isp # shorthand
234 234 # should NOT cause dedent
235 235 isp.push('if 1:\n raised = 4')
236 236 self.assertEqual(isp.indent_spaces, 4)
237 237 isp.push('if 1:\n raise TypeError()')
238 238 self.assertEqual(isp.indent_spaces, 0)
239 239 isp.push('if 1:\n raise')
240 240 self.assertEqual(isp.indent_spaces, 0)
241 241 isp.push('if 1:\n raise ')
242 242 self.assertEqual(isp.indent_spaces, 0)
243 243
244 244 def test_dedent_return(self):
245 245 isp = self.isp # shorthand
246 246 # should NOT cause dedent
247 247 isp.push('if 1:\n returning = 4')
248 248 self.assertEqual(isp.indent_spaces, 4)
249 249 isp.push('if 1:\n return 5 + 493')
250 250 self.assertEqual(isp.indent_spaces, 0)
251 251 isp.push('if 1:\n return')
252 252 self.assertEqual(isp.indent_spaces, 0)
253 253 isp.push('if 1:\n return ')
254 254 self.assertEqual(isp.indent_spaces, 0)
255 255 isp.push('if 1:\n return(0)')
256 256 self.assertEqual(isp.indent_spaces, 0)
257 257
258 258 def test_push(self):
259 259 isp = self.isp
260 260 self.assertTrue(isp.push('x=1'))
261 261
262 262 def test_push2(self):
263 263 isp = self.isp
264 264 self.assertFalse(isp.push('if 1:'))
265 265 for line in [' x=1', '# a comment', ' y=2']:
266 266 print(line)
267 267 self.assertTrue(isp.push(line))
268 268
269 269 def test_push3(self):
270 270 isp = self.isp
271 271 isp.push('if True:')
272 272 isp.push(' a = 1')
273 273 self.assertFalse(isp.push('b = [1,'))
274 274
275 275 def test_push_accepts_more(self):
276 276 isp = self.isp
277 277 isp.push('x=1')
278 278 self.assertFalse(isp.push_accepts_more())
279 279
280 280 def test_push_accepts_more2(self):
281 281 isp = self.isp
282 282 isp.push('if 1:')
283 283 self.assertTrue(isp.push_accepts_more())
284 284 isp.push(' x=1')
285 285 self.assertTrue(isp.push_accepts_more())
286 286 isp.push('')
287 287 self.assertFalse(isp.push_accepts_more())
288 288
289 289 def test_push_accepts_more3(self):
290 290 isp = self.isp
291 291 isp.push("x = (2+\n3)")
292 292 self.assertFalse(isp.push_accepts_more())
293 293
294 294 def test_push_accepts_more4(self):
295 295 isp = self.isp
296 296 # When a multiline statement contains parens or multiline strings, we
297 297 # shouldn't get confused.
298 298 # FIXME: we should be able to better handle de-dents in statements like
299 299 # multiline strings and multiline expressions (continued with \ or
300 300 # parens). Right now we aren't handling the indentation tracking quite
301 301 # correctly with this, though in practice it may not be too much of a
302 302 # problem. We'll need to see.
303 303 isp.push("if 1:")
304 304 isp.push(" x = (2+")
305 305 isp.push(" 3)")
306 306 self.assertTrue(isp.push_accepts_more())
307 307 isp.push(" y = 3")
308 308 self.assertTrue(isp.push_accepts_more())
309 309 isp.push('')
310 310 self.assertFalse(isp.push_accepts_more())
311 311
312 312 def test_push_accepts_more5(self):
313 313 isp = self.isp
314 314 isp.push('try:')
315 315 isp.push(' a = 5')
316 316 isp.push('except:')
317 317 isp.push(' raise')
318 318 # We want to be able to add an else: block at this point, so it should
319 319 # wait for a blank line.
320 320 self.assertTrue(isp.push_accepts_more())
321 321
322 322 def test_continuation(self):
323 323 isp = self.isp
324 324 isp.push("import os, \\")
325 325 self.assertTrue(isp.push_accepts_more())
326 326 isp.push("sys")
327 327 self.assertFalse(isp.push_accepts_more())
328 328
329 329 def test_syntax_error(self):
330 330 isp = self.isp
331 331 # Syntax errors immediately produce a 'ready' block, so the invalid
332 332 # Python can be sent to the kernel for evaluation with possible ipython
333 333 # special-syntax conversion.
334 334 isp.push('run foo')
335 335 self.assertFalse(isp.push_accepts_more())
336 336
337 337 def test_unicode(self):
338 338 self.isp.push(u"PΓ©rez")
339 339 self.isp.push(u'\xc3\xa9')
340 340 self.isp.push(u"u'\xc3\xa9'")
341 341
342 342 def test_line_continuation(self):
343 343 """ Test issue #2108."""
344 344 isp = self.isp
345 345 # A blank line after a line continuation should not accept more
346 346 isp.push("1 \\\n\n")
347 347 self.assertFalse(isp.push_accepts_more())
348 348 # Whitespace after a \ is a SyntaxError. The only way to test that
349 349 # here is to test that push doesn't accept more (as with
350 350 # test_syntax_error() above).
351 351 isp.push(r"1 \ ")
352 352 self.assertFalse(isp.push_accepts_more())
353 353 # Even if the line is continuable (c.f. the regular Python
354 354 # interpreter)
355 355 isp.push(r"(1 \ ")
356 356 self.assertFalse(isp.push_accepts_more())
357 357
358 358 class InteractiveLoopTestCase(unittest.TestCase):
359 359 """Tests for an interactive loop like a python shell.
360 360 """
361 361 def check_ns(self, lines, ns):
362 362 """Validate that the given input lines produce the resulting namespace.
363 363
364 364 Note: the input lines are given exactly as they would be typed in an
365 365 auto-indenting environment, as mini_interactive_loop above already does
366 366 auto-indenting and prepends spaces to the input.
367 367 """
368 368 src = mini_interactive_loop(pseudo_input(lines))
369 369 test_ns = {}
370 370 exec(src, test_ns)
371 371 # We can't check that the provided ns is identical to the test_ns,
372 372 # because Python fills test_ns with extra keys (copyright, etc). But
373 373 # we can check that the given dict is *contained* in test_ns
374 374 for k,v in ns.items():
375 375 self.assertEqual(test_ns[k], v)
376 376
377 377 def test_simple(self):
378 378 self.check_ns(['x=1'], dict(x=1))
379 379
380 380 def test_simple2(self):
381 381 self.check_ns(['if 1:', 'x=2'], dict(x=2))
382 382
383 383 def test_xy(self):
384 384 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
385 385
386 386 def test_abc(self):
387 387 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
388 388
389 389 def test_multi(self):
390 390 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
391 391
392 392
393 393 class IPythonInputTestCase(InputSplitterTestCase):
394 394 """By just creating a new class whose .isp is a different instance, we
395 395 re-run the same test battery on the new input splitter.
396 396
397 397 In addition, this runs the tests over the syntax and syntax_ml dicts that
398 398 were tested by individual functions, as part of the OO interface.
399 399
400 400 It also makes some checks on the raw buffer storage.
401 401 """
402 402
403 403 def setUp(self):
404 404 self.isp = isp.IPythonInputSplitter()
405 405
406 406 def test_syntax(self):
407 407 """Call all single-line syntax tests from the main object"""
408 408 isp = self.isp
409 409 for example in syntax.values():
410 410 for raw, out_t in example:
411 411 if raw.startswith(' '):
412 412 continue
413 413
414 414 isp.push(raw+'\n')
415 415 out_raw = isp.source_raw
416 416 out = isp.source_reset()
417 417 self.assertEqual(out.rstrip(), out_t,
418 418 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
419 419 self.assertEqual(out_raw.rstrip(), raw.rstrip())
420 420
421 421 def test_syntax_multiline(self):
422 422 isp = self.isp
423 423 for example in syntax_ml.values():
424 424 for line_pairs in example:
425 425 out_t_parts = []
426 426 raw_parts = []
427 427 for lraw, out_t_part in line_pairs:
428 428 if out_t_part is not None:
429 429 out_t_parts.append(out_t_part)
430 430
431 431 if lraw is not None:
432 432 isp.push(lraw)
433 433 raw_parts.append(lraw)
434 434
435 435 out_raw = isp.source_raw
436 436 out = isp.source_reset()
437 437 out_t = '\n'.join(out_t_parts).rstrip()
438 438 raw = '\n'.join(raw_parts).rstrip()
439 439 self.assertEqual(out.rstrip(), out_t)
440 440 self.assertEqual(out_raw.rstrip(), raw)
441 441
442 442 def test_syntax_multiline_cell(self):
443 443 isp = self.isp
444 444 for example in syntax_ml.values():
445 445
446 446 out_t_parts = []
447 447 for line_pairs in example:
448 448 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
449 449 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
450 450 out = isp.transform_cell(raw)
451 451 # Match ignoring trailing whitespace
452 452 self.assertEqual(out.rstrip(), out_t.rstrip())
453 453
454 454 def test_cellmagic_preempt(self):
455 455 isp = self.isp
456 456 for raw, name, line, cell in [
457 457 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
458 ("%%cellm \nline\n>>>hi", u'cellm', u'', u'line\n>>>hi'),
459 (">>>%%cellm \nline\n>>>hi", u'cellm', u'', u'line\nhi'),
460 ("%%cellm \n>>>hi", u'cellm', u'', u'hi'),
458 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
459 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
460 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
461 461 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
462 462 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
463 463 ]:
464 464 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
465 465 name, line, cell
466 466 )
467 467 out = isp.transform_cell(raw)
468 468 self.assertEqual(out.rstrip(), expected.rstrip())
469 469
470 470
471 471
472 472 #-----------------------------------------------------------------------------
473 473 # Main - use as a script, mostly for developer experiments
474 474 #-----------------------------------------------------------------------------
475 475
476 476 if __name__ == '__main__':
477 477 # A simple demo for interactive experimentation. This code will not get
478 478 # picked up by any test suite.
479 479 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
480 480
481 481 # configure here the syntax to use, prompt and whether to autoindent
482 482 #isp, start_prompt = InputSplitter(), '>>> '
483 483 isp, start_prompt = IPythonInputSplitter(), 'In> '
484 484
485 485 autoindent = True
486 486 #autoindent = False
487 487
488 488 try:
489 489 while True:
490 490 prompt = start_prompt
491 491 while isp.push_accepts_more():
492 492 indent = ' '*isp.indent_spaces
493 493 if autoindent:
494 494 line = indent + input(prompt+indent)
495 495 else:
496 496 line = input(prompt)
497 497 isp.push(line)
498 498 prompt = '... '
499 499
500 500 # Here we just return input so we can use it in a test suite, but a
501 501 # real interpreter would instead send it for execution somewhere.
502 502 #src = isp.source; raise EOFError # dbg
503 503 raw = isp.source_raw
504 504 src = isp.source_reset()
505 505 print('Input source was:\n', src)
506 506 print('Raw source was:\n', raw)
507 507 except EOFError:
508 508 print('Bye')
509 509
510 510 # Tests for cell magics support
511 511
512 512 def test_last_blank():
513 513 nt.assert_false(isp.last_blank(''))
514 514 nt.assert_false(isp.last_blank('abc'))
515 515 nt.assert_false(isp.last_blank('abc\n'))
516 516 nt.assert_false(isp.last_blank('abc\na'))
517 517
518 518 nt.assert_true(isp.last_blank('\n'))
519 519 nt.assert_true(isp.last_blank('\n '))
520 520 nt.assert_true(isp.last_blank('abc\n '))
521 521 nt.assert_true(isp.last_blank('abc\n\n'))
522 522 nt.assert_true(isp.last_blank('abc\nd\n\n'))
523 523 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
524 524 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
525 525
526 526
527 527 def test_last_two_blanks():
528 528 nt.assert_false(isp.last_two_blanks(''))
529 529 nt.assert_false(isp.last_two_blanks('abc'))
530 530 nt.assert_false(isp.last_two_blanks('abc\n'))
531 531 nt.assert_false(isp.last_two_blanks('abc\n\na'))
532 532 nt.assert_false(isp.last_two_blanks('abc\n \n'))
533 533 nt.assert_false(isp.last_two_blanks('abc\n\n'))
534 534
535 535 nt.assert_true(isp.last_two_blanks('\n\n'))
536 536 nt.assert_true(isp.last_two_blanks('\n\n '))
537 537 nt.assert_true(isp.last_two_blanks('\n \n'))
538 538 nt.assert_true(isp.last_two_blanks('abc\n\n '))
539 539 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
540 540 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
541 541 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
542 542 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
543 543 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
544 544 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
545 545
546 546
547 547 class CellMagicsCommon(object):
548 548
549 549 def test_whole_cell(self):
550 550 src = "%%cellm line\nbody\n"
551 551 out = self.sp.transform_cell(src)
552 552 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
553 553 nt.assert_equal(out, py3compat.u_format(ref))
554 554
555 555 def test_cellmagic_help(self):
556 556 self.sp.push('%%cellm?')
557 557 nt.assert_false(self.sp.push_accepts_more())
558 558
559 559 def tearDown(self):
560 560 self.sp.reset()
561 561
562 562
563 563 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
564 564 sp = isp.IPythonInputSplitter(line_input_checker=False)
565 565
566 566 def test_incremental(self):
567 567 sp = self.sp
568 568 sp.push('%%cellm firstline\n')
569 569 nt.assert_true(sp.push_accepts_more()) #1
570 570 sp.push('line2\n')
571 571 nt.assert_true(sp.push_accepts_more()) #2
572 572 sp.push('\n')
573 573 # This should accept a blank line and carry on until the cell is reset
574 574 nt.assert_true(sp.push_accepts_more()) #3
575 575
576 576 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
577 577 sp = isp.IPythonInputSplitter(line_input_checker=True)
578 578
579 579 def test_incremental(self):
580 580 sp = self.sp
581 581 sp.push('%%cellm line2\n')
582 582 nt.assert_true(sp.push_accepts_more()) #1
583 583 sp.push('\n')
584 584 # In this case, a blank line should end the cell magic
585 585 nt.assert_false(sp.push_accepts_more()) #2
General Comments 0
You need to be logged in to leave comments. Login now