##// END OF EJS Templates
Reorganise InputTransformer decorator architecture.
Thomas Kluyver -
Show More
@@ -1,4 +1,5 b''
1 import abc
1 import abc
2 import functools
2 import re
3 import re
3 from StringIO import StringIO
4 from StringIO import StringIO
4 import tokenize
5 import tokenize
@@ -55,12 +56,28 b' class InputTransformer(object):'
55 # Set this to True to allow the transformer to act on lines inside strings.
56 # Set this to True to allow the transformer to act on lines inside strings.
56 look_in_string = False
57 look_in_string = False
57
58
58 def stateless_input_transformer(func):
59 @classmethod
60 def wrap(cls, func):
61 """Can be used by subclasses as a decorator, to return a factory that
62 will allow instantiation with the decorated object.
63 """
64 @functools.wraps(func)
65 def transformer_factory():
66 transformer = cls(func)
67 if getattr(transformer_factory, 'look_in_string', False):
68 transformer.look_in_string = True
69 return transformer
70
71 return transformer_factory
72
59 class StatelessInputTransformer(InputTransformer):
73 class StatelessInputTransformer(InputTransformer):
60 """Decorator for a stateless input transformer implemented as a function."""
74 """Wrapper for a stateless input transformer implemented as a function."""
61 def __init__(self):
75 def __init__(self, func):
62 self.func = func
76 self.func = func
63
77
78 def __repr__(self):
79 return "StatelessInputTransformer(func={!r})".format(self.func)
80
64 def push(self, line):
81 def push(self, line):
65 """Send a line of input to the transformer, returning the
82 """Send a line of input to the transformer, returning the
66 transformed input."""
83 transformed input."""
@@ -70,16 +87,16 b' def stateless_input_transformer(func):'
70 """No-op - exists for compatibility."""
87 """No-op - exists for compatibility."""
71 pass
88 pass
72
89
73 return StatelessInputTransformer
74
75 def coroutine_input_transformer(coro):
76 class CoroutineInputTransformer(InputTransformer):
90 class CoroutineInputTransformer(InputTransformer):
77 """Wrapper for input transformers based on coroutines."""
91 """Wrapper for an input transformer implemented as a coroutine."""
78 def __init__(self):
92 def __init__(self, coro):
79 # Prime it
93 # Prime it
80 self.coro = coro()
94 self.coro = coro()
81 next(self.coro)
95 next(self.coro)
82
96
97 def __repr__(self):
98 return "CoroutineInputTransformer(coro={!r})".format(self.coro)
99
83 def push(self, line):
100 def push(self, line):
84 """Send a line of input to the transformer, returning the
101 """Send a line of input to the transformer, returning the
85 transformed input or None if the transformer is waiting for more
102 transformed input or None if the transformer is waiting for more
@@ -93,8 +110,6 b' def coroutine_input_transformer(coro):'
93 """
110 """
94 return self.coro.send(None)
111 return self.coro.send(None)
95
112
96 return CoroutineInputTransformer
97
98
113
99 # Utilities
114 # Utilities
100 def _make_help_call(target, esc, lspace, next_input=None):
115 def _make_help_call(target, esc, lspace, next_input=None):
@@ -110,7 +125,7 b' def _make_help_call(target, esc, lspace, next_input=None):'
110 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
125 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
111 (lspace, next_input, arg)
126 (lspace, next_input, arg)
112
127
113 @coroutine_input_transformer
128 @CoroutineInputTransformer.wrap
114 def escaped_transformer():
129 def escaped_transformer():
115 """Translate lines beginning with one of IPython's escape characters.
130 """Translate lines beginning with one of IPython's escape characters.
116
131
@@ -220,7 +235,7 b' def has_comment(src):'
220 return(tokenize.COMMENT in toktypes)
235 return(tokenize.COMMENT in toktypes)
221
236
222
237
223 @stateless_input_transformer
238 @StatelessInputTransformer.wrap
224 def help_end(line):
239 def help_end(line):
225 """Translate lines with ?/?? at the end"""
240 """Translate lines with ?/?? at the end"""
226 m = _help_end_re.search(line)
241 m = _help_end_re.search(line)
@@ -236,7 +251,7 b' def help_end(line):'
236 return _make_help_call(target, esc, lspace, next_input)
251 return _make_help_call(target, esc, lspace, next_input)
237
252
238
253
239 @coroutine_input_transformer
254 @CoroutineInputTransformer.wrap
240 def cellmagic():
255 def cellmagic():
241 """Captures & transforms cell magics.
256 """Captures & transforms cell magics.
242
257
@@ -289,7 +304,7 b' def _strip_prompts(prompt1_re, prompt2_re):'
289 while line is not None:
304 while line is not None:
290 line = (yield line)
305 line = (yield line)
291
306
292 @coroutine_input_transformer
307 @CoroutineInputTransformer.wrap
293 def classic_prompt():
308 def classic_prompt():
294 """Strip the >>>/... prompts of the Python interactive shell."""
309 """Strip the >>>/... prompts of the Python interactive shell."""
295 prompt1_re = re.compile(r'^(>>> )')
310 prompt1_re = re.compile(r'^(>>> )')
@@ -298,7 +313,7 b' def classic_prompt():'
298
313
299 classic_prompt.look_in_string = True
314 classic_prompt.look_in_string = True
300
315
301 @coroutine_input_transformer
316 @CoroutineInputTransformer.wrap
302 def ipy_prompt():
317 def ipy_prompt():
303 """Strip IPython's In [1]:/...: prompts."""
318 """Strip IPython's In [1]:/...: prompts."""
304 prompt1_re = re.compile(r'^In \[\d+\]: ')
319 prompt1_re = re.compile(r'^In \[\d+\]: ')
@@ -308,7 +323,7 b' def ipy_prompt():'
308 ipy_prompt.look_in_string = True
323 ipy_prompt.look_in_string = True
309
324
310
325
311 @coroutine_input_transformer
326 @CoroutineInputTransformer.wrap
312 def leading_indent():
327 def leading_indent():
313 """Remove leading indentation.
328 """Remove leading indentation.
314
329
@@ -365,7 +380,7 b' def _special_assignment(assignment_re, template):'
365 whole = assignment_re.match(' '.join(parts))
380 whole = assignment_re.match(' '.join(parts))
366 line = template % (whole.group('lhs'), whole.group('cmd'))
381 line = template % (whole.group('lhs'), whole.group('cmd'))
367
382
368 @coroutine_input_transformer
383 @CoroutineInputTransformer.wrap
369 def assign_from_system():
384 def assign_from_system():
370 """Transform assignment from system commands (e.g. files = !ls)"""
385 """Transform assignment from system commands (e.g. files = !ls)"""
371 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
386 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
@@ -373,7 +388,7 b' def assign_from_system():'
373 template = '%s = get_ipython().getoutput(%r)'
388 template = '%s = get_ipython().getoutput(%r)'
374 return _special_assignment(assignment_re, template)
389 return _special_assignment(assignment_re, template)
375
390
376 @coroutine_input_transformer
391 @CoroutineInputTransformer.wrap
377 def assign_from_magic():
392 def assign_from_magic():
378 """Transform assignment from magic commands (e.g. a = %who_ls)"""
393 """Transform assignment from magic commands (e.g. a = %who_ls)"""
379 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
394 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
General Comments 0
You need to be logged in to leave comments. Login now