Show More
@@ -0,0 +1,103 b'' | |||||
|
1 | ||||
|
2 | =========================== | |||
|
3 | Custom input transformation | |||
|
4 | =========================== | |||
|
5 | ||||
|
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 | |||
|
8 | processed into Python code to be executed. | |||
|
9 | ||||
|
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! | |||
|
12 | ||||
|
13 | String based transformations | |||
|
14 | ============================ | |||
|
15 | ||||
|
16 | When the user enters a line of code, it is first processed as a string. By the | |||
|
17 | end of this stage, it must be valid Python syntax. | |||
|
18 | ||||
|
19 | These transformers all subclass :class:`IPython.core.inputtransformer.InputTransformer`, | |||
|
20 | and are used by :class:`IPython.core.inputsplitter.IPythonInputSplitter`, | |||
|
21 | the ``transform`` attribute of which is a list of instances. | |||
|
22 | ||||
|
23 | By default, these transformers are skipped when :class:`~IPython.core.inputsplitter.IPythonInputSplitter` | |||
|
24 | detects that the line starts inside a multi-line string. Some transformers, such | |||
|
25 | as those that remove the prompt markers from pasted examples, need to look | |||
|
26 | inside multiline strings as well - these set the attribute ``look_in_string`` to | |||
|
27 | ``True``. | |||
|
28 | ||||
|
29 | Stateless transformations | |||
|
30 | ------------------------- | |||
|
31 | ||||
|
32 | The simplest kind of transformations work one line at a time. Write a function | |||
|
33 | which takes a line and returns a line, and decorate it with | |||
|
34 | :meth:`~IPython.core.inputtransformer.StatelessInputTransformer.wrap`:: | |||
|
35 | ||||
|
36 | @StatelessInputTransformer.wrap | |||
|
37 | def my_special_commands(line): | |||
|
38 | if line.startswith("¬"): | |||
|
39 | return "specialcommand(" + repr(line) + ")" | |||
|
40 | return line | |||
|
41 | ||||
|
42 | The decorator returns a factory function which will produce instances of | |||
|
43 | :class:`~IPython.core.inputtransformer.StatelessInputTransformer` using your | |||
|
44 | function. | |||
|
45 | ||||
|
46 | Coroutine transformers | |||
|
47 | ---------------------- | |||
|
48 | ||||
|
49 | More advanced transformers can be written as coroutines. The coroutine will be | |||
|
50 | sent each line in turn, followed by ``None`` to reset it. It can yield lines, or | |||
|
51 | ``None`` if it is accumulating text to yield at a later point. When reset, it | |||
|
52 | should give up any code it has accumulated. | |||
|
53 | ||||
|
54 | This code in IPython strips a constant amount of leading indentation from each | |||
|
55 | line in a cell:: | |||
|
56 | ||||
|
57 | @CoroutineInputTransformer.wrap | |||
|
58 | def leading_indent(): | |||
|
59 | """Remove leading indentation. | |||
|
60 | ||||
|
61 | If the first line starts with a spaces or tabs, the same whitespace will be | |||
|
62 | removed from each following line until it is reset. | |||
|
63 | """ | |||
|
64 | space_re = re.compile(r'^[ \t]+') | |||
|
65 | line = '' | |||
|
66 | while True: | |||
|
67 | line = (yield line) | |||
|
68 | ||||
|
69 | if line is None: | |||
|
70 | continue | |||
|
71 | ||||
|
72 | m = space_re.match(line) | |||
|
73 | if m: | |||
|
74 | space = m.group(0) | |||
|
75 | while line is not None: | |||
|
76 | if line.startswith(space): | |||
|
77 | line = line[len(space):] | |||
|
78 | line = (yield line) | |||
|
79 | else: | |||
|
80 | # No leading spaces - wait for reset | |||
|
81 | while line is not None: | |||
|
82 | line = (yield line) | |||
|
83 | ||||
|
84 | leading_indent.look_in_string = True | |||
|
85 | ||||
|
86 | AST transformations | |||
|
87 | =================== | |||
|
88 | ||||
|
89 | After the code has been parsed as Python syntax, you can use Python's powerful | |||
|
90 | *Abstract Syntax Tree* tools to modify it. Subclass :class:`ast.NodeTransformer`, | |||
|
91 | and add an instance to ``shell.ast_transformers``. | |||
|
92 | ||||
|
93 | This example wraps integer literals in an ``Integer`` class, which is useful for | |||
|
94 | mathematical frameworks that want to handle e.g. ``1/3`` as a precise fraction:: | |||
|
95 | ||||
|
96 | ||||
|
97 | class IntegerWrapper(ast.NodeTransformer): | |||
|
98 | """Wraps all integers in a call to Integer()""" | |||
|
99 | def visit_Num(self, node): | |||
|
100 | if isinstance(node.n, int): | |||
|
101 | return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()), | |||
|
102 | args=[node], keywords=[]) | |||
|
103 | return node |
General Comments 0
You need to be logged in to leave comments.
Login now