##// END OF EJS Templates
Only allow '>>>' prompt without space afterwards if line is blank...
Thomas Kluyver -
Show More
@@ -1,549 +1,549 b''
1 """Input transformer classes to support IPython special syntax.
1 """Input transformer classes to support IPython special syntax.
2
2
3 This includes the machinery to recognise and transform ``%magic`` commands,
3 This includes the machinery to recognise and transform ``%magic`` commands,
4 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
4 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
5 """
5 """
6 import abc
6 import abc
7 import functools
7 import functools
8 import re
8 import re
9
9
10 from IPython.core.splitinput import LineInfo
10 from IPython.core.splitinput import LineInfo
11 from IPython.utils import tokenize2
11 from IPython.utils import tokenize2
12 from IPython.utils.openpy import cookie_comment_re
12 from IPython.utils.openpy import cookie_comment_re
13 from IPython.utils.py3compat import with_metaclass, PY3
13 from IPython.utils.py3compat import with_metaclass, PY3
14 from IPython.utils.tokenize2 import generate_tokens, untokenize, TokenError
14 from IPython.utils.tokenize2 import generate_tokens, untokenize, TokenError
15
15
16 if PY3:
16 if PY3:
17 from io import StringIO
17 from io import StringIO
18 else:
18 else:
19 from StringIO import StringIO
19 from StringIO import StringIO
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Globals
22 # Globals
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 # The escape sequences that define the syntax transformations IPython will
25 # The escape sequences that define the syntax transformations IPython will
26 # apply to user input. These can NOT be just changed here: many regular
26 # apply to user input. These can NOT be just changed here: many regular
27 # expressions and other parts of the code may use their hardcoded values, and
27 # expressions and other parts of the code may use their hardcoded values, and
28 # for all intents and purposes they constitute the 'IPython syntax', so they
28 # for all intents and purposes they constitute the 'IPython syntax', so they
29 # should be considered fixed.
29 # should be considered fixed.
30
30
31 ESC_SHELL = '!' # Send line to underlying system shell
31 ESC_SHELL = '!' # Send line to underlying system shell
32 ESC_SH_CAP = '!!' # Send line to system shell and capture output
32 ESC_SH_CAP = '!!' # Send line to system shell and capture output
33 ESC_HELP = '?' # Find information about object
33 ESC_HELP = '?' # Find information about object
34 ESC_HELP2 = '??' # Find extra-detailed information about object
34 ESC_HELP2 = '??' # Find extra-detailed information about object
35 ESC_MAGIC = '%' # Call magic function
35 ESC_MAGIC = '%' # Call magic function
36 ESC_MAGIC2 = '%%' # Call cell-magic function
36 ESC_MAGIC2 = '%%' # Call cell-magic function
37 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
37 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
38 ESC_QUOTE2 = ';' # Quote all args as a single string, call
38 ESC_QUOTE2 = ';' # Quote all args as a single string, call
39 ESC_PAREN = '/' # Call first argument with rest of line as arguments
39 ESC_PAREN = '/' # Call first argument with rest of line as arguments
40
40
41 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
41 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
42 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
42 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
43 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
43 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
44
44
45
45
46 class InputTransformer(with_metaclass(abc.ABCMeta, object)):
46 class InputTransformer(with_metaclass(abc.ABCMeta, object)):
47 """Abstract base class for line-based input transformers."""
47 """Abstract base class for line-based input transformers."""
48
48
49 @abc.abstractmethod
49 @abc.abstractmethod
50 def push(self, line):
50 def push(self, line):
51 """Send a line of input to the transformer, returning the transformed
51 """Send a line of input to the transformer, returning the transformed
52 input or None if the transformer is waiting for more input.
52 input or None if the transformer is waiting for more input.
53
53
54 Must be overridden by subclasses.
54 Must be overridden by subclasses.
55
55
56 Implementations may raise ``SyntaxError`` if the input is invalid. No
56 Implementations may raise ``SyntaxError`` if the input is invalid. No
57 other exceptions may be raised.
57 other exceptions may be raised.
58 """
58 """
59 pass
59 pass
60
60
61 @abc.abstractmethod
61 @abc.abstractmethod
62 def reset(self):
62 def reset(self):
63 """Return, transformed any lines that the transformer has accumulated,
63 """Return, transformed any lines that the transformer has accumulated,
64 and reset its internal state.
64 and reset its internal state.
65
65
66 Must be overridden by subclasses.
66 Must be overridden by subclasses.
67 """
67 """
68 pass
68 pass
69
69
70 @classmethod
70 @classmethod
71 def wrap(cls, func):
71 def wrap(cls, func):
72 """Can be used by subclasses as a decorator, to return a factory that
72 """Can be used by subclasses as a decorator, to return a factory that
73 will allow instantiation with the decorated object.
73 will allow instantiation with the decorated object.
74 """
74 """
75 @functools.wraps(func)
75 @functools.wraps(func)
76 def transformer_factory(**kwargs):
76 def transformer_factory(**kwargs):
77 return cls(func, **kwargs)
77 return cls(func, **kwargs)
78
78
79 return transformer_factory
79 return transformer_factory
80
80
81 class StatelessInputTransformer(InputTransformer):
81 class StatelessInputTransformer(InputTransformer):
82 """Wrapper for a stateless input transformer implemented as a function."""
82 """Wrapper for a stateless input transformer implemented as a function."""
83 def __init__(self, func):
83 def __init__(self, func):
84 self.func = func
84 self.func = func
85
85
86 def __repr__(self):
86 def __repr__(self):
87 return "StatelessInputTransformer(func={0!r})".format(self.func)
87 return "StatelessInputTransformer(func={0!r})".format(self.func)
88
88
89 def push(self, line):
89 def push(self, line):
90 """Send a line of input to the transformer, returning the
90 """Send a line of input to the transformer, returning the
91 transformed input."""
91 transformed input."""
92 return self.func(line)
92 return self.func(line)
93
93
94 def reset(self):
94 def reset(self):
95 """No-op - exists for compatibility."""
95 """No-op - exists for compatibility."""
96 pass
96 pass
97
97
98 class CoroutineInputTransformer(InputTransformer):
98 class CoroutineInputTransformer(InputTransformer):
99 """Wrapper for an input transformer implemented as a coroutine."""
99 """Wrapper for an input transformer implemented as a coroutine."""
100 def __init__(self, coro, **kwargs):
100 def __init__(self, coro, **kwargs):
101 # Prime it
101 # Prime it
102 self.coro = coro(**kwargs)
102 self.coro = coro(**kwargs)
103 next(self.coro)
103 next(self.coro)
104
104
105 def __repr__(self):
105 def __repr__(self):
106 return "CoroutineInputTransformer(coro={0!r})".format(self.coro)
106 return "CoroutineInputTransformer(coro={0!r})".format(self.coro)
107
107
108 def push(self, line):
108 def push(self, line):
109 """Send a line of input to the transformer, returning the
109 """Send a line of input to the transformer, returning the
110 transformed input or None if the transformer is waiting for more
110 transformed input or None if the transformer is waiting for more
111 input.
111 input.
112 """
112 """
113 return self.coro.send(line)
113 return self.coro.send(line)
114
114
115 def reset(self):
115 def reset(self):
116 """Return, transformed any lines that the transformer has
116 """Return, transformed any lines that the transformer has
117 accumulated, and reset its internal state.
117 accumulated, and reset its internal state.
118 """
118 """
119 return self.coro.send(None)
119 return self.coro.send(None)
120
120
121 class TokenInputTransformer(InputTransformer):
121 class TokenInputTransformer(InputTransformer):
122 """Wrapper for a token-based input transformer.
122 """Wrapper for a token-based input transformer.
123
123
124 func should accept a list of tokens (5-tuples, see tokenize docs), and
124 func should accept a list of tokens (5-tuples, see tokenize docs), and
125 return an iterable which can be passed to tokenize.untokenize().
125 return an iterable which can be passed to tokenize.untokenize().
126 """
126 """
127 def __init__(self, func):
127 def __init__(self, func):
128 self.func = func
128 self.func = func
129 self.current_line = ""
129 self.current_line = ""
130 self.line_used = False
130 self.line_used = False
131 self.reset_tokenizer()
131 self.reset_tokenizer()
132
132
133 def reset_tokenizer(self):
133 def reset_tokenizer(self):
134 self.tokenizer = generate_tokens(self.get_line)
134 self.tokenizer = generate_tokens(self.get_line)
135
135
136 def get_line(self):
136 def get_line(self):
137 if self.line_used:
137 if self.line_used:
138 raise TokenError
138 raise TokenError
139 self.line_used = True
139 self.line_used = True
140 return self.current_line
140 return self.current_line
141
141
142 def push(self, line):
142 def push(self, line):
143 self.current_line += line + "\n"
143 self.current_line += line + "\n"
144 if self.current_line.isspace():
144 if self.current_line.isspace():
145 return self.reset()
145 return self.reset()
146
146
147 self.line_used = False
147 self.line_used = False
148 tokens = []
148 tokens = []
149 stop_at_NL = False
149 stop_at_NL = False
150 try:
150 try:
151 for intok in self.tokenizer:
151 for intok in self.tokenizer:
152 tokens.append(intok)
152 tokens.append(intok)
153 t = intok[0]
153 t = intok[0]
154 if t == tokenize2.NEWLINE or (stop_at_NL and t == tokenize2.NL):
154 if t == tokenize2.NEWLINE or (stop_at_NL and t == tokenize2.NL):
155 # Stop before we try to pull a line we don't have yet
155 # Stop before we try to pull a line we don't have yet
156 break
156 break
157 elif t == tokenize2.ERRORTOKEN:
157 elif t == tokenize2.ERRORTOKEN:
158 stop_at_NL = True
158 stop_at_NL = True
159 except TokenError:
159 except TokenError:
160 # Multi-line statement - stop and try again with the next line
160 # Multi-line statement - stop and try again with the next line
161 self.reset_tokenizer()
161 self.reset_tokenizer()
162 return None
162 return None
163
163
164 return self.output(tokens)
164 return self.output(tokens)
165
165
166 def output(self, tokens):
166 def output(self, tokens):
167 self.current_line = ""
167 self.current_line = ""
168 self.reset_tokenizer()
168 self.reset_tokenizer()
169 return untokenize(self.func(tokens)).rstrip('\n')
169 return untokenize(self.func(tokens)).rstrip('\n')
170
170
171 def reset(self):
171 def reset(self):
172 l = self.current_line
172 l = self.current_line
173 self.current_line = ""
173 self.current_line = ""
174 self.reset_tokenizer()
174 self.reset_tokenizer()
175 if l:
175 if l:
176 return l.rstrip('\n')
176 return l.rstrip('\n')
177
177
178 class assemble_python_lines(TokenInputTransformer):
178 class assemble_python_lines(TokenInputTransformer):
179 def __init__(self):
179 def __init__(self):
180 super(assemble_python_lines, self).__init__(None)
180 super(assemble_python_lines, self).__init__(None)
181
181
182 def output(self, tokens):
182 def output(self, tokens):
183 return self.reset()
183 return self.reset()
184
184
185 @CoroutineInputTransformer.wrap
185 @CoroutineInputTransformer.wrap
186 def assemble_logical_lines():
186 def assemble_logical_lines():
187 """Join lines following explicit line continuations (\)"""
187 """Join lines following explicit line continuations (\)"""
188 line = ''
188 line = ''
189 while True:
189 while True:
190 line = (yield line)
190 line = (yield line)
191 if not line or line.isspace():
191 if not line or line.isspace():
192 continue
192 continue
193
193
194 parts = []
194 parts = []
195 while line is not None:
195 while line is not None:
196 if line.endswith('\\') and (not has_comment(line)):
196 if line.endswith('\\') and (not has_comment(line)):
197 parts.append(line[:-1])
197 parts.append(line[:-1])
198 line = (yield None) # Get another line
198 line = (yield None) # Get another line
199 else:
199 else:
200 parts.append(line)
200 parts.append(line)
201 break
201 break
202
202
203 # Output
203 # Output
204 line = ''.join(parts)
204 line = ''.join(parts)
205
205
206 # Utilities
206 # Utilities
207 def _make_help_call(target, esc, lspace, next_input=None):
207 def _make_help_call(target, esc, lspace, next_input=None):
208 """Prepares a pinfo(2)/psearch call from a target name and the escape
208 """Prepares a pinfo(2)/psearch call from a target name and the escape
209 (i.e. ? or ??)"""
209 (i.e. ? or ??)"""
210 method = 'pinfo2' if esc == '??' \
210 method = 'pinfo2' if esc == '??' \
211 else 'psearch' if '*' in target \
211 else 'psearch' if '*' in target \
212 else 'pinfo'
212 else 'pinfo'
213 arg = " ".join([method, target])
213 arg = " ".join([method, target])
214 if next_input is None:
214 if next_input is None:
215 return '%sget_ipython().magic(%r)' % (lspace, arg)
215 return '%sget_ipython().magic(%r)' % (lspace, arg)
216 else:
216 else:
217 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
217 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
218 (lspace, next_input, arg)
218 (lspace, next_input, arg)
219
219
220 # These define the transformations for the different escape characters.
220 # These define the transformations for the different escape characters.
221 def _tr_system(line_info):
221 def _tr_system(line_info):
222 "Translate lines escaped with: !"
222 "Translate lines escaped with: !"
223 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
223 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
224 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
224 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
225
225
226 def _tr_system2(line_info):
226 def _tr_system2(line_info):
227 "Translate lines escaped with: !!"
227 "Translate lines escaped with: !!"
228 cmd = line_info.line.lstrip()[2:]
228 cmd = line_info.line.lstrip()[2:]
229 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
229 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
230
230
231 def _tr_help(line_info):
231 def _tr_help(line_info):
232 "Translate lines escaped with: ?/??"
232 "Translate lines escaped with: ?/??"
233 # A naked help line should just fire the intro help screen
233 # A naked help line should just fire the intro help screen
234 if not line_info.line[1:]:
234 if not line_info.line[1:]:
235 return 'get_ipython().show_usage()'
235 return 'get_ipython().show_usage()'
236
236
237 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
237 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
238
238
239 def _tr_magic(line_info):
239 def _tr_magic(line_info):
240 "Translate lines escaped with: %"
240 "Translate lines escaped with: %"
241 tpl = '%sget_ipython().magic(%r)'
241 tpl = '%sget_ipython().magic(%r)'
242 if line_info.line.startswith(ESC_MAGIC2):
242 if line_info.line.startswith(ESC_MAGIC2):
243 return line_info.line
243 return line_info.line
244 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
244 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
245 return tpl % (line_info.pre, cmd)
245 return tpl % (line_info.pre, cmd)
246
246
247 def _tr_quote(line_info):
247 def _tr_quote(line_info):
248 "Translate lines escaped with: ,"
248 "Translate lines escaped with: ,"
249 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
249 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
250 '", "'.join(line_info.the_rest.split()) )
250 '", "'.join(line_info.the_rest.split()) )
251
251
252 def _tr_quote2(line_info):
252 def _tr_quote2(line_info):
253 "Translate lines escaped with: ;"
253 "Translate lines escaped with: ;"
254 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
254 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
255 line_info.the_rest)
255 line_info.the_rest)
256
256
257 def _tr_paren(line_info):
257 def _tr_paren(line_info):
258 "Translate lines escaped with: /"
258 "Translate lines escaped with: /"
259 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
259 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
260 ", ".join(line_info.the_rest.split()))
260 ", ".join(line_info.the_rest.split()))
261
261
262 tr = { ESC_SHELL : _tr_system,
262 tr = { ESC_SHELL : _tr_system,
263 ESC_SH_CAP : _tr_system2,
263 ESC_SH_CAP : _tr_system2,
264 ESC_HELP : _tr_help,
264 ESC_HELP : _tr_help,
265 ESC_HELP2 : _tr_help,
265 ESC_HELP2 : _tr_help,
266 ESC_MAGIC : _tr_magic,
266 ESC_MAGIC : _tr_magic,
267 ESC_QUOTE : _tr_quote,
267 ESC_QUOTE : _tr_quote,
268 ESC_QUOTE2 : _tr_quote2,
268 ESC_QUOTE2 : _tr_quote2,
269 ESC_PAREN : _tr_paren }
269 ESC_PAREN : _tr_paren }
270
270
271 @StatelessInputTransformer.wrap
271 @StatelessInputTransformer.wrap
272 def escaped_commands(line):
272 def escaped_commands(line):
273 """Transform escaped commands - %magic, !system, ?help + various autocalls.
273 """Transform escaped commands - %magic, !system, ?help + various autocalls.
274 """
274 """
275 if not line or line.isspace():
275 if not line or line.isspace():
276 return line
276 return line
277 lineinf = LineInfo(line)
277 lineinf = LineInfo(line)
278 if lineinf.esc not in tr:
278 if lineinf.esc not in tr:
279 return line
279 return line
280
280
281 return tr[lineinf.esc](lineinf)
281 return tr[lineinf.esc](lineinf)
282
282
283 _initial_space_re = re.compile(r'\s*')
283 _initial_space_re = re.compile(r'\s*')
284
284
285 _help_end_re = re.compile(r"""(%{0,2}
285 _help_end_re = re.compile(r"""(%{0,2}
286 [a-zA-Z_*][\w*]* # Variable name
286 [a-zA-Z_*][\w*]* # Variable name
287 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
287 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
288 )
288 )
289 (\?\??)$ # ? or ??
289 (\?\??)$ # ? or ??
290 """,
290 """,
291 re.VERBOSE)
291 re.VERBOSE)
292
292
293 # Extra pseudotokens for multiline strings and data structures
293 # Extra pseudotokens for multiline strings and data structures
294 _MULTILINE_STRING = object()
294 _MULTILINE_STRING = object()
295 _MULTILINE_STRUCTURE = object()
295 _MULTILINE_STRUCTURE = object()
296
296
297 def _line_tokens(line):
297 def _line_tokens(line):
298 """Helper for has_comment and ends_in_comment_or_string."""
298 """Helper for has_comment and ends_in_comment_or_string."""
299 readline = StringIO(line).readline
299 readline = StringIO(line).readline
300 toktypes = set()
300 toktypes = set()
301 try:
301 try:
302 for t in generate_tokens(readline):
302 for t in generate_tokens(readline):
303 toktypes.add(t[0])
303 toktypes.add(t[0])
304 except TokenError as e:
304 except TokenError as e:
305 # There are only two cases where a TokenError is raised.
305 # There are only two cases where a TokenError is raised.
306 if 'multi-line string' in e.args[0]:
306 if 'multi-line string' in e.args[0]:
307 toktypes.add(_MULTILINE_STRING)
307 toktypes.add(_MULTILINE_STRING)
308 else:
308 else:
309 toktypes.add(_MULTILINE_STRUCTURE)
309 toktypes.add(_MULTILINE_STRUCTURE)
310 return toktypes
310 return toktypes
311
311
312 def has_comment(src):
312 def has_comment(src):
313 """Indicate whether an input line has (i.e. ends in, or is) a comment.
313 """Indicate whether an input line has (i.e. ends in, or is) a comment.
314
314
315 This uses tokenize, so it can distinguish comments from # inside strings.
315 This uses tokenize, so it can distinguish comments from # inside strings.
316
316
317 Parameters
317 Parameters
318 ----------
318 ----------
319 src : string
319 src : string
320 A single line input string.
320 A single line input string.
321
321
322 Returns
322 Returns
323 -------
323 -------
324 comment : bool
324 comment : bool
325 True if source has a comment.
325 True if source has a comment.
326 """
326 """
327 return (tokenize2.COMMENT in _line_tokens(src))
327 return (tokenize2.COMMENT in _line_tokens(src))
328
328
329 def ends_in_comment_or_string(src):
329 def ends_in_comment_or_string(src):
330 """Indicates whether or not an input line ends in a comment or within
330 """Indicates whether or not an input line ends in a comment or within
331 a multiline string.
331 a multiline string.
332
332
333 Parameters
333 Parameters
334 ----------
334 ----------
335 src : string
335 src : string
336 A single line input string.
336 A single line input string.
337
337
338 Returns
338 Returns
339 -------
339 -------
340 comment : bool
340 comment : bool
341 True if source ends in a comment or multiline string.
341 True if source ends in a comment or multiline string.
342 """
342 """
343 toktypes = _line_tokens(src)
343 toktypes = _line_tokens(src)
344 return (tokenize2.COMMENT in toktypes) or (_MULTILINE_STRING in toktypes)
344 return (tokenize2.COMMENT in toktypes) or (_MULTILINE_STRING in toktypes)
345
345
346
346
347 @StatelessInputTransformer.wrap
347 @StatelessInputTransformer.wrap
348 def help_end(line):
348 def help_end(line):
349 """Translate lines with ?/?? at the end"""
349 """Translate lines with ?/?? at the end"""
350 m = _help_end_re.search(line)
350 m = _help_end_re.search(line)
351 if m is None or ends_in_comment_or_string(line):
351 if m is None or ends_in_comment_or_string(line):
352 return line
352 return line
353 target = m.group(1)
353 target = m.group(1)
354 esc = m.group(3)
354 esc = m.group(3)
355 lspace = _initial_space_re.match(line).group(0)
355 lspace = _initial_space_re.match(line).group(0)
356
356
357 # If we're mid-command, put it back on the next prompt for the user.
357 # If we're mid-command, put it back on the next prompt for the user.
358 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
358 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
359
359
360 return _make_help_call(target, esc, lspace, next_input)
360 return _make_help_call(target, esc, lspace, next_input)
361
361
362
362
363 @CoroutineInputTransformer.wrap
363 @CoroutineInputTransformer.wrap
364 def cellmagic(end_on_blank_line=False):
364 def cellmagic(end_on_blank_line=False):
365 """Captures & transforms cell magics.
365 """Captures & transforms cell magics.
366
366
367 After a cell magic is started, this stores up any lines it gets until it is
367 After a cell magic is started, this stores up any lines it gets until it is
368 reset (sent None).
368 reset (sent None).
369 """
369 """
370 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
370 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
371 cellmagic_help_re = re.compile('%%\w+\?')
371 cellmagic_help_re = re.compile('%%\w+\?')
372 line = ''
372 line = ''
373 while True:
373 while True:
374 line = (yield line)
374 line = (yield line)
375 # consume leading empty lines
375 # consume leading empty lines
376 while not line:
376 while not line:
377 line = (yield line)
377 line = (yield line)
378
378
379 if not line.startswith(ESC_MAGIC2):
379 if not line.startswith(ESC_MAGIC2):
380 # This isn't a cell magic, idle waiting for reset then start over
380 # This isn't a cell magic, idle waiting for reset then start over
381 while line is not None:
381 while line is not None:
382 line = (yield line)
382 line = (yield line)
383 continue
383 continue
384
384
385 if cellmagic_help_re.match(line):
385 if cellmagic_help_re.match(line):
386 # This case will be handled by help_end
386 # This case will be handled by help_end
387 continue
387 continue
388
388
389 first = line
389 first = line
390 body = []
390 body = []
391 line = (yield None)
391 line = (yield None)
392 while (line is not None) and \
392 while (line is not None) and \
393 ((line.strip() != '') or not end_on_blank_line):
393 ((line.strip() != '') or not end_on_blank_line):
394 body.append(line)
394 body.append(line)
395 line = (yield None)
395 line = (yield None)
396
396
397 # Output
397 # Output
398 magic_name, _, first = first.partition(' ')
398 magic_name, _, first = first.partition(' ')
399 magic_name = magic_name.lstrip(ESC_MAGIC2)
399 magic_name = magic_name.lstrip(ESC_MAGIC2)
400 line = tpl % (magic_name, first, u'\n'.join(body))
400 line = tpl % (magic_name, first, u'\n'.join(body))
401
401
402
402
403 def _strip_prompts(prompt_re, initial_re=None):
403 def _strip_prompts(prompt_re, initial_re=None):
404 """Remove matching input prompts from a block of input.
404 """Remove matching input prompts from a block of input.
405
405
406 Parameters
406 Parameters
407 ----------
407 ----------
408 prompt_re : regular expression
408 prompt_re : regular expression
409 A regular expression matching any input prompt (including continuation)
409 A regular expression matching any input prompt (including continuation)
410 initial_re : regular expression, optional
410 initial_re : regular expression, optional
411 A regular expression matching only the initial prompt, but not continuation.
411 A regular expression matching only the initial prompt, but not continuation.
412 If no initial expression is given, prompt_re will be used everywhere.
412 If no initial expression is given, prompt_re will be used everywhere.
413 Used mainly for plain Python prompts, where the continuation prompt
413 Used mainly for plain Python prompts, where the continuation prompt
414 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
414 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
415
415
416 If initial_re and prompt_re differ,
416 If initial_re and prompt_re differ,
417 only initial_re will be tested against the first line.
417 only initial_re will be tested against the first line.
418 If any prompt is found on the first two lines,
418 If any prompt is found on the first two lines,
419 prompts will be stripped from the rest of the block.
419 prompts will be stripped from the rest of the block.
420 """
420 """
421 if initial_re is None:
421 if initial_re is None:
422 initial_re = prompt_re
422 initial_re = prompt_re
423 line = ''
423 line = ''
424 while True:
424 while True:
425 line = (yield line)
425 line = (yield line)
426
426
427 # First line of cell
427 # First line of cell
428 if line is None:
428 if line is None:
429 continue
429 continue
430 out, n1 = initial_re.subn('', line, count=1)
430 out, n1 = initial_re.subn('', line, count=1)
431 line = (yield out)
431 line = (yield out)
432
432
433 if line is None:
433 if line is None:
434 continue
434 continue
435 # check for any prompt on the second line of the cell,
435 # check for any prompt on the second line of the cell,
436 # because people often copy from just after the first prompt,
436 # because people often copy from just after the first prompt,
437 # so we might not see it in the first line.
437 # so we might not see it in the first line.
438 out, n2 = prompt_re.subn('', line, count=1)
438 out, n2 = prompt_re.subn('', line, count=1)
439 line = (yield out)
439 line = (yield out)
440
440
441 if n1 or n2:
441 if n1 or n2:
442 # Found a prompt in the first two lines - check for it in
442 # Found a prompt in the first two lines - check for it in
443 # the rest of the cell as well.
443 # the rest of the cell as well.
444 while line is not None:
444 while line is not None:
445 line = (yield prompt_re.sub('', line, count=1))
445 line = (yield prompt_re.sub('', line, count=1))
446
446
447 else:
447 else:
448 # Prompts not in input - wait for reset
448 # Prompts not in input - wait for reset
449 while line is not None:
449 while line is not None:
450 line = (yield line)
450 line = (yield line)
451
451
452 @CoroutineInputTransformer.wrap
452 @CoroutineInputTransformer.wrap
453 def classic_prompt():
453 def classic_prompt():
454 """Strip the >>>/... prompts of the Python interactive shell."""
454 """Strip the >>>/... prompts of the Python interactive shell."""
455 # FIXME: non-capturing version (?:...) usable?
455 # FIXME: non-capturing version (?:...) usable?
456 prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)')
456 prompt_re = re.compile(r'^(>>>|\.\.\.)( |$)')
457 initial_re = re.compile(r'^(>>> ?)')
457 initial_re = re.compile(r'^>>>( |$)')
458 return _strip_prompts(prompt_re, initial_re)
458 return _strip_prompts(prompt_re, initial_re)
459
459
460 @CoroutineInputTransformer.wrap
460 @CoroutineInputTransformer.wrap
461 def ipy_prompt():
461 def ipy_prompt():
462 """Strip IPython's In [1]:/...: prompts."""
462 """Strip IPython's In [1]:/...: prompts."""
463 # FIXME: non-capturing version (?:...) usable?
463 # FIXME: non-capturing version (?:...) usable?
464 prompt_re = re.compile(r'^(In \[\d+\]: |\ {3,}\.{3,}: )')
464 prompt_re = re.compile(r'^(In \[\d+\]: |\ {3,}\.{3,}: )')
465 return _strip_prompts(prompt_re)
465 return _strip_prompts(prompt_re)
466
466
467
467
468 @CoroutineInputTransformer.wrap
468 @CoroutineInputTransformer.wrap
469 def leading_indent():
469 def leading_indent():
470 """Remove leading indentation.
470 """Remove leading indentation.
471
471
472 If the first line starts with a spaces or tabs, the same whitespace will be
472 If the first line starts with a spaces or tabs, the same whitespace will be
473 removed from each following line until it is reset.
473 removed from each following line until it is reset.
474 """
474 """
475 space_re = re.compile(r'^[ \t]+')
475 space_re = re.compile(r'^[ \t]+')
476 line = ''
476 line = ''
477 while True:
477 while True:
478 line = (yield line)
478 line = (yield line)
479
479
480 if line is None:
480 if line is None:
481 continue
481 continue
482
482
483 m = space_re.match(line)
483 m = space_re.match(line)
484 if m:
484 if m:
485 space = m.group(0)
485 space = m.group(0)
486 while line is not None:
486 while line is not None:
487 if line.startswith(space):
487 if line.startswith(space):
488 line = line[len(space):]
488 line = line[len(space):]
489 line = (yield line)
489 line = (yield line)
490 else:
490 else:
491 # No leading spaces - wait for reset
491 # No leading spaces - wait for reset
492 while line is not None:
492 while line is not None:
493 line = (yield line)
493 line = (yield line)
494
494
495
495
496 @CoroutineInputTransformer.wrap
496 @CoroutineInputTransformer.wrap
497 def strip_encoding_cookie():
497 def strip_encoding_cookie():
498 """Remove encoding comment if found in first two lines
498 """Remove encoding comment if found in first two lines
499
499
500 If the first or second line has the `# coding: utf-8` comment,
500 If the first or second line has the `# coding: utf-8` comment,
501 it will be removed.
501 it will be removed.
502 """
502 """
503 line = ''
503 line = ''
504 while True:
504 while True:
505 line = (yield line)
505 line = (yield line)
506 # check comment on first two lines
506 # check comment on first two lines
507 for i in range(2):
507 for i in range(2):
508 if line is None:
508 if line is None:
509 break
509 break
510 if cookie_comment_re.match(line):
510 if cookie_comment_re.match(line):
511 line = (yield "")
511 line = (yield "")
512 else:
512 else:
513 line = (yield line)
513 line = (yield line)
514
514
515 # no-op on the rest of the cell
515 # no-op on the rest of the cell
516 while line is not None:
516 while line is not None:
517 line = (yield line)
517 line = (yield line)
518
518
519 _assign_pat = \
519 _assign_pat = \
520 r'''(?P<lhs>(\s*)
520 r'''(?P<lhs>(\s*)
521 ([\w\.]+) # Initial identifier
521 ([\w\.]+) # Initial identifier
522 (\s*,\s*
522 (\s*,\s*
523 \*?[\w\.]+)* # Further identifiers for unpacking
523 \*?[\w\.]+)* # Further identifiers for unpacking
524 \s*?,? # Trailing comma
524 \s*?,? # Trailing comma
525 )
525 )
526 \s*=\s*
526 \s*=\s*
527 '''
527 '''
528
528
529 assign_system_re = re.compile(r'{}!\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
529 assign_system_re = re.compile(r'{}!\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
530 assign_system_template = '%s = get_ipython().getoutput(%r)'
530 assign_system_template = '%s = get_ipython().getoutput(%r)'
531 @StatelessInputTransformer.wrap
531 @StatelessInputTransformer.wrap
532 def assign_from_system(line):
532 def assign_from_system(line):
533 """Transform assignment from system commands (e.g. files = !ls)"""
533 """Transform assignment from system commands (e.g. files = !ls)"""
534 m = assign_system_re.match(line)
534 m = assign_system_re.match(line)
535 if m is None:
535 if m is None:
536 return line
536 return line
537
537
538 return assign_system_template % m.group('lhs', 'cmd')
538 return assign_system_template % m.group('lhs', 'cmd')
539
539
540 assign_magic_re = re.compile(r'{}%\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
540 assign_magic_re = re.compile(r'{}%\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
541 assign_magic_template = '%s = get_ipython().magic(%r)'
541 assign_magic_template = '%s = get_ipython().magic(%r)'
542 @StatelessInputTransformer.wrap
542 @StatelessInputTransformer.wrap
543 def assign_from_magic(line):
543 def assign_from_magic(line):
544 """Transform assignment from magic commands (e.g. a = %who_ls)"""
544 """Transform assignment from magic commands (e.g. a = %who_ls)"""
545 m = assign_magic_re.match(line)
545 m = assign_magic_re.match(line)
546 if m is None:
546 if m is None:
547 return line
547 return line
548
548
549 return assign_magic_template % m.group('lhs', 'cmd')
549 return assign_magic_template % m.group('lhs', 'cmd')
@@ -1,585 +1,585 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the inputsplitter module.
2 """Tests for the inputsplitter module.
3
3
4 Authors
4 Authors
5 -------
5 -------
6 * Fernando Perez
6 * Fernando Perez
7 * Robert Kern
7 * Robert Kern
8 """
8 """
9 from __future__ import print_function
9 from __future__ import print_function
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2010-2011 The IPython Development Team
11 # Copyright (C) 2010-2011 The IPython Development Team
12 #
12 #
13 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Imports
18 # Imports
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # stdlib
20 # stdlib
21 import unittest
21 import unittest
22 import sys
22 import sys
23
23
24 # Third party
24 # Third party
25 import nose.tools as nt
25 import nose.tools as nt
26
26
27 # Our own
27 # Our own
28 from IPython.core import inputsplitter as isp
28 from IPython.core import inputsplitter as isp
29 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
29 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
30 from IPython.testing import tools as tt
30 from IPython.testing import tools as tt
31 from IPython.utils import py3compat
31 from IPython.utils import py3compat
32 from IPython.utils.py3compat import string_types, input
32 from IPython.utils.py3compat import string_types, input
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Semi-complete examples (also used as tests)
35 # Semi-complete examples (also used as tests)
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 # Note: at the bottom, there's a slightly more complete version of this that
38 # Note: at the bottom, there's a slightly more complete version of this that
39 # can be useful during development of code here.
39 # can be useful during development of code here.
40
40
41 def mini_interactive_loop(input_func):
41 def mini_interactive_loop(input_func):
42 """Minimal example of the logic of an interactive interpreter loop.
42 """Minimal example of the logic of an interactive interpreter loop.
43
43
44 This serves as an example, and it is used by the test system with a fake
44 This serves as an example, and it is used by the test system with a fake
45 raw_input that simulates interactive input."""
45 raw_input that simulates interactive input."""
46
46
47 from IPython.core.inputsplitter import InputSplitter
47 from IPython.core.inputsplitter import InputSplitter
48
48
49 isp = InputSplitter()
49 isp = InputSplitter()
50 # In practice, this input loop would be wrapped in an outside loop to read
50 # In practice, this input loop would be wrapped in an outside loop to read
51 # input indefinitely, until some exit/quit command was issued. Here we
51 # input indefinitely, until some exit/quit command was issued. Here we
52 # only illustrate the basic inner loop.
52 # only illustrate the basic inner loop.
53 while isp.push_accepts_more():
53 while isp.push_accepts_more():
54 indent = ' '*isp.indent_spaces
54 indent = ' '*isp.indent_spaces
55 prompt = '>>> ' + indent
55 prompt = '>>> ' + indent
56 line = indent + input_func(prompt)
56 line = indent + input_func(prompt)
57 isp.push(line)
57 isp.push(line)
58
58
59 # Here we just return input so we can use it in a test suite, but a real
59 # Here we just return input so we can use it in a test suite, but a real
60 # interpreter would instead send it for execution somewhere.
60 # interpreter would instead send it for execution somewhere.
61 src = isp.source_reset()
61 src = isp.source_reset()
62 #print 'Input source was:\n', src # dbg
62 #print 'Input source was:\n', src # dbg
63 return src
63 return src
64
64
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66 # Test utilities, just for local use
66 # Test utilities, just for local use
67 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68
68
69 def assemble(block):
69 def assemble(block):
70 """Assemble a block into multi-line sub-blocks."""
70 """Assemble a block into multi-line sub-blocks."""
71 return ['\n'.join(sub_block)+'\n' for sub_block in block]
71 return ['\n'.join(sub_block)+'\n' for sub_block in block]
72
72
73
73
74 def pseudo_input(lines):
74 def pseudo_input(lines):
75 """Return a function that acts like raw_input but feeds the input list."""
75 """Return a function that acts like raw_input but feeds the input list."""
76 ilines = iter(lines)
76 ilines = iter(lines)
77 def raw_in(prompt):
77 def raw_in(prompt):
78 try:
78 try:
79 return next(ilines)
79 return next(ilines)
80 except StopIteration:
80 except StopIteration:
81 return ''
81 return ''
82 return raw_in
82 return raw_in
83
83
84 #-----------------------------------------------------------------------------
84 #-----------------------------------------------------------------------------
85 # Tests
85 # Tests
86 #-----------------------------------------------------------------------------
86 #-----------------------------------------------------------------------------
87 def test_spaces():
87 def test_spaces():
88 tests = [('', 0),
88 tests = [('', 0),
89 (' ', 1),
89 (' ', 1),
90 ('\n', 0),
90 ('\n', 0),
91 (' \n', 1),
91 (' \n', 1),
92 ('x', 0),
92 ('x', 0),
93 (' x', 1),
93 (' x', 1),
94 (' x',2),
94 (' x',2),
95 (' x',4),
95 (' x',4),
96 # Note: tabs are counted as a single whitespace!
96 # Note: tabs are counted as a single whitespace!
97 ('\tx', 1),
97 ('\tx', 1),
98 ('\t x', 2),
98 ('\t x', 2),
99 ]
99 ]
100 tt.check_pairs(isp.num_ini_spaces, tests)
100 tt.check_pairs(isp.num_ini_spaces, tests)
101
101
102
102
103 def test_remove_comments():
103 def test_remove_comments():
104 tests = [('text', 'text'),
104 tests = [('text', 'text'),
105 ('text # comment', 'text '),
105 ('text # comment', 'text '),
106 ('text # comment\n', 'text \n'),
106 ('text # comment\n', 'text \n'),
107 ('text # comment \n', 'text \n'),
107 ('text # comment \n', 'text \n'),
108 ('line # c \nline\n','line \nline\n'),
108 ('line # c \nline\n','line \nline\n'),
109 ('line # c \nline#c2 \nline\nline #c\n\n',
109 ('line # c \nline#c2 \nline\nline #c\n\n',
110 'line \nline\nline\nline \n\n'),
110 'line \nline\nline\nline \n\n'),
111 ]
111 ]
112 tt.check_pairs(isp.remove_comments, tests)
112 tt.check_pairs(isp.remove_comments, tests)
113
113
114
114
115 def test_get_input_encoding():
115 def test_get_input_encoding():
116 encoding = isp.get_input_encoding()
116 encoding = isp.get_input_encoding()
117 nt.assert_true(isinstance(encoding, string_types))
117 nt.assert_true(isinstance(encoding, string_types))
118 # simple-minded check that at least encoding a simple string works with the
118 # simple-minded check that at least encoding a simple string works with the
119 # encoding we got.
119 # encoding we got.
120 nt.assert_equal(u'test'.encode(encoding), b'test')
120 nt.assert_equal(u'test'.encode(encoding), b'test')
121
121
122
122
123 class NoInputEncodingTestCase(unittest.TestCase):
123 class NoInputEncodingTestCase(unittest.TestCase):
124 def setUp(self):
124 def setUp(self):
125 self.old_stdin = sys.stdin
125 self.old_stdin = sys.stdin
126 class X: pass
126 class X: pass
127 fake_stdin = X()
127 fake_stdin = X()
128 sys.stdin = fake_stdin
128 sys.stdin = fake_stdin
129
129
130 def test(self):
130 def test(self):
131 # Verify that if sys.stdin has no 'encoding' attribute we do the right
131 # Verify that if sys.stdin has no 'encoding' attribute we do the right
132 # thing
132 # thing
133 enc = isp.get_input_encoding()
133 enc = isp.get_input_encoding()
134 self.assertEqual(enc, 'ascii')
134 self.assertEqual(enc, 'ascii')
135
135
136 def tearDown(self):
136 def tearDown(self):
137 sys.stdin = self.old_stdin
137 sys.stdin = self.old_stdin
138
138
139
139
140 class InputSplitterTestCase(unittest.TestCase):
140 class InputSplitterTestCase(unittest.TestCase):
141 def setUp(self):
141 def setUp(self):
142 self.isp = isp.InputSplitter()
142 self.isp = isp.InputSplitter()
143
143
144 def test_reset(self):
144 def test_reset(self):
145 isp = self.isp
145 isp = self.isp
146 isp.push('x=1')
146 isp.push('x=1')
147 isp.reset()
147 isp.reset()
148 self.assertEqual(isp._buffer, [])
148 self.assertEqual(isp._buffer, [])
149 self.assertEqual(isp.indent_spaces, 0)
149 self.assertEqual(isp.indent_spaces, 0)
150 self.assertEqual(isp.source, '')
150 self.assertEqual(isp.source, '')
151 self.assertEqual(isp.code, None)
151 self.assertEqual(isp.code, None)
152 self.assertEqual(isp._is_complete, False)
152 self.assertEqual(isp._is_complete, False)
153
153
154 def test_source(self):
154 def test_source(self):
155 self.isp._store('1')
155 self.isp._store('1')
156 self.isp._store('2')
156 self.isp._store('2')
157 self.assertEqual(self.isp.source, '1\n2\n')
157 self.assertEqual(self.isp.source, '1\n2\n')
158 self.assertTrue(len(self.isp._buffer)>0)
158 self.assertTrue(len(self.isp._buffer)>0)
159 self.assertEqual(self.isp.source_reset(), '1\n2\n')
159 self.assertEqual(self.isp.source_reset(), '1\n2\n')
160 self.assertEqual(self.isp._buffer, [])
160 self.assertEqual(self.isp._buffer, [])
161 self.assertEqual(self.isp.source, '')
161 self.assertEqual(self.isp.source, '')
162
162
163 def test_indent(self):
163 def test_indent(self):
164 isp = self.isp # shorthand
164 isp = self.isp # shorthand
165 isp.push('x=1')
165 isp.push('x=1')
166 self.assertEqual(isp.indent_spaces, 0)
166 self.assertEqual(isp.indent_spaces, 0)
167 isp.push('if 1:\n x=1')
167 isp.push('if 1:\n x=1')
168 self.assertEqual(isp.indent_spaces, 4)
168 self.assertEqual(isp.indent_spaces, 4)
169 isp.push('y=2\n')
169 isp.push('y=2\n')
170 self.assertEqual(isp.indent_spaces, 0)
170 self.assertEqual(isp.indent_spaces, 0)
171
171
172 def test_indent2(self):
172 def test_indent2(self):
173 isp = self.isp
173 isp = self.isp
174 isp.push('if 1:')
174 isp.push('if 1:')
175 self.assertEqual(isp.indent_spaces, 4)
175 self.assertEqual(isp.indent_spaces, 4)
176 isp.push(' x=1')
176 isp.push(' x=1')
177 self.assertEqual(isp.indent_spaces, 4)
177 self.assertEqual(isp.indent_spaces, 4)
178 # Blank lines shouldn't change the indent level
178 # Blank lines shouldn't change the indent level
179 isp.push(' '*2)
179 isp.push(' '*2)
180 self.assertEqual(isp.indent_spaces, 4)
180 self.assertEqual(isp.indent_spaces, 4)
181
181
182 def test_indent3(self):
182 def test_indent3(self):
183 isp = self.isp
183 isp = self.isp
184 # When a multiline statement contains parens or multiline strings, we
184 # When a multiline statement contains parens or multiline strings, we
185 # shouldn't get confused.
185 # shouldn't get confused.
186 isp.push("if 1:")
186 isp.push("if 1:")
187 isp.push(" x = (1+\n 2)")
187 isp.push(" x = (1+\n 2)")
188 self.assertEqual(isp.indent_spaces, 4)
188 self.assertEqual(isp.indent_spaces, 4)
189
189
190 def test_indent4(self):
190 def test_indent4(self):
191 isp = self.isp
191 isp = self.isp
192 # whitespace after ':' should not screw up indent level
192 # whitespace after ':' should not screw up indent level
193 isp.push('if 1: \n x=1')
193 isp.push('if 1: \n x=1')
194 self.assertEqual(isp.indent_spaces, 4)
194 self.assertEqual(isp.indent_spaces, 4)
195 isp.push('y=2\n')
195 isp.push('y=2\n')
196 self.assertEqual(isp.indent_spaces, 0)
196 self.assertEqual(isp.indent_spaces, 0)
197 isp.push('if 1:\t\n x=1')
197 isp.push('if 1:\t\n x=1')
198 self.assertEqual(isp.indent_spaces, 4)
198 self.assertEqual(isp.indent_spaces, 4)
199 isp.push('y=2\n')
199 isp.push('y=2\n')
200 self.assertEqual(isp.indent_spaces, 0)
200 self.assertEqual(isp.indent_spaces, 0)
201
201
202 def test_dedent_pass(self):
202 def test_dedent_pass(self):
203 isp = self.isp # shorthand
203 isp = self.isp # shorthand
204 # should NOT cause dedent
204 # should NOT cause dedent
205 isp.push('if 1:\n passes = 5')
205 isp.push('if 1:\n passes = 5')
206 self.assertEqual(isp.indent_spaces, 4)
206 self.assertEqual(isp.indent_spaces, 4)
207 isp.push('if 1:\n pass')
207 isp.push('if 1:\n pass')
208 self.assertEqual(isp.indent_spaces, 0)
208 self.assertEqual(isp.indent_spaces, 0)
209 isp.push('if 1:\n pass ')
209 isp.push('if 1:\n pass ')
210 self.assertEqual(isp.indent_spaces, 0)
210 self.assertEqual(isp.indent_spaces, 0)
211
211
212 def test_dedent_break(self):
212 def test_dedent_break(self):
213 isp = self.isp # shorthand
213 isp = self.isp # shorthand
214 # should NOT cause dedent
214 # should NOT cause dedent
215 isp.push('while 1:\n breaks = 5')
215 isp.push('while 1:\n breaks = 5')
216 self.assertEqual(isp.indent_spaces, 4)
216 self.assertEqual(isp.indent_spaces, 4)
217 isp.push('while 1:\n break')
217 isp.push('while 1:\n break')
218 self.assertEqual(isp.indent_spaces, 0)
218 self.assertEqual(isp.indent_spaces, 0)
219 isp.push('while 1:\n break ')
219 isp.push('while 1:\n break ')
220 self.assertEqual(isp.indent_spaces, 0)
220 self.assertEqual(isp.indent_spaces, 0)
221
221
222 def test_dedent_continue(self):
222 def test_dedent_continue(self):
223 isp = self.isp # shorthand
223 isp = self.isp # shorthand
224 # should NOT cause dedent
224 # should NOT cause dedent
225 isp.push('while 1:\n continues = 5')
225 isp.push('while 1:\n continues = 5')
226 self.assertEqual(isp.indent_spaces, 4)
226 self.assertEqual(isp.indent_spaces, 4)
227 isp.push('while 1:\n continue')
227 isp.push('while 1:\n continue')
228 self.assertEqual(isp.indent_spaces, 0)
228 self.assertEqual(isp.indent_spaces, 0)
229 isp.push('while 1:\n continue ')
229 isp.push('while 1:\n continue ')
230 self.assertEqual(isp.indent_spaces, 0)
230 self.assertEqual(isp.indent_spaces, 0)
231
231
232 def test_dedent_raise(self):
232 def test_dedent_raise(self):
233 isp = self.isp # shorthand
233 isp = self.isp # shorthand
234 # should NOT cause dedent
234 # should NOT cause dedent
235 isp.push('if 1:\n raised = 4')
235 isp.push('if 1:\n raised = 4')
236 self.assertEqual(isp.indent_spaces, 4)
236 self.assertEqual(isp.indent_spaces, 4)
237 isp.push('if 1:\n raise TypeError()')
237 isp.push('if 1:\n raise TypeError()')
238 self.assertEqual(isp.indent_spaces, 0)
238 self.assertEqual(isp.indent_spaces, 0)
239 isp.push('if 1:\n raise')
239 isp.push('if 1:\n raise')
240 self.assertEqual(isp.indent_spaces, 0)
240 self.assertEqual(isp.indent_spaces, 0)
241 isp.push('if 1:\n raise ')
241 isp.push('if 1:\n raise ')
242 self.assertEqual(isp.indent_spaces, 0)
242 self.assertEqual(isp.indent_spaces, 0)
243
243
244 def test_dedent_return(self):
244 def test_dedent_return(self):
245 isp = self.isp # shorthand
245 isp = self.isp # shorthand
246 # should NOT cause dedent
246 # should NOT cause dedent
247 isp.push('if 1:\n returning = 4')
247 isp.push('if 1:\n returning = 4')
248 self.assertEqual(isp.indent_spaces, 4)
248 self.assertEqual(isp.indent_spaces, 4)
249 isp.push('if 1:\n return 5 + 493')
249 isp.push('if 1:\n return 5 + 493')
250 self.assertEqual(isp.indent_spaces, 0)
250 self.assertEqual(isp.indent_spaces, 0)
251 isp.push('if 1:\n return')
251 isp.push('if 1:\n return')
252 self.assertEqual(isp.indent_spaces, 0)
252 self.assertEqual(isp.indent_spaces, 0)
253 isp.push('if 1:\n return ')
253 isp.push('if 1:\n return ')
254 self.assertEqual(isp.indent_spaces, 0)
254 self.assertEqual(isp.indent_spaces, 0)
255 isp.push('if 1:\n return(0)')
255 isp.push('if 1:\n return(0)')
256 self.assertEqual(isp.indent_spaces, 0)
256 self.assertEqual(isp.indent_spaces, 0)
257
257
258 def test_push(self):
258 def test_push(self):
259 isp = self.isp
259 isp = self.isp
260 self.assertTrue(isp.push('x=1'))
260 self.assertTrue(isp.push('x=1'))
261
261
262 def test_push2(self):
262 def test_push2(self):
263 isp = self.isp
263 isp = self.isp
264 self.assertFalse(isp.push('if 1:'))
264 self.assertFalse(isp.push('if 1:'))
265 for line in [' x=1', '# a comment', ' y=2']:
265 for line in [' x=1', '# a comment', ' y=2']:
266 print(line)
266 print(line)
267 self.assertTrue(isp.push(line))
267 self.assertTrue(isp.push(line))
268
268
269 def test_push3(self):
269 def test_push3(self):
270 isp = self.isp
270 isp = self.isp
271 isp.push('if True:')
271 isp.push('if True:')
272 isp.push(' a = 1')
272 isp.push(' a = 1')
273 self.assertFalse(isp.push('b = [1,'))
273 self.assertFalse(isp.push('b = [1,'))
274
274
275 def test_push_accepts_more(self):
275 def test_push_accepts_more(self):
276 isp = self.isp
276 isp = self.isp
277 isp.push('x=1')
277 isp.push('x=1')
278 self.assertFalse(isp.push_accepts_more())
278 self.assertFalse(isp.push_accepts_more())
279
279
280 def test_push_accepts_more2(self):
280 def test_push_accepts_more2(self):
281 isp = self.isp
281 isp = self.isp
282 isp.push('if 1:')
282 isp.push('if 1:')
283 self.assertTrue(isp.push_accepts_more())
283 self.assertTrue(isp.push_accepts_more())
284 isp.push(' x=1')
284 isp.push(' x=1')
285 self.assertTrue(isp.push_accepts_more())
285 self.assertTrue(isp.push_accepts_more())
286 isp.push('')
286 isp.push('')
287 self.assertFalse(isp.push_accepts_more())
287 self.assertFalse(isp.push_accepts_more())
288
288
289 def test_push_accepts_more3(self):
289 def test_push_accepts_more3(self):
290 isp = self.isp
290 isp = self.isp
291 isp.push("x = (2+\n3)")
291 isp.push("x = (2+\n3)")
292 self.assertFalse(isp.push_accepts_more())
292 self.assertFalse(isp.push_accepts_more())
293
293
294 def test_push_accepts_more4(self):
294 def test_push_accepts_more4(self):
295 isp = self.isp
295 isp = self.isp
296 # When a multiline statement contains parens or multiline strings, we
296 # When a multiline statement contains parens or multiline strings, we
297 # shouldn't get confused.
297 # shouldn't get confused.
298 # FIXME: we should be able to better handle de-dents in statements like
298 # FIXME: we should be able to better handle de-dents in statements like
299 # multiline strings and multiline expressions (continued with \ or
299 # multiline strings and multiline expressions (continued with \ or
300 # parens). Right now we aren't handling the indentation tracking quite
300 # parens). Right now we aren't handling the indentation tracking quite
301 # correctly with this, though in practice it may not be too much of a
301 # correctly with this, though in practice it may not be too much of a
302 # problem. We'll need to see.
302 # problem. We'll need to see.
303 isp.push("if 1:")
303 isp.push("if 1:")
304 isp.push(" x = (2+")
304 isp.push(" x = (2+")
305 isp.push(" 3)")
305 isp.push(" 3)")
306 self.assertTrue(isp.push_accepts_more())
306 self.assertTrue(isp.push_accepts_more())
307 isp.push(" y = 3")
307 isp.push(" y = 3")
308 self.assertTrue(isp.push_accepts_more())
308 self.assertTrue(isp.push_accepts_more())
309 isp.push('')
309 isp.push('')
310 self.assertFalse(isp.push_accepts_more())
310 self.assertFalse(isp.push_accepts_more())
311
311
312 def test_push_accepts_more5(self):
312 def test_push_accepts_more5(self):
313 isp = self.isp
313 isp = self.isp
314 isp.push('try:')
314 isp.push('try:')
315 isp.push(' a = 5')
315 isp.push(' a = 5')
316 isp.push('except:')
316 isp.push('except:')
317 isp.push(' raise')
317 isp.push(' raise')
318 # We want to be able to add an else: block at this point, so it should
318 # We want to be able to add an else: block at this point, so it should
319 # wait for a blank line.
319 # wait for a blank line.
320 self.assertTrue(isp.push_accepts_more())
320 self.assertTrue(isp.push_accepts_more())
321
321
322 def test_continuation(self):
322 def test_continuation(self):
323 isp = self.isp
323 isp = self.isp
324 isp.push("import os, \\")
324 isp.push("import os, \\")
325 self.assertTrue(isp.push_accepts_more())
325 self.assertTrue(isp.push_accepts_more())
326 isp.push("sys")
326 isp.push("sys")
327 self.assertFalse(isp.push_accepts_more())
327 self.assertFalse(isp.push_accepts_more())
328
328
329 def test_syntax_error(self):
329 def test_syntax_error(self):
330 isp = self.isp
330 isp = self.isp
331 # Syntax errors immediately produce a 'ready' block, so the invalid
331 # Syntax errors immediately produce a 'ready' block, so the invalid
332 # Python can be sent to the kernel for evaluation with possible ipython
332 # Python can be sent to the kernel for evaluation with possible ipython
333 # special-syntax conversion.
333 # special-syntax conversion.
334 isp.push('run foo')
334 isp.push('run foo')
335 self.assertFalse(isp.push_accepts_more())
335 self.assertFalse(isp.push_accepts_more())
336
336
337 def test_unicode(self):
337 def test_unicode(self):
338 self.isp.push(u"PΓ©rez")
338 self.isp.push(u"PΓ©rez")
339 self.isp.push(u'\xc3\xa9')
339 self.isp.push(u'\xc3\xa9')
340 self.isp.push(u"u'\xc3\xa9'")
340 self.isp.push(u"u'\xc3\xa9'")
341
341
342 def test_line_continuation(self):
342 def test_line_continuation(self):
343 """ Test issue #2108."""
343 """ Test issue #2108."""
344 isp = self.isp
344 isp = self.isp
345 # A blank line after a line continuation should not accept more
345 # A blank line after a line continuation should not accept more
346 isp.push("1 \\\n\n")
346 isp.push("1 \\\n\n")
347 self.assertFalse(isp.push_accepts_more())
347 self.assertFalse(isp.push_accepts_more())
348 # Whitespace after a \ is a SyntaxError. The only way to test that
348 # Whitespace after a \ is a SyntaxError. The only way to test that
349 # here is to test that push doesn't accept more (as with
349 # here is to test that push doesn't accept more (as with
350 # test_syntax_error() above).
350 # test_syntax_error() above).
351 isp.push(r"1 \ ")
351 isp.push(r"1 \ ")
352 self.assertFalse(isp.push_accepts_more())
352 self.assertFalse(isp.push_accepts_more())
353 # Even if the line is continuable (c.f. the regular Python
353 # Even if the line is continuable (c.f. the regular Python
354 # interpreter)
354 # interpreter)
355 isp.push(r"(1 \ ")
355 isp.push(r"(1 \ ")
356 self.assertFalse(isp.push_accepts_more())
356 self.assertFalse(isp.push_accepts_more())
357
357
358 class InteractiveLoopTestCase(unittest.TestCase):
358 class InteractiveLoopTestCase(unittest.TestCase):
359 """Tests for an interactive loop like a python shell.
359 """Tests for an interactive loop like a python shell.
360 """
360 """
361 def check_ns(self, lines, ns):
361 def check_ns(self, lines, ns):
362 """Validate that the given input lines produce the resulting namespace.
362 """Validate that the given input lines produce the resulting namespace.
363
363
364 Note: the input lines are given exactly as they would be typed in an
364 Note: the input lines are given exactly as they would be typed in an
365 auto-indenting environment, as mini_interactive_loop above already does
365 auto-indenting environment, as mini_interactive_loop above already does
366 auto-indenting and prepends spaces to the input.
366 auto-indenting and prepends spaces to the input.
367 """
367 """
368 src = mini_interactive_loop(pseudo_input(lines))
368 src = mini_interactive_loop(pseudo_input(lines))
369 test_ns = {}
369 test_ns = {}
370 exec(src, test_ns)
370 exec(src, test_ns)
371 # We can't check that the provided ns is identical to the test_ns,
371 # We can't check that the provided ns is identical to the test_ns,
372 # because Python fills test_ns with extra keys (copyright, etc). But
372 # because Python fills test_ns with extra keys (copyright, etc). But
373 # we can check that the given dict is *contained* in test_ns
373 # we can check that the given dict is *contained* in test_ns
374 for k,v in ns.items():
374 for k,v in ns.items():
375 self.assertEqual(test_ns[k], v)
375 self.assertEqual(test_ns[k], v)
376
376
377 def test_simple(self):
377 def test_simple(self):
378 self.check_ns(['x=1'], dict(x=1))
378 self.check_ns(['x=1'], dict(x=1))
379
379
380 def test_simple2(self):
380 def test_simple2(self):
381 self.check_ns(['if 1:', 'x=2'], dict(x=2))
381 self.check_ns(['if 1:', 'x=2'], dict(x=2))
382
382
383 def test_xy(self):
383 def test_xy(self):
384 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
384 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
385
385
386 def test_abc(self):
386 def test_abc(self):
387 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
387 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
388
388
389 def test_multi(self):
389 def test_multi(self):
390 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
390 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
391
391
392
392
393 class IPythonInputTestCase(InputSplitterTestCase):
393 class IPythonInputTestCase(InputSplitterTestCase):
394 """By just creating a new class whose .isp is a different instance, we
394 """By just creating a new class whose .isp is a different instance, we
395 re-run the same test battery on the new input splitter.
395 re-run the same test battery on the new input splitter.
396
396
397 In addition, this runs the tests over the syntax and syntax_ml dicts that
397 In addition, this runs the tests over the syntax and syntax_ml dicts that
398 were tested by individual functions, as part of the OO interface.
398 were tested by individual functions, as part of the OO interface.
399
399
400 It also makes some checks on the raw buffer storage.
400 It also makes some checks on the raw buffer storage.
401 """
401 """
402
402
403 def setUp(self):
403 def setUp(self):
404 self.isp = isp.IPythonInputSplitter()
404 self.isp = isp.IPythonInputSplitter()
405
405
406 def test_syntax(self):
406 def test_syntax(self):
407 """Call all single-line syntax tests from the main object"""
407 """Call all single-line syntax tests from the main object"""
408 isp = self.isp
408 isp = self.isp
409 for example in syntax.values():
409 for example in syntax.values():
410 for raw, out_t in example:
410 for raw, out_t in example:
411 if raw.startswith(' '):
411 if raw.startswith(' '):
412 continue
412 continue
413
413
414 isp.push(raw+'\n')
414 isp.push(raw+'\n')
415 out_raw = isp.source_raw
415 out_raw = isp.source_raw
416 out = isp.source_reset()
416 out = isp.source_reset()
417 self.assertEqual(out.rstrip(), out_t,
417 self.assertEqual(out.rstrip(), out_t,
418 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
418 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
419 self.assertEqual(out_raw.rstrip(), raw.rstrip())
419 self.assertEqual(out_raw.rstrip(), raw.rstrip())
420
420
421 def test_syntax_multiline(self):
421 def test_syntax_multiline(self):
422 isp = self.isp
422 isp = self.isp
423 for example in syntax_ml.values():
423 for example in syntax_ml.values():
424 for line_pairs in example:
424 for line_pairs in example:
425 out_t_parts = []
425 out_t_parts = []
426 raw_parts = []
426 raw_parts = []
427 for lraw, out_t_part in line_pairs:
427 for lraw, out_t_part in line_pairs:
428 if out_t_part is not None:
428 if out_t_part is not None:
429 out_t_parts.append(out_t_part)
429 out_t_parts.append(out_t_part)
430
430
431 if lraw is not None:
431 if lraw is not None:
432 isp.push(lraw)
432 isp.push(lraw)
433 raw_parts.append(lraw)
433 raw_parts.append(lraw)
434
434
435 out_raw = isp.source_raw
435 out_raw = isp.source_raw
436 out = isp.source_reset()
436 out = isp.source_reset()
437 out_t = '\n'.join(out_t_parts).rstrip()
437 out_t = '\n'.join(out_t_parts).rstrip()
438 raw = '\n'.join(raw_parts).rstrip()
438 raw = '\n'.join(raw_parts).rstrip()
439 self.assertEqual(out.rstrip(), out_t)
439 self.assertEqual(out.rstrip(), out_t)
440 self.assertEqual(out_raw.rstrip(), raw)
440 self.assertEqual(out_raw.rstrip(), raw)
441
441
442 def test_syntax_multiline_cell(self):
442 def test_syntax_multiline_cell(self):
443 isp = self.isp
443 isp = self.isp
444 for example in syntax_ml.values():
444 for example in syntax_ml.values():
445
445
446 out_t_parts = []
446 out_t_parts = []
447 for line_pairs in example:
447 for line_pairs in example:
448 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
448 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
449 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
449 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
450 out = isp.transform_cell(raw)
450 out = isp.transform_cell(raw)
451 # Match ignoring trailing whitespace
451 # Match ignoring trailing whitespace
452 self.assertEqual(out.rstrip(), out_t.rstrip())
452 self.assertEqual(out.rstrip(), out_t.rstrip())
453
453
454 def test_cellmagic_preempt(self):
454 def test_cellmagic_preempt(self):
455 isp = self.isp
455 isp = self.isp
456 for raw, name, line, cell in [
456 for raw, name, line, cell in [
457 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
457 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
458 ("%%cellm \nline\n>>>hi", u'cellm', u'', u'line\n>>>hi'),
458 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
459 (">>>%%cellm \nline\n>>>hi", u'cellm', u'', u'line\nhi'),
459 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
460 ("%%cellm \n>>>hi", u'cellm', u'', u'hi'),
460 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
461 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
461 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
462 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
462 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
463 ]:
463 ]:
464 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
464 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
465 name, line, cell
465 name, line, cell
466 )
466 )
467 out = isp.transform_cell(raw)
467 out = isp.transform_cell(raw)
468 self.assertEqual(out.rstrip(), expected.rstrip())
468 self.assertEqual(out.rstrip(), expected.rstrip())
469
469
470
470
471
471
472 #-----------------------------------------------------------------------------
472 #-----------------------------------------------------------------------------
473 # Main - use as a script, mostly for developer experiments
473 # Main - use as a script, mostly for developer experiments
474 #-----------------------------------------------------------------------------
474 #-----------------------------------------------------------------------------
475
475
476 if __name__ == '__main__':
476 if __name__ == '__main__':
477 # A simple demo for interactive experimentation. This code will not get
477 # A simple demo for interactive experimentation. This code will not get
478 # picked up by any test suite.
478 # picked up by any test suite.
479 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
479 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
480
480
481 # configure here the syntax to use, prompt and whether to autoindent
481 # configure here the syntax to use, prompt and whether to autoindent
482 #isp, start_prompt = InputSplitter(), '>>> '
482 #isp, start_prompt = InputSplitter(), '>>> '
483 isp, start_prompt = IPythonInputSplitter(), 'In> '
483 isp, start_prompt = IPythonInputSplitter(), 'In> '
484
484
485 autoindent = True
485 autoindent = True
486 #autoindent = False
486 #autoindent = False
487
487
488 try:
488 try:
489 while True:
489 while True:
490 prompt = start_prompt
490 prompt = start_prompt
491 while isp.push_accepts_more():
491 while isp.push_accepts_more():
492 indent = ' '*isp.indent_spaces
492 indent = ' '*isp.indent_spaces
493 if autoindent:
493 if autoindent:
494 line = indent + input(prompt+indent)
494 line = indent + input(prompt+indent)
495 else:
495 else:
496 line = input(prompt)
496 line = input(prompt)
497 isp.push(line)
497 isp.push(line)
498 prompt = '... '
498 prompt = '... '
499
499
500 # Here we just return input so we can use it in a test suite, but a
500 # Here we just return input so we can use it in a test suite, but a
501 # real interpreter would instead send it for execution somewhere.
501 # real interpreter would instead send it for execution somewhere.
502 #src = isp.source; raise EOFError # dbg
502 #src = isp.source; raise EOFError # dbg
503 raw = isp.source_raw
503 raw = isp.source_raw
504 src = isp.source_reset()
504 src = isp.source_reset()
505 print('Input source was:\n', src)
505 print('Input source was:\n', src)
506 print('Raw source was:\n', raw)
506 print('Raw source was:\n', raw)
507 except EOFError:
507 except EOFError:
508 print('Bye')
508 print('Bye')
509
509
510 # Tests for cell magics support
510 # Tests for cell magics support
511
511
512 def test_last_blank():
512 def test_last_blank():
513 nt.assert_false(isp.last_blank(''))
513 nt.assert_false(isp.last_blank(''))
514 nt.assert_false(isp.last_blank('abc'))
514 nt.assert_false(isp.last_blank('abc'))
515 nt.assert_false(isp.last_blank('abc\n'))
515 nt.assert_false(isp.last_blank('abc\n'))
516 nt.assert_false(isp.last_blank('abc\na'))
516 nt.assert_false(isp.last_blank('abc\na'))
517
517
518 nt.assert_true(isp.last_blank('\n'))
518 nt.assert_true(isp.last_blank('\n'))
519 nt.assert_true(isp.last_blank('\n '))
519 nt.assert_true(isp.last_blank('\n '))
520 nt.assert_true(isp.last_blank('abc\n '))
520 nt.assert_true(isp.last_blank('abc\n '))
521 nt.assert_true(isp.last_blank('abc\n\n'))
521 nt.assert_true(isp.last_blank('abc\n\n'))
522 nt.assert_true(isp.last_blank('abc\nd\n\n'))
522 nt.assert_true(isp.last_blank('abc\nd\n\n'))
523 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
523 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
524 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
524 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
525
525
526
526
527 def test_last_two_blanks():
527 def test_last_two_blanks():
528 nt.assert_false(isp.last_two_blanks(''))
528 nt.assert_false(isp.last_two_blanks(''))
529 nt.assert_false(isp.last_two_blanks('abc'))
529 nt.assert_false(isp.last_two_blanks('abc'))
530 nt.assert_false(isp.last_two_blanks('abc\n'))
530 nt.assert_false(isp.last_two_blanks('abc\n'))
531 nt.assert_false(isp.last_two_blanks('abc\n\na'))
531 nt.assert_false(isp.last_two_blanks('abc\n\na'))
532 nt.assert_false(isp.last_two_blanks('abc\n \n'))
532 nt.assert_false(isp.last_two_blanks('abc\n \n'))
533 nt.assert_false(isp.last_two_blanks('abc\n\n'))
533 nt.assert_false(isp.last_two_blanks('abc\n\n'))
534
534
535 nt.assert_true(isp.last_two_blanks('\n\n'))
535 nt.assert_true(isp.last_two_blanks('\n\n'))
536 nt.assert_true(isp.last_two_blanks('\n\n '))
536 nt.assert_true(isp.last_two_blanks('\n\n '))
537 nt.assert_true(isp.last_two_blanks('\n \n'))
537 nt.assert_true(isp.last_two_blanks('\n \n'))
538 nt.assert_true(isp.last_two_blanks('abc\n\n '))
538 nt.assert_true(isp.last_two_blanks('abc\n\n '))
539 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
539 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
540 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
540 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
541 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
541 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
542 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
542 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
543 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
543 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
544 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
544 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
545
545
546
546
547 class CellMagicsCommon(object):
547 class CellMagicsCommon(object):
548
548
549 def test_whole_cell(self):
549 def test_whole_cell(self):
550 src = "%%cellm line\nbody\n"
550 src = "%%cellm line\nbody\n"
551 out = self.sp.transform_cell(src)
551 out = self.sp.transform_cell(src)
552 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
552 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
553 nt.assert_equal(out, py3compat.u_format(ref))
553 nt.assert_equal(out, py3compat.u_format(ref))
554
554
555 def test_cellmagic_help(self):
555 def test_cellmagic_help(self):
556 self.sp.push('%%cellm?')
556 self.sp.push('%%cellm?')
557 nt.assert_false(self.sp.push_accepts_more())
557 nt.assert_false(self.sp.push_accepts_more())
558
558
559 def tearDown(self):
559 def tearDown(self):
560 self.sp.reset()
560 self.sp.reset()
561
561
562
562
563 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
563 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
564 sp = isp.IPythonInputSplitter(line_input_checker=False)
564 sp = isp.IPythonInputSplitter(line_input_checker=False)
565
565
566 def test_incremental(self):
566 def test_incremental(self):
567 sp = self.sp
567 sp = self.sp
568 sp.push('%%cellm firstline\n')
568 sp.push('%%cellm firstline\n')
569 nt.assert_true(sp.push_accepts_more()) #1
569 nt.assert_true(sp.push_accepts_more()) #1
570 sp.push('line2\n')
570 sp.push('line2\n')
571 nt.assert_true(sp.push_accepts_more()) #2
571 nt.assert_true(sp.push_accepts_more()) #2
572 sp.push('\n')
572 sp.push('\n')
573 # This should accept a blank line and carry on until the cell is reset
573 # This should accept a blank line and carry on until the cell is reset
574 nt.assert_true(sp.push_accepts_more()) #3
574 nt.assert_true(sp.push_accepts_more()) #3
575
575
576 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
576 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
577 sp = isp.IPythonInputSplitter(line_input_checker=True)
577 sp = isp.IPythonInputSplitter(line_input_checker=True)
578
578
579 def test_incremental(self):
579 def test_incremental(self):
580 sp = self.sp
580 sp = self.sp
581 sp.push('%%cellm line2\n')
581 sp.push('%%cellm line2\n')
582 nt.assert_true(sp.push_accepts_more()) #1
582 nt.assert_true(sp.push_accepts_more()) #1
583 sp.push('\n')
583 sp.push('\n')
584 # In this case, a blank line should end the cell magic
584 # In this case, a blank line should end the cell magic
585 nt.assert_false(sp.push_accepts_more()) #2
585 nt.assert_false(sp.push_accepts_more()) #2
General Comments 0
You need to be logged in to leave comments. Login now