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