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