##// END OF EJS Templates
don't close string from inside re.VERBOSE comment...
MinRK -
Show More
@@ -1,472 +1,473 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 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
230 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
231 return tpl % (line_info.pre, cmd)
231 return tpl % (line_info.pre, cmd)
232
232
233 def _tr_quote(line_info):
233 def _tr_quote(line_info):
234 "Translate lines escaped with: ,"
234 "Translate lines escaped with: ,"
235 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
235 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
236 '", "'.join(line_info.the_rest.split()) )
236 '", "'.join(line_info.the_rest.split()) )
237
237
238 def _tr_quote2(line_info):
238 def _tr_quote2(line_info):
239 "Translate lines escaped with: ;"
239 "Translate lines escaped with: ;"
240 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
240 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
241 line_info.the_rest)
241 line_info.the_rest)
242
242
243 def _tr_paren(line_info):
243 def _tr_paren(line_info):
244 "Translate lines escaped with: /"
244 "Translate lines escaped with: /"
245 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
245 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
246 ", ".join(line_info.the_rest.split()))
246 ", ".join(line_info.the_rest.split()))
247
247
248 tr = { ESC_SHELL : _tr_system,
248 tr = { ESC_SHELL : _tr_system,
249 ESC_SH_CAP : _tr_system2,
249 ESC_SH_CAP : _tr_system2,
250 ESC_HELP : _tr_help,
250 ESC_HELP : _tr_help,
251 ESC_HELP2 : _tr_help,
251 ESC_HELP2 : _tr_help,
252 ESC_MAGIC : _tr_magic,
252 ESC_MAGIC : _tr_magic,
253 ESC_QUOTE : _tr_quote,
253 ESC_QUOTE : _tr_quote,
254 ESC_QUOTE2 : _tr_quote2,
254 ESC_QUOTE2 : _tr_quote2,
255 ESC_PAREN : _tr_paren }
255 ESC_PAREN : _tr_paren }
256
256
257 @StatelessInputTransformer.wrap
257 @StatelessInputTransformer.wrap
258 def escaped_commands(line):
258 def escaped_commands(line):
259 """Transform escaped commands - %magic, !system, ?help + various autocalls.
259 """Transform escaped commands - %magic, !system, ?help + various autocalls.
260 """
260 """
261 if not line or line.isspace():
261 if not line or line.isspace():
262 return line
262 return line
263 lineinf = LineInfo(line)
263 lineinf = LineInfo(line)
264 if lineinf.esc not in tr:
264 if lineinf.esc not in tr:
265 return line
265 return line
266
266
267 return tr[lineinf.esc](lineinf)
267 return tr[lineinf.esc](lineinf)
268
268
269 _initial_space_re = re.compile(r'\s*')
269 _initial_space_re = re.compile(r'\s*')
270
270
271 _help_end_re = re.compile(r"""(%{0,2}
271 _help_end_re = re.compile(r"""(%{0,2}
272 [a-zA-Z_*][\w*]* # Variable name
272 [a-zA-Z_*][\w*]* # Variable name
273 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
273 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
274 )
274 )
275 (\?\??)$ # ? or ??""",
275 (\?\??)$ # ? or ??
276 """,
276 re.VERBOSE)
277 re.VERBOSE)
277
278
278 def has_comment(src):
279 def has_comment(src):
279 """Indicate whether an input line has (i.e. ends in, or is) a comment.
280 """Indicate whether an input line has (i.e. ends in, or is) a comment.
280
281
281 This uses tokenize, so it can distinguish comments from # inside strings.
282 This uses tokenize, so it can distinguish comments from # inside strings.
282
283
283 Parameters
284 Parameters
284 ----------
285 ----------
285 src : string
286 src : string
286 A single line input string.
287 A single line input string.
287
288
288 Returns
289 Returns
289 -------
290 -------
290 comment : bool
291 comment : bool
291 True if source has a comment.
292 True if source has a comment.
292 """
293 """
293 readline = StringIO(src).readline
294 readline = StringIO(src).readline
294 toktypes = set()
295 toktypes = set()
295 try:
296 try:
296 for t in generate_tokens(readline):
297 for t in generate_tokens(readline):
297 toktypes.add(t[0])
298 toktypes.add(t[0])
298 except TokenError:
299 except TokenError:
299 pass
300 pass
300 return(tokenize2.COMMENT in toktypes)
301 return(tokenize2.COMMENT in toktypes)
301
302
302
303
303 @StatelessInputTransformer.wrap
304 @StatelessInputTransformer.wrap
304 def help_end(line):
305 def help_end(line):
305 """Translate lines with ?/?? at the end"""
306 """Translate lines with ?/?? at the end"""
306 m = _help_end_re.search(line)
307 m = _help_end_re.search(line)
307 if m is None or has_comment(line):
308 if m is None or has_comment(line):
308 return line
309 return line
309 target = m.group(1)
310 target = m.group(1)
310 esc = m.group(3)
311 esc = m.group(3)
311 lspace = _initial_space_re.match(line).group(0)
312 lspace = _initial_space_re.match(line).group(0)
312
313
313 # If we're mid-command, put it back on the next prompt for the user.
314 # If we're mid-command, put it back on the next prompt for the user.
314 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
315 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
315
316
316 return _make_help_call(target, esc, lspace, next_input)
317 return _make_help_call(target, esc, lspace, next_input)
317
318
318
319
319 @CoroutineInputTransformer.wrap
320 @CoroutineInputTransformer.wrap
320 def cellmagic(end_on_blank_line=False):
321 def cellmagic(end_on_blank_line=False):
321 """Captures & transforms cell magics.
322 """Captures & transforms cell magics.
322
323
323 After a cell magic is started, this stores up any lines it gets until it is
324 After a cell magic is started, this stores up any lines it gets until it is
324 reset (sent None).
325 reset (sent None).
325 """
326 """
326 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
327 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
327 cellmagic_help_re = re.compile('%%\w+\?')
328 cellmagic_help_re = re.compile('%%\w+\?')
328 line = ''
329 line = ''
329 while True:
330 while True:
330 line = (yield line)
331 line = (yield line)
331 if (not line) or (not line.startswith(ESC_MAGIC2)):
332 if (not line) or (not line.startswith(ESC_MAGIC2)):
332 continue
333 continue
333
334
334 if cellmagic_help_re.match(line):
335 if cellmagic_help_re.match(line):
335 # This case will be handled by help_end
336 # This case will be handled by help_end
336 continue
337 continue
337
338
338 first = line
339 first = line
339 body = []
340 body = []
340 line = (yield None)
341 line = (yield None)
341 while (line is not None) and \
342 while (line is not None) and \
342 ((line.strip() != '') or not end_on_blank_line):
343 ((line.strip() != '') or not end_on_blank_line):
343 body.append(line)
344 body.append(line)
344 line = (yield None)
345 line = (yield None)
345
346
346 # Output
347 # Output
347 magic_name, _, first = first.partition(' ')
348 magic_name, _, first = first.partition(' ')
348 magic_name = magic_name.lstrip(ESC_MAGIC2)
349 magic_name = magic_name.lstrip(ESC_MAGIC2)
349 line = tpl % (magic_name, first, u'\n'.join(body))
350 line = tpl % (magic_name, first, u'\n'.join(body))
350
351
351
352
352 def _strip_prompts(prompt_re):
353 def _strip_prompts(prompt_re):
353 """Remove matching input prompts from a block of input."""
354 """Remove matching input prompts from a block of input."""
354 line = ''
355 line = ''
355 while True:
356 while True:
356 line = (yield line)
357 line = (yield line)
357
358
358 # First line of cell
359 # First line of cell
359 if line is None:
360 if line is None:
360 continue
361 continue
361 out, n1 = prompt_re.subn('', line, count=1)
362 out, n1 = prompt_re.subn('', line, count=1)
362 line = (yield out)
363 line = (yield out)
363
364
364 # Second line of cell, because people often copy from just after the
365 # Second line of cell, because people often copy from just after the
365 # first prompt, so we might not see it in the first line.
366 # first prompt, so we might not see it in the first line.
366 if line is None:
367 if line is None:
367 continue
368 continue
368 out, n2 = prompt_re.subn('', line, count=1)
369 out, n2 = prompt_re.subn('', line, count=1)
369 line = (yield out)
370 line = (yield out)
370
371
371 if n1 or n2:
372 if n1 or n2:
372 # Found the input prompt in the first two lines - check for it in
373 # Found the input prompt in the first two lines - check for it in
373 # the rest of the cell as well.
374 # the rest of the cell as well.
374 while line is not None:
375 while line is not None:
375 line = (yield prompt_re.sub('', line, count=1))
376 line = (yield prompt_re.sub('', line, count=1))
376
377
377 else:
378 else:
378 # Prompts not in input - wait for reset
379 # Prompts not in input - wait for reset
379 while line is not None:
380 while line is not None:
380 line = (yield line)
381 line = (yield line)
381
382
382 @CoroutineInputTransformer.wrap
383 @CoroutineInputTransformer.wrap
383 def classic_prompt():
384 def classic_prompt():
384 """Strip the >>>/... prompts of the Python interactive shell."""
385 """Strip the >>>/... prompts of the Python interactive shell."""
385 # FIXME: non-capturing version (?:...) usable?
386 # FIXME: non-capturing version (?:...) usable?
386 prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)')
387 prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)')
387 return _strip_prompts(prompt_re)
388 return _strip_prompts(prompt_re)
388
389
389 @CoroutineInputTransformer.wrap
390 @CoroutineInputTransformer.wrap
390 def ipy_prompt():
391 def ipy_prompt():
391 """Strip IPython's In [1]:/...: prompts."""
392 """Strip IPython's In [1]:/...: prompts."""
392 # FIXME: non-capturing version (?:...) usable?
393 # FIXME: non-capturing version (?:...) usable?
393 # FIXME: r'^(In \[\d+\]: | {3}\.{3,}: )' clearer?
394 # FIXME: r'^(In \[\d+\]: | {3}\.{3,}: )' clearer?
394 prompt_re = re.compile(r'^(In \[\d+\]: |\ \ \ \.\.\.+: )')
395 prompt_re = re.compile(r'^(In \[\d+\]: |\ \ \ \.\.\.+: )')
395 return _strip_prompts(prompt_re)
396 return _strip_prompts(prompt_re)
396
397
397
398
398 @CoroutineInputTransformer.wrap
399 @CoroutineInputTransformer.wrap
399 def leading_indent():
400 def leading_indent():
400 """Remove leading indentation.
401 """Remove leading indentation.
401
402
402 If the first line starts with a spaces or tabs, the same whitespace will be
403 If the first line starts with a spaces or tabs, the same whitespace will be
403 removed from each following line until it is reset.
404 removed from each following line until it is reset.
404 """
405 """
405 space_re = re.compile(r'^[ \t]+')
406 space_re = re.compile(r'^[ \t]+')
406 line = ''
407 line = ''
407 while True:
408 while True:
408 line = (yield line)
409 line = (yield line)
409
410
410 if line is None:
411 if line is None:
411 continue
412 continue
412
413
413 m = space_re.match(line)
414 m = space_re.match(line)
414 if m:
415 if m:
415 space = m.group(0)
416 space = m.group(0)
416 while line is not None:
417 while line is not None:
417 if line.startswith(space):
418 if line.startswith(space):
418 line = line[len(space):]
419 line = line[len(space):]
419 line = (yield line)
420 line = (yield line)
420 else:
421 else:
421 # No leading spaces - wait for reset
422 # No leading spaces - wait for reset
422 while line is not None:
423 while line is not None:
423 line = (yield line)
424 line = (yield line)
424
425
425
426
426 @CoroutineInputTransformer.wrap
427 @CoroutineInputTransformer.wrap
427 def strip_encoding_cookie():
428 def strip_encoding_cookie():
428 """Remove encoding comment if found in first two lines
429 """Remove encoding comment if found in first two lines
429
430
430 If the first or second line has the `# coding: utf-8` comment,
431 If the first or second line has the `# coding: utf-8` comment,
431 it will be removed.
432 it will be removed.
432 """
433 """
433 line = ''
434 line = ''
434 while True:
435 while True:
435 line = (yield line)
436 line = (yield line)
436 # check comment on first two lines
437 # check comment on first two lines
437 for i in range(2):
438 for i in range(2):
438 if line is None:
439 if line is None:
439 break
440 break
440 if cookie_comment_re.match(line):
441 if cookie_comment_re.match(line):
441 line = (yield "")
442 line = (yield "")
442 else:
443 else:
443 line = (yield line)
444 line = (yield line)
444
445
445 # no-op on the rest of the cell
446 # no-op on the rest of the cell
446 while line is not None:
447 while line is not None:
447 line = (yield line)
448 line = (yield line)
448
449
449
450
450 assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
451 assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
451 r'\s*=\s*!\s*(?P<cmd>.*)')
452 r'\s*=\s*!\s*(?P<cmd>.*)')
452 assign_system_template = '%s = get_ipython().getoutput(%r)'
453 assign_system_template = '%s = get_ipython().getoutput(%r)'
453 @StatelessInputTransformer.wrap
454 @StatelessInputTransformer.wrap
454 def assign_from_system(line):
455 def assign_from_system(line):
455 """Transform assignment from system commands (e.g. files = !ls)"""
456 """Transform assignment from system commands (e.g. files = !ls)"""
456 m = assign_system_re.match(line)
457 m = assign_system_re.match(line)
457 if m is None:
458 if m is None:
458 return line
459 return line
459
460
460 return assign_system_template % m.group('lhs', 'cmd')
461 return assign_system_template % m.group('lhs', 'cmd')
461
462
462 assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
463 assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
463 r'\s*=\s*%\s*(?P<cmd>.*)')
464 r'\s*=\s*%\s*(?P<cmd>.*)')
464 assign_magic_template = '%s = get_ipython().magic(%r)'
465 assign_magic_template = '%s = get_ipython().magic(%r)'
465 @StatelessInputTransformer.wrap
466 @StatelessInputTransformer.wrap
466 def assign_from_magic(line):
467 def assign_from_magic(line):
467 """Transform assignment from magic commands (e.g. a = %who_ls)"""
468 """Transform assignment from magic commands (e.g. a = %who_ls)"""
468 m = assign_magic_re.match(line)
469 m = assign_magic_re.match(line)
469 if m is None:
470 if m is None:
470 return line
471 return line
471
472
472 return assign_magic_template % m.group('lhs', 'cmd')
473 return assign_magic_template % m.group('lhs', 'cmd')
General Comments 0
You need to be logged in to leave comments. Login now