##// END OF EJS Templates
remove PY3
Srinivas Reddy Thatiparthy -
Show More
@@ -1,537 +1,532 b''
1 """Input transformer classes to support IPython special syntax.
1 """Input transformer classes to support IPython special syntax.
2
2
3 This includes the machinery to recognise and transform ``%magic`` commands,
3 This includes the machinery to recognise and transform ``%magic`` commands,
4 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
4 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
5 """
5 """
6 import abc
6 import abc
7 import functools
7 import functools
8 import re
8 import re
9 from io import StringIO
9
10
10 from IPython.core.splitinput import LineInfo
11 from IPython.core.splitinput import LineInfo
11 from IPython.utils import tokenize2
12 from IPython.utils import tokenize2
12 from IPython.utils.py3compat import PY3
13 from IPython.utils.tokenize2 import generate_tokens, untokenize, TokenError
13 from IPython.utils.tokenize2 import generate_tokens, untokenize, TokenError
14
14
15 if PY3:
16 from io import StringIO
17 else:
18 from StringIO import StringIO
19
20 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
21 # Globals
16 # Globals
22 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
23
18
24 # The escape sequences that define the syntax transformations IPython will
19 # The escape sequences that define the syntax transformations IPython will
25 # apply to user input. These can NOT be just changed here: many regular
20 # apply to user input. These can NOT be just changed here: many regular
26 # expressions and other parts of the code may use their hardcoded values, and
21 # expressions and other parts of the code may use their hardcoded values, and
27 # for all intents and purposes they constitute the 'IPython syntax', so they
22 # for all intents and purposes they constitute the 'IPython syntax', so they
28 # should be considered fixed.
23 # should be considered fixed.
29
24
30 ESC_SHELL = '!' # Send line to underlying system shell
25 ESC_SHELL = '!' # Send line to underlying system shell
31 ESC_SH_CAP = '!!' # Send line to system shell and capture output
26 ESC_SH_CAP = '!!' # Send line to system shell and capture output
32 ESC_HELP = '?' # Find information about object
27 ESC_HELP = '?' # Find information about object
33 ESC_HELP2 = '??' # Find extra-detailed information about object
28 ESC_HELP2 = '??' # Find extra-detailed information about object
34 ESC_MAGIC = '%' # Call magic function
29 ESC_MAGIC = '%' # Call magic function
35 ESC_MAGIC2 = '%%' # Call cell-magic function
30 ESC_MAGIC2 = '%%' # Call cell-magic function
36 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
31 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
37 ESC_QUOTE2 = ';' # Quote all args as a single string, call
32 ESC_QUOTE2 = ';' # Quote all args as a single string, call
38 ESC_PAREN = '/' # Call first argument with rest of line as arguments
33 ESC_PAREN = '/' # Call first argument with rest of line as arguments
39
34
40 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
35 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
41 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
36 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
42 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
37 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
43
38
44
39
45 class InputTransformer(metaclass=abc.ABCMeta):
40 class InputTransformer(metaclass=abc.ABCMeta):
46 """Abstract base class for line-based input transformers."""
41 """Abstract base class for line-based input transformers."""
47
42
48 @abc.abstractmethod
43 @abc.abstractmethod
49 def push(self, line):
44 def push(self, line):
50 """Send a line of input to the transformer, returning the transformed
45 """Send a line of input to the transformer, returning the transformed
51 input or None if the transformer is waiting for more input.
46 input or None if the transformer is waiting for more input.
52
47
53 Must be overridden by subclasses.
48 Must be overridden by subclasses.
54
49
55 Implementations may raise ``SyntaxError`` if the input is invalid. No
50 Implementations may raise ``SyntaxError`` if the input is invalid. No
56 other exceptions may be raised.
51 other exceptions may be raised.
57 """
52 """
58 pass
53 pass
59
54
60 @abc.abstractmethod
55 @abc.abstractmethod
61 def reset(self):
56 def reset(self):
62 """Return, transformed any lines that the transformer has accumulated,
57 """Return, transformed any lines that the transformer has accumulated,
63 and reset its internal state.
58 and reset its internal state.
64
59
65 Must be overridden by subclasses.
60 Must be overridden by subclasses.
66 """
61 """
67 pass
62 pass
68
63
69 @classmethod
64 @classmethod
70 def wrap(cls, func):
65 def wrap(cls, func):
71 """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
72 will allow instantiation with the decorated object.
67 will allow instantiation with the decorated object.
73 """
68 """
74 @functools.wraps(func)
69 @functools.wraps(func)
75 def transformer_factory(**kwargs):
70 def transformer_factory(**kwargs):
76 return cls(func, **kwargs)
71 return cls(func, **kwargs)
77
72
78 return transformer_factory
73 return transformer_factory
79
74
80 class StatelessInputTransformer(InputTransformer):
75 class StatelessInputTransformer(InputTransformer):
81 """Wrapper for a stateless input transformer implemented as a function."""
76 """Wrapper for a stateless input transformer implemented as a function."""
82 def __init__(self, func):
77 def __init__(self, func):
83 self.func = func
78 self.func = func
84
79
85 def __repr__(self):
80 def __repr__(self):
86 return "StatelessInputTransformer(func={0!r})".format(self.func)
81 return "StatelessInputTransformer(func={0!r})".format(self.func)
87
82
88 def push(self, line):
83 def push(self, line):
89 """Send a line of input to the transformer, returning the
84 """Send a line of input to the transformer, returning the
90 transformed input."""
85 transformed input."""
91 return self.func(line)
86 return self.func(line)
92
87
93 def reset(self):
88 def reset(self):
94 """No-op - exists for compatibility."""
89 """No-op - exists for compatibility."""
95 pass
90 pass
96
91
97 class CoroutineInputTransformer(InputTransformer):
92 class CoroutineInputTransformer(InputTransformer):
98 """Wrapper for an input transformer implemented as a coroutine."""
93 """Wrapper for an input transformer implemented as a coroutine."""
99 def __init__(self, coro, **kwargs):
94 def __init__(self, coro, **kwargs):
100 # Prime it
95 # Prime it
101 self.coro = coro(**kwargs)
96 self.coro = coro(**kwargs)
102 next(self.coro)
97 next(self.coro)
103
98
104 def __repr__(self):
99 def __repr__(self):
105 return "CoroutineInputTransformer(coro={0!r})".format(self.coro)
100 return "CoroutineInputTransformer(coro={0!r})".format(self.coro)
106
101
107 def push(self, line):
102 def push(self, line):
108 """Send a line of input to the transformer, returning the
103 """Send a line of input to the transformer, returning the
109 transformed input or None if the transformer is waiting for more
104 transformed input or None if the transformer is waiting for more
110 input.
105 input.
111 """
106 """
112 return self.coro.send(line)
107 return self.coro.send(line)
113
108
114 def reset(self):
109 def reset(self):
115 """Return, transformed any lines that the transformer has
110 """Return, transformed any lines that the transformer has
116 accumulated, and reset its internal state.
111 accumulated, and reset its internal state.
117 """
112 """
118 return self.coro.send(None)
113 return self.coro.send(None)
119
114
120 class TokenInputTransformer(InputTransformer):
115 class TokenInputTransformer(InputTransformer):
121 """Wrapper for a token-based input transformer.
116 """Wrapper for a token-based input transformer.
122
117
123 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
124 return an iterable which can be passed to tokenize.untokenize().
119 return an iterable which can be passed to tokenize.untokenize().
125 """
120 """
126 def __init__(self, func):
121 def __init__(self, func):
127 self.func = func
122 self.func = func
128 self.current_line = ""
123 self.current_line = ""
129 self.line_used = False
124 self.line_used = False
130 self.reset_tokenizer()
125 self.reset_tokenizer()
131
126
132 def reset_tokenizer(self):
127 def reset_tokenizer(self):
133 self.tokenizer = generate_tokens(self.get_line)
128 self.tokenizer = generate_tokens(self.get_line)
134
129
135 def get_line(self):
130 def get_line(self):
136 if self.line_used:
131 if self.line_used:
137 raise TokenError
132 raise TokenError
138 self.line_used = True
133 self.line_used = True
139 return self.current_line
134 return self.current_line
140
135
141 def push(self, line):
136 def push(self, line):
142 self.current_line += line + "\n"
137 self.current_line += line + "\n"
143 if self.current_line.isspace():
138 if self.current_line.isspace():
144 return self.reset()
139 return self.reset()
145
140
146 self.line_used = False
141 self.line_used = False
147 tokens = []
142 tokens = []
148 stop_at_NL = False
143 stop_at_NL = False
149 try:
144 try:
150 for intok in self.tokenizer:
145 for intok in self.tokenizer:
151 tokens.append(intok)
146 tokens.append(intok)
152 t = intok[0]
147 t = intok[0]
153 if t == tokenize2.NEWLINE or (stop_at_NL and t == tokenize2.NL):
148 if t == tokenize2.NEWLINE or (stop_at_NL and t == tokenize2.NL):
154 # 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
155 break
150 break
156 elif t == tokenize2.ERRORTOKEN:
151 elif t == tokenize2.ERRORTOKEN:
157 stop_at_NL = True
152 stop_at_NL = True
158 except TokenError:
153 except TokenError:
159 # Multi-line statement - stop and try again with the next line
154 # Multi-line statement - stop and try again with the next line
160 self.reset_tokenizer()
155 self.reset_tokenizer()
161 return None
156 return None
162
157
163 return self.output(tokens)
158 return self.output(tokens)
164
159
165 def output(self, tokens):
160 def output(self, tokens):
166 self.current_line = ""
161 self.current_line = ""
167 self.reset_tokenizer()
162 self.reset_tokenizer()
168 return untokenize(self.func(tokens)).rstrip('\n')
163 return untokenize(self.func(tokens)).rstrip('\n')
169
164
170 def reset(self):
165 def reset(self):
171 l = self.current_line
166 l = self.current_line
172 self.current_line = ""
167 self.current_line = ""
173 self.reset_tokenizer()
168 self.reset_tokenizer()
174 if l:
169 if l:
175 return l.rstrip('\n')
170 return l.rstrip('\n')
176
171
177 class assemble_python_lines(TokenInputTransformer):
172 class assemble_python_lines(TokenInputTransformer):
178 def __init__(self):
173 def __init__(self):
179 super(assemble_python_lines, self).__init__(None)
174 super(assemble_python_lines, self).__init__(None)
180
175
181 def output(self, tokens):
176 def output(self, tokens):
182 return self.reset()
177 return self.reset()
183
178
184 @CoroutineInputTransformer.wrap
179 @CoroutineInputTransformer.wrap
185 def assemble_logical_lines():
180 def assemble_logical_lines():
186 """Join lines following explicit line continuations (\)"""
181 """Join lines following explicit line continuations (\)"""
187 line = ''
182 line = ''
188 while True:
183 while True:
189 line = (yield line)
184 line = (yield line)
190 if not line or line.isspace():
185 if not line or line.isspace():
191 continue
186 continue
192
187
193 parts = []
188 parts = []
194 while line is not None:
189 while line is not None:
195 if line.endswith('\\') and (not has_comment(line)):
190 if line.endswith('\\') and (not has_comment(line)):
196 parts.append(line[:-1])
191 parts.append(line[:-1])
197 line = (yield None) # Get another line
192 line = (yield None) # Get another line
198 else:
193 else:
199 parts.append(line)
194 parts.append(line)
200 break
195 break
201
196
202 # Output
197 # Output
203 line = ''.join(parts)
198 line = ''.join(parts)
204
199
205 # Utilities
200 # Utilities
206 def _make_help_call(target, esc, lspace, next_input=None):
201 def _make_help_call(target, esc, lspace, next_input=None):
207 """Prepares a pinfo(2)/psearch call from a target name and the escape
202 """Prepares a pinfo(2)/psearch call from a target name and the escape
208 (i.e. ? or ??)"""
203 (i.e. ? or ??)"""
209 method = 'pinfo2' if esc == '??' \
204 method = 'pinfo2' if esc == '??' \
210 else 'psearch' if '*' in target \
205 else 'psearch' if '*' in target \
211 else 'pinfo'
206 else 'pinfo'
212 arg = " ".join([method, target])
207 arg = " ".join([method, target])
213 if next_input is None:
208 if next_input is None:
214 return '%sget_ipython().magic(%r)' % (lspace, arg)
209 return '%sget_ipython().magic(%r)' % (lspace, arg)
215 else:
210 else:
216 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
211 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
217 (lspace, next_input, arg)
212 (lspace, next_input, arg)
218
213
219 # These define the transformations for the different escape characters.
214 # These define the transformations for the different escape characters.
220 def _tr_system(line_info):
215 def _tr_system(line_info):
221 "Translate lines escaped with: !"
216 "Translate lines escaped with: !"
222 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
217 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
223 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
218 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
224
219
225 def _tr_system2(line_info):
220 def _tr_system2(line_info):
226 "Translate lines escaped with: !!"
221 "Translate lines escaped with: !!"
227 cmd = line_info.line.lstrip()[2:]
222 cmd = line_info.line.lstrip()[2:]
228 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
223 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
229
224
230 def _tr_help(line_info):
225 def _tr_help(line_info):
231 "Translate lines escaped with: ?/??"
226 "Translate lines escaped with: ?/??"
232 # A naked help line should just fire the intro help screen
227 # A naked help line should just fire the intro help screen
233 if not line_info.line[1:]:
228 if not line_info.line[1:]:
234 return 'get_ipython().show_usage()'
229 return 'get_ipython().show_usage()'
235
230
236 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
231 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
237
232
238 def _tr_magic(line_info):
233 def _tr_magic(line_info):
239 "Translate lines escaped with: %"
234 "Translate lines escaped with: %"
240 tpl = '%sget_ipython().magic(%r)'
235 tpl = '%sget_ipython().magic(%r)'
241 if line_info.line.startswith(ESC_MAGIC2):
236 if line_info.line.startswith(ESC_MAGIC2):
242 return line_info.line
237 return line_info.line
243 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
238 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
244 return tpl % (line_info.pre, cmd)
239 return tpl % (line_info.pre, cmd)
245
240
246 def _tr_quote(line_info):
241 def _tr_quote(line_info):
247 "Translate lines escaped with: ,"
242 "Translate lines escaped with: ,"
248 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
243 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
249 '", "'.join(line_info.the_rest.split()) )
244 '", "'.join(line_info.the_rest.split()) )
250
245
251 def _tr_quote2(line_info):
246 def _tr_quote2(line_info):
252 "Translate lines escaped with: ;"
247 "Translate lines escaped with: ;"
253 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
248 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
254 line_info.the_rest)
249 line_info.the_rest)
255
250
256 def _tr_paren(line_info):
251 def _tr_paren(line_info):
257 "Translate lines escaped with: /"
252 "Translate lines escaped with: /"
258 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
253 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
259 ", ".join(line_info.the_rest.split()))
254 ", ".join(line_info.the_rest.split()))
260
255
261 tr = { ESC_SHELL : _tr_system,
256 tr = { ESC_SHELL : _tr_system,
262 ESC_SH_CAP : _tr_system2,
257 ESC_SH_CAP : _tr_system2,
263 ESC_HELP : _tr_help,
258 ESC_HELP : _tr_help,
264 ESC_HELP2 : _tr_help,
259 ESC_HELP2 : _tr_help,
265 ESC_MAGIC : _tr_magic,
260 ESC_MAGIC : _tr_magic,
266 ESC_QUOTE : _tr_quote,
261 ESC_QUOTE : _tr_quote,
267 ESC_QUOTE2 : _tr_quote2,
262 ESC_QUOTE2 : _tr_quote2,
268 ESC_PAREN : _tr_paren }
263 ESC_PAREN : _tr_paren }
269
264
270 @StatelessInputTransformer.wrap
265 @StatelessInputTransformer.wrap
271 def escaped_commands(line):
266 def escaped_commands(line):
272 """Transform escaped commands - %magic, !system, ?help + various autocalls.
267 """Transform escaped commands - %magic, !system, ?help + various autocalls.
273 """
268 """
274 if not line or line.isspace():
269 if not line or line.isspace():
275 return line
270 return line
276 lineinf = LineInfo(line)
271 lineinf = LineInfo(line)
277 if lineinf.esc not in tr:
272 if lineinf.esc not in tr:
278 return line
273 return line
279
274
280 return tr[lineinf.esc](lineinf)
275 return tr[lineinf.esc](lineinf)
281
276
282 _initial_space_re = re.compile(r'\s*')
277 _initial_space_re = re.compile(r'\s*')
283
278
284 _help_end_re = re.compile(r"""(%{0,2}
279 _help_end_re = re.compile(r"""(%{0,2}
285 [a-zA-Z_*][\w*]* # Variable name
280 [a-zA-Z_*][\w*]* # Variable name
286 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
281 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
287 )
282 )
288 (\?\??)$ # ? or ??
283 (\?\??)$ # ? or ??
289 """,
284 """,
290 re.VERBOSE)
285 re.VERBOSE)
291
286
292 # Extra pseudotokens for multiline strings and data structures
287 # Extra pseudotokens for multiline strings and data structures
293 _MULTILINE_STRING = object()
288 _MULTILINE_STRING = object()
294 _MULTILINE_STRUCTURE = object()
289 _MULTILINE_STRUCTURE = object()
295
290
296 def _line_tokens(line):
291 def _line_tokens(line):
297 """Helper for has_comment and ends_in_comment_or_string."""
292 """Helper for has_comment and ends_in_comment_or_string."""
298 readline = StringIO(line).readline
293 readline = StringIO(line).readline
299 toktypes = set()
294 toktypes = set()
300 try:
295 try:
301 for t in generate_tokens(readline):
296 for t in generate_tokens(readline):
302 toktypes.add(t[0])
297 toktypes.add(t[0])
303 except TokenError as e:
298 except TokenError as e:
304 # There are only two cases where a TokenError is raised.
299 # There are only two cases where a TokenError is raised.
305 if 'multi-line string' in e.args[0]:
300 if 'multi-line string' in e.args[0]:
306 toktypes.add(_MULTILINE_STRING)
301 toktypes.add(_MULTILINE_STRING)
307 else:
302 else:
308 toktypes.add(_MULTILINE_STRUCTURE)
303 toktypes.add(_MULTILINE_STRUCTURE)
309 return toktypes
304 return toktypes
310
305
311 def has_comment(src):
306 def has_comment(src):
312 """Indicate whether an input line has (i.e. ends in, or is) a comment.
307 """Indicate whether an input line has (i.e. ends in, or is) a comment.
313
308
314 This uses tokenize, so it can distinguish comments from # inside strings.
309 This uses tokenize, so it can distinguish comments from # inside strings.
315
310
316 Parameters
311 Parameters
317 ----------
312 ----------
318 src : string
313 src : string
319 A single line input string.
314 A single line input string.
320
315
321 Returns
316 Returns
322 -------
317 -------
323 comment : bool
318 comment : bool
324 True if source has a comment.
319 True if source has a comment.
325 """
320 """
326 return (tokenize2.COMMENT in _line_tokens(src))
321 return (tokenize2.COMMENT in _line_tokens(src))
327
322
328 def ends_in_comment_or_string(src):
323 def ends_in_comment_or_string(src):
329 """Indicates whether or not an input line ends in a comment or within
324 """Indicates whether or not an input line ends in a comment or within
330 a multiline string.
325 a multiline string.
331
326
332 Parameters
327 Parameters
333 ----------
328 ----------
334 src : string
329 src : string
335 A single line input string.
330 A single line input string.
336
331
337 Returns
332 Returns
338 -------
333 -------
339 comment : bool
334 comment : bool
340 True if source ends in a comment or multiline string.
335 True if source ends in a comment or multiline string.
341 """
336 """
342 toktypes = _line_tokens(src)
337 toktypes = _line_tokens(src)
343 return (tokenize2.COMMENT in toktypes) or (_MULTILINE_STRING in toktypes)
338 return (tokenize2.COMMENT in toktypes) or (_MULTILINE_STRING in toktypes)
344
339
345
340
346 @StatelessInputTransformer.wrap
341 @StatelessInputTransformer.wrap
347 def help_end(line):
342 def help_end(line):
348 """Translate lines with ?/?? at the end"""
343 """Translate lines with ?/?? at the end"""
349 m = _help_end_re.search(line)
344 m = _help_end_re.search(line)
350 if m is None or ends_in_comment_or_string(line):
345 if m is None or ends_in_comment_or_string(line):
351 return line
346 return line
352 target = m.group(1)
347 target = m.group(1)
353 esc = m.group(3)
348 esc = m.group(3)
354 lspace = _initial_space_re.match(line).group(0)
349 lspace = _initial_space_re.match(line).group(0)
355
350
356 # If we're mid-command, put it back on the next prompt for the user.
351 # If we're mid-command, put it back on the next prompt for the user.
357 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
352 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
358
353
359 return _make_help_call(target, esc, lspace, next_input)
354 return _make_help_call(target, esc, lspace, next_input)
360
355
361
356
362 @CoroutineInputTransformer.wrap
357 @CoroutineInputTransformer.wrap
363 def cellmagic(end_on_blank_line=False):
358 def cellmagic(end_on_blank_line=False):
364 """Captures & transforms cell magics.
359 """Captures & transforms cell magics.
365
360
366 After a cell magic is started, this stores up any lines it gets until it is
361 After a cell magic is started, this stores up any lines it gets until it is
367 reset (sent None).
362 reset (sent None).
368 """
363 """
369 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
364 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
370 cellmagic_help_re = re.compile('%%\w+\?')
365 cellmagic_help_re = re.compile('%%\w+\?')
371 line = ''
366 line = ''
372 while True:
367 while True:
373 line = (yield line)
368 line = (yield line)
374 # consume leading empty lines
369 # consume leading empty lines
375 while not line:
370 while not line:
376 line = (yield line)
371 line = (yield line)
377
372
378 if not line.startswith(ESC_MAGIC2):
373 if not line.startswith(ESC_MAGIC2):
379 # This isn't a cell magic, idle waiting for reset then start over
374 # This isn't a cell magic, idle waiting for reset then start over
380 while line is not None:
375 while line is not None:
381 line = (yield line)
376 line = (yield line)
382 continue
377 continue
383
378
384 if cellmagic_help_re.match(line):
379 if cellmagic_help_re.match(line):
385 # This case will be handled by help_end
380 # This case will be handled by help_end
386 continue
381 continue
387
382
388 first = line
383 first = line
389 body = []
384 body = []
390 line = (yield None)
385 line = (yield None)
391 while (line is not None) and \
386 while (line is not None) and \
392 ((line.strip() != '') or not end_on_blank_line):
387 ((line.strip() != '') or not end_on_blank_line):
393 body.append(line)
388 body.append(line)
394 line = (yield None)
389 line = (yield None)
395
390
396 # Output
391 # Output
397 magic_name, _, first = first.partition(' ')
392 magic_name, _, first = first.partition(' ')
398 magic_name = magic_name.lstrip(ESC_MAGIC2)
393 magic_name = magic_name.lstrip(ESC_MAGIC2)
399 line = tpl % (magic_name, first, u'\n'.join(body))
394 line = tpl % (magic_name, first, u'\n'.join(body))
400
395
401
396
402 def _strip_prompts(prompt_re, initial_re=None, turnoff_re=None):
397 def _strip_prompts(prompt_re, initial_re=None, turnoff_re=None):
403 """Remove matching input prompts from a block of input.
398 """Remove matching input prompts from a block of input.
404
399
405 Parameters
400 Parameters
406 ----------
401 ----------
407 prompt_re : regular expression
402 prompt_re : regular expression
408 A regular expression matching any input prompt (including continuation)
403 A regular expression matching any input prompt (including continuation)
409 initial_re : regular expression, optional
404 initial_re : regular expression, optional
410 A regular expression matching only the initial prompt, but not continuation.
405 A regular expression matching only the initial prompt, but not continuation.
411 If no initial expression is given, prompt_re will be used everywhere.
406 If no initial expression is given, prompt_re will be used everywhere.
412 Used mainly for plain Python prompts, where the continuation prompt
407 Used mainly for plain Python prompts, where the continuation prompt
413 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
408 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
414
409
415 If initial_re and prompt_re differ,
410 If initial_re and prompt_re differ,
416 only initial_re will be tested against the first line.
411 only initial_re will be tested against the first line.
417 If any prompt is found on the first two lines,
412 If any prompt is found on the first two lines,
418 prompts will be stripped from the rest of the block.
413 prompts will be stripped from the rest of the block.
419 """
414 """
420 if initial_re is None:
415 if initial_re is None:
421 initial_re = prompt_re
416 initial_re = prompt_re
422 line = ''
417 line = ''
423 while True:
418 while True:
424 line = (yield line)
419 line = (yield line)
425
420
426 # First line of cell
421 # First line of cell
427 if line is None:
422 if line is None:
428 continue
423 continue
429 out, n1 = initial_re.subn('', line, count=1)
424 out, n1 = initial_re.subn('', line, count=1)
430 if turnoff_re and not n1:
425 if turnoff_re and not n1:
431 if turnoff_re.match(line):
426 if turnoff_re.match(line):
432 # We're in e.g. a cell magic; disable this transformer for
427 # We're in e.g. a cell magic; disable this transformer for
433 # the rest of the cell.
428 # the rest of the cell.
434 while line is not None:
429 while line is not None:
435 line = (yield line)
430 line = (yield line)
436 continue
431 continue
437
432
438 line = (yield out)
433 line = (yield out)
439
434
440 if line is None:
435 if line is None:
441 continue
436 continue
442 # check for any prompt on the second line of the cell,
437 # check for any prompt on the second line of the cell,
443 # because people often copy from just after the first prompt,
438 # because people often copy from just after the first prompt,
444 # so we might not see it in the first line.
439 # so we might not see it in the first line.
445 out, n2 = prompt_re.subn('', line, count=1)
440 out, n2 = prompt_re.subn('', line, count=1)
446 line = (yield out)
441 line = (yield out)
447
442
448 if n1 or n2:
443 if n1 or n2:
449 # Found a prompt in the first two lines - check for it in
444 # Found a prompt in the first two lines - check for it in
450 # the rest of the cell as well.
445 # the rest of the cell as well.
451 while line is not None:
446 while line is not None:
452 line = (yield prompt_re.sub('', line, count=1))
447 line = (yield prompt_re.sub('', line, count=1))
453
448
454 else:
449 else:
455 # Prompts not in input - wait for reset
450 # Prompts not in input - wait for reset
456 while line is not None:
451 while line is not None:
457 line = (yield line)
452 line = (yield line)
458
453
459 @CoroutineInputTransformer.wrap
454 @CoroutineInputTransformer.wrap
460 def classic_prompt():
455 def classic_prompt():
461 """Strip the >>>/... prompts of the Python interactive shell."""
456 """Strip the >>>/... prompts of the Python interactive shell."""
462 # FIXME: non-capturing version (?:...) usable?
457 # FIXME: non-capturing version (?:...) usable?
463 prompt_re = re.compile(r'^(>>>|\.\.\.)( |$)')
458 prompt_re = re.compile(r'^(>>>|\.\.\.)( |$)')
464 initial_re = re.compile(r'^>>>( |$)')
459 initial_re = re.compile(r'^>>>( |$)')
465 # Any %magic/!system is IPython syntax, so we needn't look for >>> prompts
460 # Any %magic/!system is IPython syntax, so we needn't look for >>> prompts
466 turnoff_re = re.compile(r'^[%!]')
461 turnoff_re = re.compile(r'^[%!]')
467 return _strip_prompts(prompt_re, initial_re, turnoff_re)
462 return _strip_prompts(prompt_re, initial_re, turnoff_re)
468
463
469 @CoroutineInputTransformer.wrap
464 @CoroutineInputTransformer.wrap
470 def ipy_prompt():
465 def ipy_prompt():
471 """Strip IPython's In [1]:/...: prompts."""
466 """Strip IPython's In [1]:/...: prompts."""
472 # FIXME: non-capturing version (?:...) usable?
467 # FIXME: non-capturing version (?:...) usable?
473 prompt_re = re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)')
468 prompt_re = re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)')
474 # Disable prompt stripping inside cell magics
469 # Disable prompt stripping inside cell magics
475 turnoff_re = re.compile(r'^%%')
470 turnoff_re = re.compile(r'^%%')
476 return _strip_prompts(prompt_re, turnoff_re=turnoff_re)
471 return _strip_prompts(prompt_re, turnoff_re=turnoff_re)
477
472
478
473
479 @CoroutineInputTransformer.wrap
474 @CoroutineInputTransformer.wrap
480 def leading_indent():
475 def leading_indent():
481 """Remove leading indentation.
476 """Remove leading indentation.
482
477
483 If the first line starts with a spaces or tabs, the same whitespace will be
478 If the first line starts with a spaces or tabs, the same whitespace will be
484 removed from each following line until it is reset.
479 removed from each following line until it is reset.
485 """
480 """
486 space_re = re.compile(r'^[ \t]+')
481 space_re = re.compile(r'^[ \t]+')
487 line = ''
482 line = ''
488 while True:
483 while True:
489 line = (yield line)
484 line = (yield line)
490
485
491 if line is None:
486 if line is None:
492 continue
487 continue
493
488
494 m = space_re.match(line)
489 m = space_re.match(line)
495 if m:
490 if m:
496 space = m.group(0)
491 space = m.group(0)
497 while line is not None:
492 while line is not None:
498 if line.startswith(space):
493 if line.startswith(space):
499 line = line[len(space):]
494 line = line[len(space):]
500 line = (yield line)
495 line = (yield line)
501 else:
496 else:
502 # No leading spaces - wait for reset
497 # No leading spaces - wait for reset
503 while line is not None:
498 while line is not None:
504 line = (yield line)
499 line = (yield line)
505
500
506
501
507 _assign_pat = \
502 _assign_pat = \
508 r'''(?P<lhs>(\s*)
503 r'''(?P<lhs>(\s*)
509 ([\w\.]+) # Initial identifier
504 ([\w\.]+) # Initial identifier
510 (\s*,\s*
505 (\s*,\s*
511 \*?[\w\.]+)* # Further identifiers for unpacking
506 \*?[\w\.]+)* # Further identifiers for unpacking
512 \s*?,? # Trailing comma
507 \s*?,? # Trailing comma
513 )
508 )
514 \s*=\s*
509 \s*=\s*
515 '''
510 '''
516
511
517 assign_system_re = re.compile(r'{}!\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
512 assign_system_re = re.compile(r'{}!\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
518 assign_system_template = '%s = get_ipython().getoutput(%r)'
513 assign_system_template = '%s = get_ipython().getoutput(%r)'
519 @StatelessInputTransformer.wrap
514 @StatelessInputTransformer.wrap
520 def assign_from_system(line):
515 def assign_from_system(line):
521 """Transform assignment from system commands (e.g. files = !ls)"""
516 """Transform assignment from system commands (e.g. files = !ls)"""
522 m = assign_system_re.match(line)
517 m = assign_system_re.match(line)
523 if m is None:
518 if m is None:
524 return line
519 return line
525
520
526 return assign_system_template % m.group('lhs', 'cmd')
521 return assign_system_template % m.group('lhs', 'cmd')
527
522
528 assign_magic_re = re.compile(r'{}%\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
523 assign_magic_re = re.compile(r'{}%\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
529 assign_magic_template = '%s = get_ipython().magic(%r)'
524 assign_magic_template = '%s = get_ipython().magic(%r)'
530 @StatelessInputTransformer.wrap
525 @StatelessInputTransformer.wrap
531 def assign_from_magic(line):
526 def assign_from_magic(line):
532 """Transform assignment from magic commands (e.g. a = %who_ls)"""
527 """Transform assignment from magic commands (e.g. a = %who_ls)"""
533 m = assign_magic_re.match(line)
528 m = assign_magic_re.match(line)
534 if m is None:
529 if m is None:
535 return line
530 return line
536
531
537 return assign_magic_template % m.group('lhs', 'cmd')
532 return assign_magic_template % m.group('lhs', 'cmd')
General Comments 0
You need to be logged in to leave comments. Login now