##// END OF EJS Templates
Example of adding input transformers....
Thomas Kluyver -
Show More
@@ -1,129 +1,132 b''
1
1
2 ===========================
2 ===========================
3 Custom input transformation
3 Custom input transformation
4 ===========================
4 ===========================
5
5
6 IPython extends Python syntax to allow things like magic commands, and help with
6 IPython extends Python syntax to allow things like magic commands, and help with
7 the ``?`` syntax. There are several ways to customise how the user's input is
7 the ``?`` syntax. There are several ways to customise how the user's input is
8 processed into Python code to be executed.
8 processed into Python code to be executed.
9
9
10 These hooks are mainly for other projects using IPython as the core of their
10 These hooks are mainly for other projects using IPython as the core of their
11 interactive interface. Using them carelessly can easily break IPython!
11 interactive interface. Using them carelessly can easily break IPython!
12
12
13 String based transformations
13 String based transformations
14 ============================
14 ============================
15
15
16 .. currentmodule:: IPython.core.inputtransforms
16 .. currentmodule:: IPython.core.inputtransforms
17
17
18 When the user enters a line of code, it is first processed as a string. By the
18 When the user enters a line of code, it is first processed as a string. By the
19 end of this stage, it must be valid Python syntax.
19 end of this stage, it must be valid Python syntax.
20
20
21 These transformers all subclass :class:`IPython.core.inputtransformer.InputTransformer`,
21 These transformers all subclass :class:`IPython.core.inputtransformer.InputTransformer`,
22 and are used by :class:`IPython.core.inputsplitter.IPythonInputSplitter`.
22 and are used by :class:`IPython.core.inputsplitter.IPythonInputSplitter`.
23
23
24 These transformers act in three groups, stored separately as lists of instances
24 These transformers act in three groups, stored separately as lists of instances
25 in attributes of :class:`~IPython.core.inputsplitter.IPythonInputSplitter`:
25 in attributes of :class:`~IPython.core.inputsplitter.IPythonInputSplitter`:
26
26
27 * ``physical_line_transforms`` act on the lines as the user enters them. For
27 * ``physical_line_transforms`` act on the lines as the user enters them. For
28 example, these strip Python prompts from examples pasted in.
28 example, these strip Python prompts from examples pasted in.
29 * ``logical_line_transforms`` act on lines as connected by explicit line
29 * ``logical_line_transforms`` act on lines as connected by explicit line
30 continuations, i.e. ``\`` at the end of physical lines. They are skipped
30 continuations, i.e. ``\`` at the end of physical lines. They are skipped
31 inside multiline Python statements. This is the point where IPython recognises
31 inside multiline Python statements. This is the point where IPython recognises
32 ``%magic`` commands, for instance.
32 ``%magic`` commands, for instance.
33 * ``python_line_transforms`` act on blocks containing complete Python statements.
33 * ``python_line_transforms`` act on blocks containing complete Python statements.
34 Multi-line strings, lists and function calls are reassembled before being
34 Multi-line strings, lists and function calls are reassembled before being
35 passed to these, but note that function and class *definitions* are still a
35 passed to these, but note that function and class *definitions* are still a
36 series of separate statements. IPython does not use any of these by default.
36 series of separate statements. IPython does not use any of these by default.
37
37
38 An InteractiveShell instance actually has two
38 An InteractiveShell instance actually has two
39 :class:`~IPython.core.inputsplitter.IPythonInputSplitter` instances, as the
39 :class:`~IPython.core.inputsplitter.IPythonInputSplitter` instances, as the
40 attributes :attr:`~IPython.core.interactiveshell.InteractiveShell.input_splitter`,
40 attributes :attr:`~IPython.core.interactiveshell.InteractiveShell.input_splitter`,
41 to tell when a block of input is complete, and
41 to tell when a block of input is complete, and
42 :attr:`~IPython.core.interactiveshell.InteractiveShell.input_transformer_manager`,
42 :attr:`~IPython.core.interactiveshell.InteractiveShell.input_transformer_manager`,
43 to transform complete cells. If you add a transformer, you should make sure that
43 to transform complete cells. If you add a transformer, you should make sure that
44 it gets added to both.
44 it gets added to both, e.g.::
45
46 ip.input_splitter.logical_line_transforms.append(my_transformer())
47 ip.input_transformer_manager.logical_line_transforms.append(my_transformer())
45
48
46 Stateless transformations
49 Stateless transformations
47 -------------------------
50 -------------------------
48
51
49 The simplest kind of transformations work one line at a time. Write a function
52 The simplest kind of transformations work one line at a time. Write a function
50 which takes a line and returns a line, and decorate it with
53 which takes a line and returns a line, and decorate it with
51 :meth:`StatelessInputTransformer.wrap`::
54 :meth:`StatelessInputTransformer.wrap`::
52
55
53 @StatelessInputTransformer.wrap
56 @StatelessInputTransformer.wrap
54 def my_special_commands(line):
57 def my_special_commands(line):
55 if line.startswith("Β¬"):
58 if line.startswith("Β¬"):
56 return "specialcommand(" + repr(line) + ")"
59 return "specialcommand(" + repr(line) + ")"
57 return line
60 return line
58
61
59 The decorator returns a factory function which will produce instances of
62 The decorator returns a factory function which will produce instances of
60 :class:`~IPython.core.inputtransformer.StatelessInputTransformer` using your
63 :class:`~IPython.core.inputtransformer.StatelessInputTransformer` using your
61 function.
64 function.
62
65
63 Coroutine transformers
66 Coroutine transformers
64 ----------------------
67 ----------------------
65
68
66 More advanced transformers can be written as coroutines. The coroutine will be
69 More advanced transformers can be written as coroutines. The coroutine will be
67 sent each line in turn, followed by ``None`` to reset it. It can yield lines, or
70 sent each line in turn, followed by ``None`` to reset it. It can yield lines, or
68 ``None`` if it is accumulating text to yield at a later point. When reset, it
71 ``None`` if it is accumulating text to yield at a later point. When reset, it
69 should give up any code it has accumulated.
72 should give up any code it has accumulated.
70
73
71 This code in IPython strips a constant amount of leading indentation from each
74 This code in IPython strips a constant amount of leading indentation from each
72 line in a cell::
75 line in a cell::
73
76
74 @CoroutineInputTransformer.wrap
77 @CoroutineInputTransformer.wrap
75 def leading_indent():
78 def leading_indent():
76 """Remove leading indentation.
79 """Remove leading indentation.
77
80
78 If the first line starts with a spaces or tabs, the same whitespace will be
81 If the first line starts with a spaces or tabs, the same whitespace will be
79 removed from each following line until it is reset.
82 removed from each following line until it is reset.
80 """
83 """
81 space_re = re.compile(r'^[ \t]+')
84 space_re = re.compile(r'^[ \t]+')
82 line = ''
85 line = ''
83 while True:
86 while True:
84 line = (yield line)
87 line = (yield line)
85
88
86 if line is None:
89 if line is None:
87 continue
90 continue
88
91
89 m = space_re.match(line)
92 m = space_re.match(line)
90 if m:
93 if m:
91 space = m.group(0)
94 space = m.group(0)
92 while line is not None:
95 while line is not None:
93 if line.startswith(space):
96 if line.startswith(space):
94 line = line[len(space):]
97 line = line[len(space):]
95 line = (yield line)
98 line = (yield line)
96 else:
99 else:
97 # No leading spaces - wait for reset
100 # No leading spaces - wait for reset
98 while line is not None:
101 while line is not None:
99 line = (yield line)
102 line = (yield line)
100
103
101 leading_indent.look_in_string = True
104 leading_indent.look_in_string = True
102
105
103 Token-based transformers
106 Token-based transformers
104 ------------------------
107 ------------------------
105
108
106 There is an experimental framework that takes care of tokenizing and
109 There is an experimental framework that takes care of tokenizing and
107 untokenizing lines of code. Define a function that accepts a list of tokens, and
110 untokenizing lines of code. Define a function that accepts a list of tokens, and
108 returns an iterable of output tokens, and decorate it with
111 returns an iterable of output tokens, and decorate it with
109 :meth:`TokenInputTransformer.wrap`. These should only be used in
112 :meth:`TokenInputTransformer.wrap`. These should only be used in
110 ``python_line_transforms``.
113 ``python_line_transforms``.
111
114
112 AST transformations
115 AST transformations
113 ===================
116 ===================
114
117
115 After the code has been parsed as Python syntax, you can use Python's powerful
118 After the code has been parsed as Python syntax, you can use Python's powerful
116 *Abstract Syntax Tree* tools to modify it. Subclass :class:`ast.NodeTransformer`,
119 *Abstract Syntax Tree* tools to modify it. Subclass :class:`ast.NodeTransformer`,
117 and add an instance to ``shell.ast_transformers``.
120 and add an instance to ``shell.ast_transformers``.
118
121
119 This example wraps integer literals in an ``Integer`` class, which is useful for
122 This example wraps integer literals in an ``Integer`` class, which is useful for
120 mathematical frameworks that want to handle e.g. ``1/3`` as a precise fraction::
123 mathematical frameworks that want to handle e.g. ``1/3`` as a precise fraction::
121
124
122
125
123 class IntegerWrapper(ast.NodeTransformer):
126 class IntegerWrapper(ast.NodeTransformer):
124 """Wraps all integers in a call to Integer()"""
127 """Wraps all integers in a call to Integer()"""
125 def visit_Num(self, node):
128 def visit_Num(self, node):
126 if isinstance(node.n, int):
129 if isinstance(node.n, int):
127 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
130 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
128 args=[node], keywords=[])
131 args=[node], keywords=[])
129 return node
132 return node
General Comments 0
You need to be logged in to leave comments. Login now