##// END OF EJS Templates
More whitespace between top level functions
Thomas Kluyver -
Show More
@@ -1,333 +1,336 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
34 class InputTransformer(object):
33 class InputTransformer(object):
35 __metaclass__ = abc.ABCMeta
34 __metaclass__ = abc.ABCMeta
36
35
37 @abc.abstractmethod
36 @abc.abstractmethod
38 def push(self, line):
37 def push(self, line):
39 pass
38 pass
40
39
41 @abc.abstractmethod
40 @abc.abstractmethod
42 def reset(self):
41 def reset(self):
43 pass
42 pass
44
43
45 look_in_string = False
44 look_in_string = False
46
45
47 def stateless_input_transformer(func):
46 def stateless_input_transformer(func):
48 class StatelessInputTransformer(InputTransformer):
47 class StatelessInputTransformer(InputTransformer):
49 """Decorator for a stateless input transformer implemented as a function."""
48 """Decorator for a stateless input transformer implemented as a function."""
50 def __init__(self):
49 def __init__(self):
51 self.func = func
50 self.func = func
52
51
53 def push(self, line):
52 def push(self, line):
54 return self.func(line)
53 return self.func(line)
55
54
56 def reset(self):
55 def reset(self):
57 pass
56 pass
58
57
59 return StatelessInputTransformer
58 return StatelessInputTransformer
60
59
61 def coroutine_input_transformer(coro):
60 def coroutine_input_transformer(coro):
62 class CoroutineInputTransformer(InputTransformer):
61 class CoroutineInputTransformer(InputTransformer):
63 def __init__(self):
62 def __init__(self):
64 # Prime it
63 # Prime it
65 self.coro = coro()
64 self.coro = coro()
66 next(self.coro)
65 next(self.coro)
67
66
68 def push(self, line):
67 def push(self, line):
69 return self.coro.send(line)
68 return self.coro.send(line)
70
69
71 def reset(self):
70 def reset(self):
72 return self.coro.send(None)
71 return self.coro.send(None)
73
72
74 return CoroutineInputTransformer
73 return CoroutineInputTransformer
75
74
76
75
77 # Utilities
76 # Utilities
78 def _make_help_call(target, esc, lspace, next_input=None):
77 def _make_help_call(target, esc, lspace, next_input=None):
79 """Prepares a pinfo(2)/psearch call from a target name and the escape
78 """Prepares a pinfo(2)/psearch call from a target name and the escape
80 (i.e. ? or ??)"""
79 (i.e. ? or ??)"""
81 method = 'pinfo2' if esc == '??' \
80 method = 'pinfo2' if esc == '??' \
82 else 'psearch' if '*' in target \
81 else 'psearch' if '*' in target \
83 else 'pinfo'
82 else 'pinfo'
84 arg = " ".join([method, target])
83 arg = " ".join([method, target])
85 if next_input is None:
84 if next_input is None:
86 return '%sget_ipython().magic(%r)' % (lspace, arg)
85 return '%sget_ipython().magic(%r)' % (lspace, arg)
87 else:
86 else:
88 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
87 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
89 (lspace, next_input, arg)
88 (lspace, next_input, arg)
90
89
91 @coroutine_input_transformer
90 @coroutine_input_transformer
92 def escaped_transformer():
91 def escaped_transformer():
93 """Translate lines beginning with one of IPython's escape characters."""
92 """Translate lines beginning with one of IPython's escape characters."""
94
93
95 # These define the transformations for the different escape characters.
94 # These define the transformations for the different escape characters.
96 def _tr_system(line_info):
95 def _tr_system(line_info):
97 "Translate lines escaped with: !"
96 "Translate lines escaped with: !"
98 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
97 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
99 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
98 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
100
99
101 def _tr_system2(line_info):
100 def _tr_system2(line_info):
102 "Translate lines escaped with: !!"
101 "Translate lines escaped with: !!"
103 cmd = line_info.line.lstrip()[2:]
102 cmd = line_info.line.lstrip()[2:]
104 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
103 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
105
104
106 def _tr_help(line_info):
105 def _tr_help(line_info):
107 "Translate lines escaped with: ?/??"
106 "Translate lines escaped with: ?/??"
108 # A naked help line should just fire the intro help screen
107 # A naked help line should just fire the intro help screen
109 if not line_info.line[1:]:
108 if not line_info.line[1:]:
110 return 'get_ipython().show_usage()'
109 return 'get_ipython().show_usage()'
111
110
112 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
111 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
113
112
114 def _tr_magic(line_info):
113 def _tr_magic(line_info):
115 "Translate lines escaped with: %"
114 "Translate lines escaped with: %"
116 tpl = '%sget_ipython().magic(%r)'
115 tpl = '%sget_ipython().magic(%r)'
117 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
116 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
118 return tpl % (line_info.pre, cmd)
117 return tpl % (line_info.pre, cmd)
119
118
120 def _tr_quote(line_info):
119 def _tr_quote(line_info):
121 "Translate lines escaped with: ,"
120 "Translate lines escaped with: ,"
122 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
121 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
123 '", "'.join(line_info.the_rest.split()) )
122 '", "'.join(line_info.the_rest.split()) )
124
123
125 def _tr_quote2(line_info):
124 def _tr_quote2(line_info):
126 "Translate lines escaped with: ;"
125 "Translate lines escaped with: ;"
127 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
126 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
128 line_info.the_rest)
127 line_info.the_rest)
129
128
130 def _tr_paren(line_info):
129 def _tr_paren(line_info):
131 "Translate lines escaped with: /"
130 "Translate lines escaped with: /"
132 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
131 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
133 ", ".join(line_info.the_rest.split()))
132 ", ".join(line_info.the_rest.split()))
134
133
135 tr = { ESC_SHELL : _tr_system,
134 tr = { ESC_SHELL : _tr_system,
136 ESC_SH_CAP : _tr_system2,
135 ESC_SH_CAP : _tr_system2,
137 ESC_HELP : _tr_help,
136 ESC_HELP : _tr_help,
138 ESC_HELP2 : _tr_help,
137 ESC_HELP2 : _tr_help,
139 ESC_MAGIC : _tr_magic,
138 ESC_MAGIC : _tr_magic,
140 ESC_QUOTE : _tr_quote,
139 ESC_QUOTE : _tr_quote,
141 ESC_QUOTE2 : _tr_quote2,
140 ESC_QUOTE2 : _tr_quote2,
142 ESC_PAREN : _tr_paren }
141 ESC_PAREN : _tr_paren }
143
142
144 line = ''
143 line = ''
145 while True:
144 while True:
146 line = (yield line)
145 line = (yield line)
147 if not line or line.isspace():
146 if not line or line.isspace():
148 continue
147 continue
149 lineinf = LineInfo(line)
148 lineinf = LineInfo(line)
150 if lineinf.esc not in tr:
149 if lineinf.esc not in tr:
151 continue
150 continue
152
151
153 parts = []
152 parts = []
154 while line is not None:
153 while line is not None:
155 parts.append(line.rstrip('\\'))
154 parts.append(line.rstrip('\\'))
156 if not line.endswith('\\'):
155 if not line.endswith('\\'):
157 break
156 break
158 line = (yield None)
157 line = (yield None)
159
158
160 # Output
159 # Output
161 lineinf = LineInfo(' '.join(parts))
160 lineinf = LineInfo(' '.join(parts))
162 line = tr[lineinf.esc](lineinf)
161 line = tr[lineinf.esc](lineinf)
163
162
164 _initial_space_re = re.compile(r'\s*')
163 _initial_space_re = re.compile(r'\s*')
165
164
166 _help_end_re = re.compile(r"""(%{0,2}
165 _help_end_re = re.compile(r"""(%{0,2}
167 [a-zA-Z_*][\w*]* # Variable name
166 [a-zA-Z_*][\w*]* # Variable name
168 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
167 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
169 )
168 )
170 (\?\??)$ # ? or ??""",
169 (\?\??)$ # ? or ??""",
171 re.VERBOSE)
170 re.VERBOSE)
172
171
173 def has_comment(src):
172 def has_comment(src):
174 """Indicate whether an input line has (i.e. ends in, or is) a comment.
173 """Indicate whether an input line has (i.e. ends in, or is) a comment.
175
174
176 This uses tokenize, so it can distinguish comments from # inside strings.
175 This uses tokenize, so it can distinguish comments from # inside strings.
177
176
178 Parameters
177 Parameters
179 ----------
178 ----------
180 src : string
179 src : string
181 A single line input string.
180 A single line input string.
182
181
183 Returns
182 Returns
184 -------
183 -------
185 Boolean: True if source has a comment.
184 Boolean: True if source has a comment.
186 """
185 """
187 readline = StringIO(src).readline
186 readline = StringIO(src).readline
188 toktypes = set()
187 toktypes = set()
189 try:
188 try:
190 for t in tokenize.generate_tokens(readline):
189 for t in tokenize.generate_tokens(readline):
191 toktypes.add(t[0])
190 toktypes.add(t[0])
192 except tokenize.TokenError:
191 except tokenize.TokenError:
193 pass
192 pass
194 return(tokenize.COMMENT in toktypes)
193 return(tokenize.COMMENT in toktypes)
195
194
195
196 @stateless_input_transformer
196 @stateless_input_transformer
197 def help_end(line):
197 def help_end(line):
198 """Translate lines with ?/?? at the end"""
198 """Translate lines with ?/?? at the end"""
199 m = _help_end_re.search(line)
199 m = _help_end_re.search(line)
200 if m is None or has_comment(line):
200 if m is None or has_comment(line):
201 return line
201 return line
202 target = m.group(1)
202 target = m.group(1)
203 esc = m.group(3)
203 esc = m.group(3)
204 lspace = _initial_space_re.match(line).group(0)
204 lspace = _initial_space_re.match(line).group(0)
205
205
206 # If we're mid-command, put it back on the next prompt for the user.
206 # 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
207 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
208
208
209 return _make_help_call(target, esc, lspace, next_input)
209 return _make_help_call(target, esc, lspace, next_input)
210
210
211
211
212 @coroutine_input_transformer
212 @coroutine_input_transformer
213 def cellmagic():
213 def cellmagic():
214 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
214 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
215 cellmagic_help_re = re.compile('%%\w+\?')
215 cellmagic_help_re = re.compile('%%\w+\?')
216 line = ''
216 line = ''
217 while True:
217 while True:
218 line = (yield line)
218 line = (yield line)
219 if (not line) or (not line.startswith(ESC_MAGIC2)):
219 if (not line) or (not line.startswith(ESC_MAGIC2)):
220 continue
220 continue
221
221
222 if cellmagic_help_re.match(line):
222 if cellmagic_help_re.match(line):
223 # This case will be handled by help_end
223 # This case will be handled by help_end
224 continue
224 continue
225
225
226 first = line
226 first = line
227 body = []
227 body = []
228 line = (yield None)
228 line = (yield None)
229 while (line is not None) and (line.strip() != ''):
229 while (line is not None) and (line.strip() != ''):
230 body.append(line)
230 body.append(line)
231 line = (yield None)
231 line = (yield None)
232
232
233 # Output
233 # Output
234 magic_name, _, first = first.partition(' ')
234 magic_name, _, first = first.partition(' ')
235 magic_name = magic_name.lstrip(ESC_MAGIC2)
235 magic_name = magic_name.lstrip(ESC_MAGIC2)
236 line = tpl % (magic_name, first, u'\n'.join(body))
236 line = tpl % (magic_name, first, u'\n'.join(body))
237
237
238
238 def _strip_prompts(prompt1_re, prompt2_re):
239 def _strip_prompts(prompt1_re, prompt2_re):
239 """Remove matching input prompts from a block of input."""
240 """Remove matching input prompts from a block of input."""
240 line = ''
241 line = ''
241 while True:
242 while True:
242 line = (yield line)
243 line = (yield line)
243
244
244 if line is None:
245 if line is None:
245 continue
246 continue
246
247
247 m = prompt1_re.match(line)
248 m = prompt1_re.match(line)
248 if m:
249 if m:
249 while m:
250 while m:
250 line = (yield line[len(m.group(0)):])
251 line = (yield line[len(m.group(0)):])
251 if line is None:
252 if line is None:
252 break
253 break
253 m = prompt2_re.match(line)
254 m = prompt2_re.match(line)
254 else:
255 else:
255 # Prompts not in input - wait for reset
256 # Prompts not in input - wait for reset
256 while line is not None:
257 while line is not None:
257 line = (yield line)
258 line = (yield line)
258
259
259 @coroutine_input_transformer
260 @coroutine_input_transformer
260 def classic_prompt():
261 def classic_prompt():
261 prompt1_re = re.compile(r'^(>>> )')
262 prompt1_re = re.compile(r'^(>>> )')
262 prompt2_re = re.compile(r'^(>>> |^\.\.\. )')
263 prompt2_re = re.compile(r'^(>>> |^\.\.\. )')
263 return _strip_prompts(prompt1_re, prompt2_re)
264 return _strip_prompts(prompt1_re, prompt2_re)
264
265
265 classic_prompt.look_in_string = True
266 classic_prompt.look_in_string = True
266
267
267 @coroutine_input_transformer
268 @coroutine_input_transformer
268 def ipy_prompt():
269 def ipy_prompt():
269 prompt1_re = re.compile(r'^In \[\d+\]: ')
270 prompt1_re = re.compile(r'^In \[\d+\]: ')
270 prompt2_re = re.compile(r'^(In \[\d+\]: |^\ \ \ \.\.\.+: )')
271 prompt2_re = re.compile(r'^(In \[\d+\]: |^\ \ \ \.\.\.+: )')
271 return _strip_prompts(prompt1_re, prompt2_re)
272 return _strip_prompts(prompt1_re, prompt2_re)
272
273
273 ipy_prompt.look_in_string = True
274 ipy_prompt.look_in_string = True
274
275
276
275 @coroutine_input_transformer
277 @coroutine_input_transformer
276 def leading_indent():
278 def leading_indent():
277 space_re = re.compile(r'^[ \t]+')
279 space_re = re.compile(r'^[ \t]+')
278 line = ''
280 line = ''
279 while True:
281 while True:
280 line = (yield line)
282 line = (yield line)
281
283
282 if line is None:
284 if line is None:
283 continue
285 continue
284
286
285 m = space_re.match(line)
287 m = space_re.match(line)
286 if m:
288 if m:
287 space = m.group(0)
289 space = m.group(0)
288 while line is not None:
290 while line is not None:
289 if line.startswith(space):
291 if line.startswith(space):
290 line = line[len(space):]
292 line = line[len(space):]
291 line = (yield line)
293 line = (yield line)
292 else:
294 else:
293 # No leading spaces - wait for reset
295 # No leading spaces - wait for reset
294 while line is not None:
296 while line is not None:
295 line = (yield line)
297 line = (yield line)
296
298
297 leading_indent.look_in_string = True
299 leading_indent.look_in_string = True
298
300
301
299 def _special_assignment(assignment_re, template):
302 def _special_assignment(assignment_re, template):
300 line = ''
303 line = ''
301 while True:
304 while True:
302 line = (yield line)
305 line = (yield line)
303 if not line or line.isspace():
306 if not line or line.isspace():
304 continue
307 continue
305
308
306 m = assignment_re.match(line)
309 m = assignment_re.match(line)
307 if not m:
310 if not m:
308 continue
311 continue
309
312
310 parts = []
313 parts = []
311 while line is not None:
314 while line is not None:
312 parts.append(line.rstrip('\\'))
315 parts.append(line.rstrip('\\'))
313 if not line.endswith('\\'):
316 if not line.endswith('\\'):
314 break
317 break
315 line = (yield None)
318 line = (yield None)
316
319
317 # Output
320 # Output
318 whole = assignment_re.match(' '.join(parts))
321 whole = assignment_re.match(' '.join(parts))
319 line = template % (whole.group('lhs'), whole.group('cmd'))
322 line = template % (whole.group('lhs'), whole.group('cmd'))
320
323
321 @coroutine_input_transformer
324 @coroutine_input_transformer
322 def assign_from_system():
325 def assign_from_system():
323 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
326 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
324 r'\s*=\s*!\s*(?P<cmd>.*)')
327 r'\s*=\s*!\s*(?P<cmd>.*)')
325 template = '%s = get_ipython().getoutput(%r)'
328 template = '%s = get_ipython().getoutput(%r)'
326 return _special_assignment(assignment_re, template)
329 return _special_assignment(assignment_re, template)
327
330
328 @coroutine_input_transformer
331 @coroutine_input_transformer
329 def assign_from_magic():
332 def assign_from_magic():
330 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
333 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
331 r'\s*=\s*%\s*(?P<cmd>.*)')
334 r'\s*=\s*%\s*(?P<cmd>.*)')
332 template = '%s = get_ipython().magic(%r)'
335 template = '%s = get_ipython().magic(%r)'
333 return _special_assignment(assignment_re, template)
336 return _special_assignment(assignment_re, template)
General Comments 0
You need to be logged in to leave comments. Login now