##// 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
@@ -54,46 +55,60 b' class InputTransformer(object):'
54
55
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 def stateless_input_transformer(func):
59 class StatelessInputTransformer(InputTransformer):
60 """Decorator for a stateless input transformer implemented as a function."""
61 def __init__(self):
62 self.func = func
63
64 def push(self, line):
65 """Send a line of input to the transformer, returning the
66 transformed input."""
67 return self.func(line)
68
69 def reset(self):
70 """No-op - exists for compatibility."""
71 pass
72
58
73 return StatelessInputTransformer
59 @classmethod
74
60 def wrap(cls, func):
75 def coroutine_input_transformer(coro):
61 """Can be used by subclasses as a decorator, to return a factory that
76 class CoroutineInputTransformer(InputTransformer):
62 will allow instantiation with the decorated object.
77 """Wrapper for input transformers based on coroutines."""
63 """
78 def __init__(self):
64 @functools.wraps(func)
79 # Prime it
65 def transformer_factory():
80 self.coro = coro()
66 transformer = cls(func)
81 next(self.coro)
67 if getattr(transformer_factory, 'look_in_string', False):
82
68 transformer.look_in_string = True
83 def push(self, line):
69 return transformer
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 """
88 return self.coro.send(line)
89
70
90 def reset(self):
71 return transformer_factory
91 """Return, transformed any lines that the transformer has
72
92 accumulated, and reset its internal state.
73 class StatelessInputTransformer(InputTransformer):
93 """
74 """Wrapper for a stateless input transformer implemented as a function."""
94 return self.coro.send(None)
75 def __init__(self, func):
76 self.func = func
77
78 def __repr__(self):
79 return "StatelessInputTransformer(func={!r})".format(self.func)
95
80
96 return CoroutineInputTransformer
81 def push(self, line):
82 """Send a line of input to the transformer, returning the
83 transformed input."""
84 return self.func(line)
85
86 def reset(self):
87 """No-op - exists for compatibility."""
88 pass
89
90 class CoroutineInputTransformer(InputTransformer):
91 """Wrapper for an input transformer implemented as a coroutine."""
92 def __init__(self, coro):
93 # Prime it
94 self.coro = coro()
95 next(self.coro)
96
97 def __repr__(self):
98 return "CoroutineInputTransformer(coro={!r})".format(self.coro)
99
100 def push(self, line):
101 """Send a line of input to the transformer, returning the
102 transformed input or None if the transformer is waiting for more
103 input.
104 """
105 return self.coro.send(line)
106
107 def reset(self):
108 """Return, transformed any lines that the transformer has
109 accumulated, and reset its internal state.
110 """
111 return self.coro.send(None)
97
112
98
113
99 # Utilities
114 # Utilities
@@ -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