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