##// END OF EJS Templates
adjust strip_prompts logic a bit further...
MinRK -
Show More
@@ -1,498 +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, continuation_re=None):
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
364
365 prompt_re is stripped only once, on either the first or second line.
365 Parameters
366 If prompt_re is found on one of the first two lines,
366 ----------
367 continuation_re is stripped from lines thereafter.
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.
368
374
369 If continuation_re is unspecified, prompt_re will be used for both.
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.
370 """
379 """
371 if continuation_re is None:
380 if initial_re is None:
372 continuation_re = prompt_re
381 initial_re = prompt_re
373 line = ''
382 line = ''
374 while True:
383 while True:
375 line = (yield line)
384 line = (yield line)
376
385
377 # First line of cell
386 # First line of cell
378 if line is None:
387 if line is None:
379 continue
388 continue
380 out, n1 = prompt_re.subn('', line, count=1)
389 out, n1 = initial_re.subn('', line, count=1)
381 line = (yield out)
390 line = (yield out)
382
391
383 # Second line of cell, because people often copy from just after the
384 # first prompt, so we might not see it in the first line.
385 if line is None:
392 if line is None:
386 continue
393 continue
387 # check for first prompt if not found on first line, continuation otherwise
394 # check for any prompt on the second line of the cell,
388 if n1:
395 # because people often copy from just after the first prompt,
389 pat = continuation_re
396 # so we might not see it in the first line.
390 else:
397 out, n2 = prompt_re.subn('', line, count=1)
391 pat = prompt_re
392 out, n2 = pat.subn('', line, count=1)
393 line = (yield out)
398 line = (yield out)
394
399
395 if n1 or n2:
400 if n1 or n2:
396 # 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
397 # the rest of the cell as well.
402 # the rest of the cell as well.
398 while line is not None:
403 while line is not None:
399 line = (yield continuation_re.sub('', line, count=1))
404 line = (yield prompt_re.sub('', line, count=1))
400
405
401 else:
406 else:
402 # Prompts not in input - wait for reset
407 # Prompts not in input - wait for reset
403 while line is not None:
408 while line is not None:
404 line = (yield line)
409 line = (yield line)
405
410
406 @CoroutineInputTransformer.wrap
411 @CoroutineInputTransformer.wrap
407 def classic_prompt():
412 def classic_prompt():
408 """Strip the >>>/... prompts of the Python interactive shell."""
413 """Strip the >>>/... prompts of the Python interactive shell."""
409 # FIXME: non-capturing version (?:...) usable?
414 # FIXME: non-capturing version (?:...) usable?
410 prompt_re = re.compile(r'^(>>> ?)')
415 prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)')
411 continuation_re = re.compile(r'^(>>> ?|\.\.\. ?)')
416 initial_re = re.compile(r'^(>>> ?)')
412 return _strip_prompts(prompt_re, continuation_re)
417 return _strip_prompts(prompt_re, initial_re)
413
418
414 @CoroutineInputTransformer.wrap
419 @CoroutineInputTransformer.wrap
415 def ipy_prompt():
420 def ipy_prompt():
416 """Strip IPython's In [1]:/...: prompts."""
421 """Strip IPython's In [1]:/...: prompts."""
417 # FIXME: non-capturing version (?:...) usable?
422 # FIXME: non-capturing version (?:...) usable?
418 # FIXME: r'^(In \[\d+\]: | {3}\.{3,}: )' clearer?
423 # FIXME: r'^(In \[\d+\]: | {3}\.{3,}: )' clearer?
419 prompt_re = re.compile(r'^(In \[\d+\]: )')
424 prompt_re = re.compile(r'^(In \[\d+\]: |\ \ \ \.\.\.+: )')
420 continuation_re = re.compile(r'^(In \[\d+\]: |\ \ \ \.\.\.+: )')
425 return _strip_prompts(prompt_re)
421 return _strip_prompts(prompt_re, continuation_re)
422
426
423
427
424 @CoroutineInputTransformer.wrap
428 @CoroutineInputTransformer.wrap
425 def leading_indent():
429 def leading_indent():
426 """Remove leading indentation.
430 """Remove leading indentation.
427
431
428 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
429 removed from each following line until it is reset.
433 removed from each following line until it is reset.
430 """
434 """
431 space_re = re.compile(r'^[ \t]+')
435 space_re = re.compile(r'^[ \t]+')
432 line = ''
436 line = ''
433 while True:
437 while True:
434 line = (yield line)
438 line = (yield line)
435
439
436 if line is None:
440 if line is None:
437 continue
441 continue
438
442
439 m = space_re.match(line)
443 m = space_re.match(line)
440 if m:
444 if m:
441 space = m.group(0)
445 space = m.group(0)
442 while line is not None:
446 while line is not None:
443 if line.startswith(space):
447 if line.startswith(space):
444 line = line[len(space):]
448 line = line[len(space):]
445 line = (yield line)
449 line = (yield line)
446 else:
450 else:
447 # No leading spaces - wait for reset
451 # No leading spaces - wait for reset
448 while line is not None:
452 while line is not None:
449 line = (yield line)
453 line = (yield line)
450
454
451
455
452 @CoroutineInputTransformer.wrap
456 @CoroutineInputTransformer.wrap
453 def strip_encoding_cookie():
457 def strip_encoding_cookie():
454 """Remove encoding comment if found in first two lines
458 """Remove encoding comment if found in first two lines
455
459
456 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,
457 it will be removed.
461 it will be removed.
458 """
462 """
459 line = ''
463 line = ''
460 while True:
464 while True:
461 line = (yield line)
465 line = (yield line)
462 # check comment on first two lines
466 # check comment on first two lines
463 for i in range(2):
467 for i in range(2):
464 if line is None:
468 if line is None:
465 break
469 break
466 if cookie_comment_re.match(line):
470 if cookie_comment_re.match(line):
467 line = (yield "")
471 line = (yield "")
468 else:
472 else:
469 line = (yield line)
473 line = (yield line)
470
474
471 # no-op on the rest of the cell
475 # no-op on the rest of the cell
472 while line is not None:
476 while line is not None:
473 line = (yield line)
477 line = (yield line)
474
478
475
479
476 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\.]+)*))'
477 r'\s*=\s*!\s*(?P<cmd>.*)')
481 r'\s*=\s*!\s*(?P<cmd>.*)')
478 assign_system_template = '%s = get_ipython().getoutput(%r)'
482 assign_system_template = '%s = get_ipython().getoutput(%r)'
479 @StatelessInputTransformer.wrap
483 @StatelessInputTransformer.wrap
480 def assign_from_system(line):
484 def assign_from_system(line):
481 """Transform assignment from system commands (e.g. files = !ls)"""
485 """Transform assignment from system commands (e.g. files = !ls)"""
482 m = assign_system_re.match(line)
486 m = assign_system_re.match(line)
483 if m is None:
487 if m is None:
484 return line
488 return line
485
489
486 return assign_system_template % m.group('lhs', 'cmd')
490 return assign_system_template % m.group('lhs', 'cmd')
487
491
488 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\.]+)*))'
489 r'\s*=\s*%\s*(?P<cmd>.*)')
493 r'\s*=\s*%\s*(?P<cmd>.*)')
490 assign_magic_template = '%s = get_ipython().magic(%r)'
494 assign_magic_template = '%s = get_ipython().magic(%r)'
491 @StatelessInputTransformer.wrap
495 @StatelessInputTransformer.wrap
492 def assign_from_magic(line):
496 def assign_from_magic(line):
493 """Transform assignment from magic commands (e.g. a = %who_ls)"""
497 """Transform assignment from magic commands (e.g. a = %who_ls)"""
494 m = assign_magic_re.match(line)
498 m = assign_magic_re.match(line)
495 if m is None:
499 if m is None:
496 return line
500 return line
497
501
498 return assign_magic_template % m.group('lhs', 'cmd')
502 return assign_magic_template % m.group('lhs', 'cmd')
@@ -1,456 +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 ],
185 [('a="""','a="""'),
186 ('>>> 123','123'),
187 ('... 456"""','456"""'),
184 ],
188 ],
185 [('a="""','a="""'),
189 [('a="""','a="""'),
186 ('123','123'),
190 ('123','123'),
187 ('... 456"""','... 456"""'),
191 ('... 456"""','... 456"""'),
188 ],
192 ],
189 [('....__class__','....__class__'),
193 [('....__class__','....__class__'),
190 ],
194 ],
191 [('a=5', 'a=5'),
195 [('a=5', 'a=5'),
192 ('...', '...'),
196 ('...', ''),
193 ],
197 ],
194 [('>>> def f(x):', 'def f(x):'),
198 [('>>> def f(x):', 'def f(x):'),
195 ('...', ''),
199 ('...', ''),
196 ('... return x', ' return x'),
200 ('... return x', ' return x'),
197 ],
201 ],
198 ],
202 ],
199
203
200 ipy_prompt =
204 ipy_prompt =
201 [ [('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):'),
202 (' ....: print i',' print i'),
206 (' ....: print i',' print i'),
203 (' ....: ', ''),
207 (' ....: ', ''),
204 ],
208 ],
205 [('In [2]: a="""','a="""'),
209 [('In [2]: a="""','a="""'),
206 (' ...: 123"""','123"""'),
210 (' ...: 123"""','123"""'),
207 ],
211 ],
208 [('a="""','a="""'),
212 [('a="""','a="""'),
209 (' ...: 123',' ...: 123'),
213 (' ...: 123','123'),
210 (' ...: 456"""',' ...: 456"""'),
214 (' ...: 456"""','456"""'),
215 ],
216 [('a="""','a="""'),
217 ('In [1]: 123','123'),
218 (' ...: 456"""','456"""'),
211 ],
219 ],
212 [('a="""','a="""'),
220 [('a="""','a="""'),
213 ('123','123'),
221 ('123','123'),
214 (' ...: 456"""',' ...: 456"""'),
222 (' ...: 456"""',' ...: 456"""'),
215 ],
223 ],
216 ],
224 ],
217
225
218 strip_encoding_cookie =
226 strip_encoding_cookie =
219 [
227 [
220 [
228 [
221 ('# -*- coding: utf-8 -*-', ''),
229 ('# -*- coding: utf-8 -*-', ''),
222 ('foo', 'foo'),
230 ('foo', 'foo'),
223 ],
231 ],
224 [
232 [
225 ('#!/usr/bin/env python', '#!/usr/bin/env python'),
233 ('#!/usr/bin/env python', '#!/usr/bin/env python'),
226 ('# -*- coding: latin-1 -*-', ''),
234 ('# -*- coding: latin-1 -*-', ''),
227 # only the first-two lines
235 # only the first-two lines
228 ('# -*- coding: latin-1 -*-', '# -*- coding: latin-1 -*-'),
236 ('# -*- coding: latin-1 -*-', '# -*- coding: latin-1 -*-'),
229 ],
237 ],
230 ],
238 ],
231
239
232 multiline_datastructure_prompt =
240 multiline_datastructure_prompt =
233 [ [('>>> a = [1,','a = [1,'),
241 [ [('>>> a = [1,','a = [1,'),
234 ('... 2]','2]'),
242 ('... 2]','2]'),
235 ],
243 ],
236 ],
244 ],
237
245
238 multiline_datastructure =
246 multiline_datastructure =
239 [ [('b = ("%s"', None),
247 [ [('b = ("%s"', None),
240 ('# comment', None),
248 ('# comment', None),
241 ('%foo )', 'b = ("%s"\n# comment\n%foo )'),
249 ('%foo )', 'b = ("%s"\n# comment\n%foo )'),
242 ],
250 ],
243 ],
251 ],
244
252
245 leading_indent =
253 leading_indent =
246 [ [(' print "hi"','print "hi"'),
254 [ [(' print "hi"','print "hi"'),
247 ],
255 ],
248 [(' for a in range(5):','for a in range(5):'),
256 [(' for a in range(5):','for a in range(5):'),
249 (' a*2',' a*2'),
257 (' a*2',' a*2'),
250 ],
258 ],
251 [(' a="""','a="""'),
259 [(' a="""','a="""'),
252 (' 123"""','123"""'),
260 (' 123"""','123"""'),
253 ],
261 ],
254 [('a="""','a="""'),
262 [('a="""','a="""'),
255 (' 123"""',' 123"""'),
263 (' 123"""',' 123"""'),
256 ],
264 ],
257 ],
265 ],
258
266
259 cellmagic =
267 cellmagic =
260 [ [(u'%%foo a', None),
268 [ [(u'%%foo a', None),
261 (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}'')")),
262 ],
270 ],
263 [(u'%%bar 123', None),
271 [(u'%%bar 123', None),
264 (u'hello', None),
272 (u'hello', None),
265 (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')")),
266 ],
274 ],
267 [(u'a=5', 'a=5'),
275 [(u'a=5', 'a=5'),
268 (u'%%cellmagic', '%%cellmagic'),
276 (u'%%cellmagic', '%%cellmagic'),
269 ],
277 ],
270 ],
278 ],
271
279
272 escaped =
280 escaped =
273 [ [('%abc def \\', None),
281 [ [('%abc def \\', None),
274 ('ghi', u_fmt("get_ipython().magic({u}'abc def ghi')")),
282 ('ghi', u_fmt("get_ipython().magic({u}'abc def ghi')")),
275 ],
283 ],
276 [('%abc def \\', None),
284 [('%abc def \\', None),
277 ('ghi\\', None),
285 ('ghi\\', None),
278 (None, u_fmt("get_ipython().magic({u}'abc def ghi')")),
286 (None, u_fmt("get_ipython().magic({u}'abc def ghi')")),
279 ],
287 ],
280 ],
288 ],
281
289
282 assign_magic =
290 assign_magic =
283 [ [(u'a = %bc de \\', None),
291 [ [(u'a = %bc de \\', None),
284 (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')")),
285 ],
293 ],
286 [(u'a = %bc de \\', None),
294 [(u'a = %bc de \\', None),
287 (u'fg\\', None),
295 (u'fg\\', None),
288 (None, u_fmt("a = get_ipython().magic({u}'bc de fg')")),
296 (None, u_fmt("a = get_ipython().magic({u}'bc de fg')")),
289 ],
297 ],
290 ],
298 ],
291
299
292 assign_system =
300 assign_system =
293 [ [(u'a = !bc de \\', None),
301 [ [(u'a = !bc de \\', None),
294 (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')")),
295 ],
303 ],
296 [(u'a = !bc de \\', None),
304 [(u'a = !bc de \\', None),
297 (u'fg\\', None),
305 (u'fg\\', None),
298 (None, u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
306 (None, u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
299 ],
307 ],
300 ],
308 ],
301 )
309 )
302
310
303
311
304 def test_assign_system():
312 def test_assign_system():
305 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'])
306
314
307 def test_assign_magic():
315 def test_assign_magic():
308 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'])
309
317
310 def test_classic_prompt():
318 def test_classic_prompt():
311 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'])
312 for example in syntax_ml['classic_prompt']:
320 for example in syntax_ml['classic_prompt']:
313 transform_checker(example, ipt.classic_prompt)
321 transform_checker(example, ipt.classic_prompt)
314 for example in syntax_ml['multiline_datastructure_prompt']:
322 for example in syntax_ml['multiline_datastructure_prompt']:
315 transform_checker(example, ipt.classic_prompt)
323 transform_checker(example, ipt.classic_prompt)
316
324
317
325
318 def test_ipy_prompt():
326 def test_ipy_prompt():
319 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'])
320 for example in syntax_ml['ipy_prompt']:
328 for example in syntax_ml['ipy_prompt']:
321 transform_checker(example, ipt.ipy_prompt)
329 transform_checker(example, ipt.ipy_prompt)
322
330
323 def test_coding_cookie():
331 def test_coding_cookie():
324 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'])
325 for example in syntax_ml['strip_encoding_cookie']:
333 for example in syntax_ml['strip_encoding_cookie']:
326 transform_checker(example, ipt.strip_encoding_cookie)
334 transform_checker(example, ipt.strip_encoding_cookie)
327
335
328 def test_assemble_logical_lines():
336 def test_assemble_logical_lines():
329 tests = \
337 tests = \
330 [ [(u"a = \\", None),
338 [ [(u"a = \\", None),
331 (u"123", u"a = 123"),
339 (u"123", u"a = 123"),
332 ],
340 ],
333 [(u"a = \\", None), # Test resetting when within a multi-line string
341 [(u"a = \\", None), # Test resetting when within a multi-line string
334 (u"12 *\\", None),
342 (u"12 *\\", None),
335 (None, u"a = 12 *"),
343 (None, u"a = 12 *"),
336 ],
344 ],
337 [(u"# foo\\", u"# foo\\"), # Comments can't be continued like this
345 [(u"# foo\\", u"# foo\\"), # Comments can't be continued like this
338 ],
346 ],
339 ]
347 ]
340 for example in tests:
348 for example in tests:
341 transform_checker(example, ipt.assemble_logical_lines)
349 transform_checker(example, ipt.assemble_logical_lines)
342
350
343 def test_assemble_python_lines():
351 def test_assemble_python_lines():
344 tests = \
352 tests = \
345 [ [(u"a = '''", None),
353 [ [(u"a = '''", None),
346 (u"abc'''", u"a = '''\nabc'''"),
354 (u"abc'''", u"a = '''\nabc'''"),
347 ],
355 ],
348 [(u"a = '''", None), # Test resetting when within a multi-line string
356 [(u"a = '''", None), # Test resetting when within a multi-line string
349 (u"def", None),
357 (u"def", None),
350 (None, u"a = '''\ndef"),
358 (None, u"a = '''\ndef"),
351 ],
359 ],
352 [(u"a = [1,", None),
360 [(u"a = [1,", None),
353 (u"2]", u"a = [1,\n2]"),
361 (u"2]", u"a = [1,\n2]"),
354 ],
362 ],
355 [(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
356 (u"2,", None),
364 (u"2,", None),
357 (None, u"a = [1,\n2,"),
365 (None, u"a = [1,\n2,"),
358 ],
366 ],
359 ] + syntax_ml['multiline_datastructure']
367 ] + syntax_ml['multiline_datastructure']
360 for example in tests:
368 for example in tests:
361 transform_checker(example, ipt.assemble_python_lines)
369 transform_checker(example, ipt.assemble_python_lines)
362
370
363
371
364 def test_help_end():
372 def test_help_end():
365 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'])
366
374
367 def test_escaped_noesc():
375 def test_escaped_noesc():
368 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'])
369
377
370
378
371 def test_escaped_shell():
379 def test_escaped_shell():
372 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'])
373
381
374
382
375 def test_escaped_help():
383 def test_escaped_help():
376 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'])
377
385
378
386
379 def test_escaped_magic():
387 def test_escaped_magic():
380 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'])
381
389
382
390
383 def test_escaped_quote():
391 def test_escaped_quote():
384 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'])
385
393
386
394
387 def test_escaped_quote2():
395 def test_escaped_quote2():
388 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'])
389
397
390
398
391 def test_escaped_paren():
399 def test_escaped_paren():
392 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'])
393
401
394
402
395 def test_cellmagic():
403 def test_cellmagic():
396 for example in syntax_ml['cellmagic']:
404 for example in syntax_ml['cellmagic']:
397 transform_checker(example, ipt.cellmagic)
405 transform_checker(example, ipt.cellmagic)
398
406
399 line_example = [(u'%%bar 123', None),
407 line_example = [(u'%%bar 123', None),
400 (u'hello', None),
408 (u'hello', None),
401 (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')")),
402 ]
410 ]
403 transform_checker(line_example, ipt.cellmagic, end_on_blank_line=True)
411 transform_checker(line_example, ipt.cellmagic, end_on_blank_line=True)
404
412
405 def test_has_comment():
413 def test_has_comment():
406 tests = [('text', False),
414 tests = [('text', False),
407 ('text #comment', True),
415 ('text #comment', True),
408 ('text #comment\n', True),
416 ('text #comment\n', True),
409 ('#comment', True),
417 ('#comment', True),
410 ('#comment\n', True),
418 ('#comment\n', True),
411 ('a = "#string"', False),
419 ('a = "#string"', False),
412 ('a = "#string" # comment', True),
420 ('a = "#string" # comment', True),
413 ('a #comment not "string"', True),
421 ('a #comment not "string"', True),
414 ]
422 ]
415 tt.check_pairs(ipt.has_comment, tests)
423 tt.check_pairs(ipt.has_comment, tests)
416
424
417 @ipt.TokenInputTransformer.wrap
425 @ipt.TokenInputTransformer.wrap
418 def decistmt(tokens):
426 def decistmt(tokens):
419 """Substitute Decimals for floats in a string of statements.
427 """Substitute Decimals for floats in a string of statements.
420
428
421 Based on an example from the tokenize module docs.
429 Based on an example from the tokenize module docs.
422 """
430 """
423 result = []
431 result = []
424 for toknum, tokval, _, _, _ in tokens:
432 for toknum, tokval, _, _, _ in tokens:
425 if toknum == tokenize.NUMBER and '.' in tokval: # replace NUMBER tokens
433 if toknum == tokenize.NUMBER and '.' in tokval: # replace NUMBER tokens
426 for newtok in [
434 for newtok in [
427 (tokenize.NAME, 'Decimal'),
435 (tokenize.NAME, 'Decimal'),
428 (tokenize.OP, '('),
436 (tokenize.OP, '('),
429 (tokenize.STRING, repr(tokval)),
437 (tokenize.STRING, repr(tokval)),
430 (tokenize.OP, ')')
438 (tokenize.OP, ')')
431 ]:
439 ]:
432 yield newtok
440 yield newtok
433 else:
441 else:
434 yield (toknum, tokval)
442 yield (toknum, tokval)
435
443
436
444
437
445
438 def test_token_input_transformer():
446 def test_token_input_transformer():
439 tests = [(u'1.2', u_fmt(u"Decimal ({u}'1.2')")),
447 tests = [(u'1.2', u_fmt(u"Decimal ({u}'1.2')")),
440 (u'"1.2"', u'"1.2"'),
448 (u'"1.2"', u'"1.2"'),
441 ]
449 ]
442 tt.check_pairs(transform_and_reset(decistmt), tests)
450 tt.check_pairs(transform_and_reset(decistmt), tests)
443 ml_tests = \
451 ml_tests = \
444 [ [(u"a = 1.2; b = '''x", None),
452 [ [(u"a = 1.2; b = '''x", None),
445 (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'''")),
446 ],
454 ],
447 [(u"a = [1.2,", None),
455 [(u"a = [1.2,", None),
448 (u"3]", u_fmt(u"a =[Decimal ({u}'1.2'),\n3 ]")),
456 (u"3]", u_fmt(u"a =[Decimal ({u}'1.2'),\n3 ]")),
449 ],
457 ],
450 [(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
451 (u"bar", None),
459 (u"bar", None),
452 (None, u"a = '''foo\nbar"),
460 (None, u"a = '''foo\nbar"),
453 ],
461 ],
454 ]
462 ]
455 for example in ml_tests:
463 for example in ml_tests:
456 transform_checker(example, decistmt)
464 transform_checker(example, decistmt)
General Comments 0
You need to be logged in to leave comments. Login now