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