##// 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
@@ -55,12 +56,28 b' class InputTransformer(object):'
55 56 # Set this to True to allow the transformer to act on lines inside strings.
56 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 73 class StatelessInputTransformer(InputTransformer):
60 """Decorator for a stateless input transformer implemented as a function."""
61 def __init__(self):
74 """Wrapper for a stateless input transformer implemented as a function."""
75 def __init__(self, func):
62 76 self.func = func
63 77
78 def __repr__(self):
79 return "StatelessInputTransformer(func={!r})".format(self.func)
80
64 81 def push(self, line):
65 82 """Send a line of input to the transformer, returning the
66 83 transformed input."""
@@ -70,16 +87,16 b' def stateless_input_transformer(func):'
70 87 """No-op - exists for compatibility."""
71 88 pass
72 89
73 return StatelessInputTransformer
74
75 def coroutine_input_transformer(coro):
76 90 class CoroutineInputTransformer(InputTransformer):
77 """Wrapper for input transformers based on coroutines."""
78 def __init__(self):
91 """Wrapper for an input transformer implemented as a coroutine."""
92 def __init__(self, coro):
79 93 # Prime it
80 94 self.coro = coro()
81 95 next(self.coro)
82 96
97 def __repr__(self):
98 return "CoroutineInputTransformer(coro={!r})".format(self.coro)
99
83 100 def push(self, line):
84 101 """Send a line of input to the transformer, returning the
85 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 111 return self.coro.send(None)
95 112
96 return CoroutineInputTransformer
97
98 113
99 114 # Utilities
100 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 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