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