##// END OF EJS Templates
Reorganise InputTransformer decorator architecture.
Thomas Kluyver -
Show More
@@ -1,4 +1,5 b''
1 1 import abc
2 import functools
2 3 import re
3 4 from StringIO import StringIO
4 5 import tokenize
@@ -54,46 +55,60 b' class InputTransformer(object):'
54 55
55 56 # Set this to True to allow the transformer to act on lines inside strings.
56 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
74
75 def coroutine_input_transformer(coro):
76 class CoroutineInputTransformer(InputTransformer):
77 """Wrapper for input transformers based on coroutines."""
78 def __init__(self):
79 # Prime it
80 self.coro = coro()
81 next(self.coro)
82
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 """
88 return self.coro.send(line)
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
89 70
90 def reset(self):
91 """Return, transformed any lines that the transformer has
92 accumulated, and reset its internal state.
93 """
94 return self.coro.send(None)
71 return transformer_factory
72
73 class StatelessInputTransformer(InputTransformer):
74 """Wrapper for a stateless input transformer implemented as a function."""
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 114 # Utilities
@@ -110,7 +125,7 b' def _make_help_call(target, esc, lspace, next_input=None):'
110 125 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
111 126 (lspace, next_input, arg)
112 127
113 @coroutine_input_transformer
128 @CoroutineInputTransformer.wrap
114 129 def escaped_transformer():
115 130 """Translate lines beginning with one of IPython's escape characters.
116 131
@@ -220,7 +235,7 b' def has_comment(src):'
220 235 return(tokenize.COMMENT in toktypes)
221 236
222 237
223 @stateless_input_transformer
238 @StatelessInputTransformer.wrap
224 239 def help_end(line):
225 240 """Translate lines with ?/?? at the end"""
226 241 m = _help_end_re.search(line)
@@ -236,7 +251,7 b' def help_end(line):'
236 251 return _make_help_call(target, esc, lspace, next_input)
237 252
238 253
239 @coroutine_input_transformer
254 @CoroutineInputTransformer.wrap
240 255 def cellmagic():
241 256 """Captures & transforms cell magics.
242 257
@@ -289,7 +304,7 b' def _strip_prompts(prompt1_re, prompt2_re):'
289 304 while line is not None:
290 305 line = (yield line)
291 306
292 @coroutine_input_transformer
307 @CoroutineInputTransformer.wrap
293 308 def classic_prompt():
294 309 """Strip the >>>/... prompts of the Python interactive shell."""
295 310 prompt1_re = re.compile(r'^(>>> )')
@@ -298,7 +313,7 b' def classic_prompt():'
298 313
299 314 classic_prompt.look_in_string = True
300 315
301 @coroutine_input_transformer
316 @CoroutineInputTransformer.wrap
302 317 def ipy_prompt():
303 318 """Strip IPython's In [1]:/...: prompts."""
304 319 prompt1_re = re.compile(r'^In \[\d+\]: ')
@@ -308,7 +323,7 b' def ipy_prompt():'
308 323 ipy_prompt.look_in_string = True
309 324
310 325
311 @coroutine_input_transformer
326 @CoroutineInputTransformer.wrap
312 327 def leading_indent():
313 328 """Remove leading indentation.
314 329
@@ -365,7 +380,7 b' def _special_assignment(assignment_re, template):'
365 380 whole = assignment_re.match(' '.join(parts))
366 381 line = template % (whole.group('lhs'), whole.group('cmd'))
367 382
368 @coroutine_input_transformer
383 @CoroutineInputTransformer.wrap
369 384 def assign_from_system():
370 385 """Transform assignment from system commands (e.g. files = !ls)"""
371 386 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
@@ -373,7 +388,7 b' def assign_from_system():'
373 388 template = '%s = get_ipython().getoutput(%r)'
374 389 return _special_assignment(assignment_re, template)
375 390
376 @coroutine_input_transformer
391 @CoroutineInputTransformer.wrap
377 392 def assign_from_magic():
378 393 """Transform assignment from magic commands (e.g. a = %who_ls)"""
379 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