ยปCore Development>Code coverage>Lib/lib2to3/fixes/fix_metaclass.py

Python code coverage for Lib/lib2to3/fixes/fix_metaclass.py

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