]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | """Fixer for __metaclass__ = X -> (metaclass=X) methods.\r |
2 | \r | |
3 | The various forms of classef (inherits nothing, inherits once, inherints\r | |
4 | many) don't parse the same in the CST so we look at ALL classes for\r | |
5 | a __metaclass__ and if we find one normalize the inherits to all be\r | |
6 | an arglist.\r | |
7 | \r | |
8 | For one-liner classes ('class X: pass') there is no indent/dedent so\r | |
9 | we normalize those into having a suite.\r | |
10 | \r | |
11 | Moving the __metaclass__ into the classdef can also cause the class\r | |
12 | body to be empty so there is some special casing for that as well.\r | |
13 | \r | |
14 | This fixer also tries very hard to keep original indenting and spacing\r | |
15 | in all those corner cases.\r | |
16 | \r | |
17 | """\r | |
18 | # Author: Jack Diederich\r | |
19 | \r | |
20 | # Local imports\r | |
21 | from .. import fixer_base\r | |
22 | from ..pygram import token\r | |
23 | from ..fixer_util import Name, syms, Node, Leaf\r | |
24 | \r | |
25 | \r | |
26 | def has_metaclass(parent):\r | |
27 | """ we have to check the cls_node without changing it.\r | |
28 | There are two possiblities:\r | |
29 | 1) clsdef => suite => simple_stmt => expr_stmt => Leaf('__meta')\r | |
30 | 2) clsdef => simple_stmt => expr_stmt => Leaf('__meta')\r | |
31 | """\r | |
32 | for node in parent.children:\r | |
33 | if node.type == syms.suite:\r | |
34 | return has_metaclass(node)\r | |
35 | elif node.type == syms.simple_stmt and node.children:\r | |
36 | expr_node = node.children[0]\r | |
37 | if expr_node.type == syms.expr_stmt and expr_node.children:\r | |
38 | left_side = expr_node.children[0]\r | |
39 | if isinstance(left_side, Leaf) and \\r | |
40 | left_side.value == '__metaclass__':\r | |
41 | return True\r | |
42 | return False\r | |
43 | \r | |
44 | \r | |
45 | def fixup_parse_tree(cls_node):\r | |
46 | """ one-line classes don't get a suite in the parse tree so we add\r | |
47 | one to normalize the tree\r | |
48 | """\r | |
49 | for node in cls_node.children:\r | |
50 | if node.type == syms.suite:\r | |
51 | # already in the preferred format, do nothing\r | |
52 | return\r | |
53 | \r | |
54 | # !%@#! oneliners have no suite node, we have to fake one up\r | |
55 | for i, node in enumerate(cls_node.children):\r | |
56 | if node.type == token.COLON:\r | |
57 | break\r | |
58 | else:\r | |
59 | raise ValueError("No class suite and no ':'!")\r | |
60 | \r | |
61 | # move everything into a suite node\r | |
62 | suite = Node(syms.suite, [])\r | |
63 | while cls_node.children[i+1:]:\r | |
64 | move_node = cls_node.children[i+1]\r | |
65 | suite.append_child(move_node.clone())\r | |
66 | move_node.remove()\r | |
67 | cls_node.append_child(suite)\r | |
68 | node = suite\r | |
69 | \r | |
70 | \r | |
71 | def fixup_simple_stmt(parent, i, stmt_node):\r | |
72 | """ if there is a semi-colon all the parts count as part of the same\r | |
73 | simple_stmt. We just want the __metaclass__ part so we move\r | |
74 | everything efter the semi-colon into its own simple_stmt node\r | |
75 | """\r | |
76 | for semi_ind, node in enumerate(stmt_node.children):\r | |
77 | if node.type == token.SEMI: # *sigh*\r | |
78 | break\r | |
79 | else:\r | |
80 | return\r | |
81 | \r | |
82 | node.remove() # kill the semicolon\r | |
83 | new_expr = Node(syms.expr_stmt, [])\r | |
84 | new_stmt = Node(syms.simple_stmt, [new_expr])\r | |
85 | while stmt_node.children[semi_ind:]:\r | |
86 | move_node = stmt_node.children[semi_ind]\r | |
87 | new_expr.append_child(move_node.clone())\r | |
88 | move_node.remove()\r | |
89 | parent.insert_child(i, new_stmt)\r | |
90 | new_leaf1 = new_stmt.children[0].children[0]\r | |
91 | old_leaf1 = stmt_node.children[0].children[0]\r | |
92 | new_leaf1.prefix = old_leaf1.prefix\r | |
93 | \r | |
94 | \r | |
95 | def remove_trailing_newline(node):\r | |
96 | if node.children and node.children[-1].type == token.NEWLINE:\r | |
97 | node.children[-1].remove()\r | |
98 | \r | |
99 | \r | |
100 | def find_metas(cls_node):\r | |
101 | # find the suite node (Mmm, sweet nodes)\r | |
102 | for node in cls_node.children:\r | |
103 | if node.type == syms.suite:\r | |
104 | break\r | |
105 | else:\r | |
106 | raise ValueError("No class suite!")\r | |
107 | \r | |
108 | # look for simple_stmt[ expr_stmt[ Leaf('__metaclass__') ] ]\r | |
109 | for i, simple_node in list(enumerate(node.children)):\r | |
110 | if simple_node.type == syms.simple_stmt and simple_node.children:\r | |
111 | expr_node = simple_node.children[0]\r | |
112 | if expr_node.type == syms.expr_stmt and expr_node.children:\r | |
113 | # Check if the expr_node is a simple assignment.\r | |
114 | left_node = expr_node.children[0]\r | |
115 | if isinstance(left_node, Leaf) and \\r | |
116 | left_node.value == u'__metaclass__':\r | |
117 | # We found a assignment to __metaclass__.\r | |
118 | fixup_simple_stmt(node, i, simple_node)\r | |
119 | remove_trailing_newline(simple_node)\r | |
120 | yield (node, i, simple_node)\r | |
121 | \r | |
122 | \r | |
123 | def fixup_indent(suite):\r | |
124 | """ If an INDENT is followed by a thing with a prefix then nuke the prefix\r | |
125 | Otherwise we get in trouble when removing __metaclass__ at suite start\r | |
126 | """\r | |
127 | kids = suite.children[::-1]\r | |
128 | # find the first indent\r | |
129 | while kids:\r | |
130 | node = kids.pop()\r | |
131 | if node.type == token.INDENT:\r | |
132 | break\r | |
133 | \r | |
134 | # find the first Leaf\r | |
135 | while kids:\r | |
136 | node = kids.pop()\r | |
137 | if isinstance(node, Leaf) and node.type != token.DEDENT:\r | |
138 | if node.prefix:\r | |
139 | node.prefix = u''\r | |
140 | return\r | |
141 | else:\r | |
142 | kids.extend(node.children[::-1])\r | |
143 | \r | |
144 | \r | |
145 | class FixMetaclass(fixer_base.BaseFix):\r | |
146 | BM_compatible = True\r | |
147 | \r | |
148 | PATTERN = """\r | |
149 | classdef<any*>\r | |
150 | """\r | |
151 | \r | |
152 | def transform(self, node, results):\r | |
153 | if not has_metaclass(node):\r | |
154 | return\r | |
155 | \r | |
156 | fixup_parse_tree(node)\r | |
157 | \r | |
158 | # find metaclasses, keep the last one\r | |
159 | last_metaclass = None\r | |
160 | for suite, i, stmt in find_metas(node):\r | |
161 | last_metaclass = stmt\r | |
162 | stmt.remove()\r | |
163 | \r | |
164 | text_type = node.children[0].type # always Leaf(nnn, 'class')\r | |
165 | \r | |
166 | # figure out what kind of classdef we have\r | |
167 | if len(node.children) == 7:\r | |
168 | # Node(classdef, ['class', 'name', '(', arglist, ')', ':', suite])\r | |
169 | # 0 1 2 3 4 5 6\r | |
170 | if node.children[3].type == syms.arglist:\r | |
171 | arglist = node.children[3]\r | |
172 | # Node(classdef, ['class', 'name', '(', 'Parent', ')', ':', suite])\r | |
173 | else:\r | |
174 | parent = node.children[3].clone()\r | |
175 | arglist = Node(syms.arglist, [parent])\r | |
176 | node.set_child(3, arglist)\r | |
177 | elif len(node.children) == 6:\r | |
178 | # Node(classdef, ['class', 'name', '(', ')', ':', suite])\r | |
179 | # 0 1 2 3 4 5\r | |
180 | arglist = Node(syms.arglist, [])\r | |
181 | node.insert_child(3, arglist)\r | |
182 | elif len(node.children) == 4:\r | |
183 | # Node(classdef, ['class', 'name', ':', suite])\r | |
184 | # 0 1 2 3\r | |
185 | arglist = Node(syms.arglist, [])\r | |
186 | node.insert_child(2, Leaf(token.RPAR, u')'))\r | |
187 | node.insert_child(2, arglist)\r | |
188 | node.insert_child(2, Leaf(token.LPAR, u'('))\r | |
189 | else:\r | |
190 | raise ValueError("Unexpected class definition")\r | |
191 | \r | |
192 | # now stick the metaclass in the arglist\r | |
193 | meta_txt = last_metaclass.children[0].children[0]\r | |
194 | meta_txt.value = 'metaclass'\r | |
195 | orig_meta_prefix = meta_txt.prefix\r | |
196 | \r | |
197 | if arglist.children:\r | |
198 | arglist.append_child(Leaf(token.COMMA, u','))\r | |
199 | meta_txt.prefix = u' '\r | |
200 | else:\r | |
201 | meta_txt.prefix = u''\r | |
202 | \r | |
203 | # compact the expression "metaclass = Meta" -> "metaclass=Meta"\r | |
204 | expr_stmt = last_metaclass.children[0]\r | |
205 | assert expr_stmt.type == syms.expr_stmt\r | |
206 | expr_stmt.children[1].prefix = u''\r | |
207 | expr_stmt.children[2].prefix = u''\r | |
208 | \r | |
209 | arglist.append_child(last_metaclass)\r | |
210 | \r | |
211 | fixup_indent(suite)\r | |
212 | \r | |
213 | # check for empty suite\r | |
214 | if not suite.children:\r | |
215 | # one-liner that was just __metaclass_\r | |
216 | suite.remove()\r | |
217 | pass_leaf = Leaf(text_type, u'pass')\r | |
218 | pass_leaf.prefix = orig_meta_prefix\r | |
219 | node.append_child(pass_leaf)\r | |
220 | node.append_child(Leaf(token.NEWLINE, u'\n'))\r | |
221 | \r | |
222 | elif len(suite.children) > 1 and \\r | |
223 | (suite.children[-2].type == token.INDENT and\r | |
224 | suite.children[-1].type == token.DEDENT):\r | |
225 | # there was only one line in the class body and it was __metaclass__\r | |
226 | pass_leaf = Leaf(text_type, u'pass')\r | |
227 | suite.insert_child(-1, pass_leaf)\r | |
228 | suite.insert_child(-1, Leaf(token.NEWLINE, u'\n'))\r |