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