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