mirror of
https://github.com/python/cpython.git
synced 2024-12-01 03:01:36 +01:00
b0871cac11
svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r85510 | benjamin.peterson | 2010-10-14 18:00:04 -0500 (Thu, 14 Oct 2010) | 61 lines Merged revisions 83852-83853,83857,84042,84216,84274-84276,84375,85388,85478,85506-85508 via svnmerge from svn+ssh://pythondev@svn.python.org/sandbox/trunk/2to3/lib2to3 ........ r83852 | benjamin.peterson | 2010-08-08 15:45:44 -0500 (Sun, 08 Aug 2010) | 1 line wrap with parens ........ r83853 | benjamin.peterson | 2010-08-08 15:46:31 -0500 (Sun, 08 Aug 2010) | 1 line use parens ........ r83857 | benjamin.peterson | 2010-08-08 15:59:49 -0500 (Sun, 08 Aug 2010) | 1 line things which use touch_import should be pre order ........ r84042 | george.boutsioukis | 2010-08-14 16:10:19 -0500 (Sat, 14 Aug 2010) | 2 lines This revision incorporates into the 2to3 tool the new, faster, tree matching algorithm developed during a GSOC project. The algorithm resides in the two added modules, btm_matcher and btm_utils. New code has been added to drive the new matching process in refactor.py and a few minor changes were made in other modules. A BM_compatible flag(False by default) has been added in fixer_base and it is set to True in most of the current fixers. ........ r84216 | benjamin.peterson | 2010-08-19 16:44:05 -0500 (Thu, 19 Aug 2010) | 1 line allow star_expr in testlist_gexp ........ r84274 | benjamin.peterson | 2010-08-22 18:40:46 -0500 (Sun, 22 Aug 2010) | 1 line wrap long line ........ r84275 | benjamin.peterson | 2010-08-22 18:42:22 -0500 (Sun, 22 Aug 2010) | 1 line cleanup ........ r84276 | benjamin.peterson | 2010-08-22 18:51:01 -0500 (Sun, 22 Aug 2010) | 1 line when there's a None value and a traceback, don't call type with it #9661 ........ r84375 | george.boutsioukis | 2010-08-31 08:38:53 -0500 (Tue, 31 Aug 2010) | 3 lines Idiomatic code changes & stylistic issues fixed in the BottomMatcher module. Thanks to Benjamin Peterson for taking the time to review the code. ........ r85388 | benjamin.peterson | 2010-10-12 17:27:44 -0500 (Tue, 12 Oct 2010) | 1 line fix urllib fixer with multiple as imports on a line #10069 ........ r85478 | benjamin.peterson | 2010-10-14 08:09:56 -0500 (Thu, 14 Oct 2010) | 1 line stop abusing docstrings ........ r85506 | benjamin.peterson | 2010-10-14 17:45:19 -0500 (Thu, 14 Oct 2010) | 1 line kill sibling import ........ r85507 | benjamin.peterson | 2010-10-14 17:54:15 -0500 (Thu, 14 Oct 2010) | 1 line remove trailing whitespace ........ r85508 | benjamin.peterson | 2010-10-14 17:55:28 -0500 (Thu, 14 Oct 2010) | 1 line typo ........ ................
229 lines
8.0 KiB
Python
229 lines
8.0 KiB
Python
"""Fixer for __metaclass__ = X -> (metaclass=X) methods.
|
|
|
|
The various forms of classef (inherits nothing, inherits once, inherints
|
|
many) don't parse the same in the CST so we look at ALL classes for
|
|
a __metaclass__ and if we find one normalize the inherits to all be
|
|
an arglist.
|
|
|
|
For one-liner classes ('class X: pass') there is no indent/dedent so
|
|
we normalize those into having a suite.
|
|
|
|
Moving the __metaclass__ into the classdef can also cause the class
|
|
body to be empty so there is some special casing for that as well.
|
|
|
|
This fixer also tries very hard to keep original indenting and spacing
|
|
in all those corner cases.
|
|
|
|
"""
|
|
# Author: Jack Diederich
|
|
|
|
# Local imports
|
|
from .. import fixer_base
|
|
from ..pygram import token
|
|
from ..fixer_util import Name, syms, Node, Leaf
|
|
|
|
|
|
def has_metaclass(parent):
|
|
""" we have to check the cls_node without changing it.
|
|
There are two possiblities:
|
|
1) clsdef => suite => simple_stmt => expr_stmt => Leaf('__meta')
|
|
2) clsdef => simple_stmt => expr_stmt => Leaf('__meta')
|
|
"""
|
|
for node in parent.children:
|
|
if node.type == syms.suite:
|
|
return has_metaclass(node)
|
|
elif node.type == syms.simple_stmt and node.children:
|
|
expr_node = node.children[0]
|
|
if expr_node.type == syms.expr_stmt and expr_node.children:
|
|
left_side = expr_node.children[0]
|
|
if isinstance(left_side, Leaf) and \
|
|
left_side.value == '__metaclass__':
|
|
return True
|
|
return False
|
|
|
|
|
|
def fixup_parse_tree(cls_node):
|
|
""" one-line classes don't get a suite in the parse tree so we add
|
|
one to normalize the tree
|
|
"""
|
|
for node in cls_node.children:
|
|
if node.type == syms.suite:
|
|
# already in the prefered format, do nothing
|
|
return
|
|
|
|
# !%@#! oneliners have no suite node, we have to fake one up
|
|
for i, node in enumerate(cls_node.children):
|
|
if node.type == token.COLON:
|
|
break
|
|
else:
|
|
raise ValueError("No class suite and no ':'!")
|
|
|
|
# move everything into a suite node
|
|
suite = Node(syms.suite, [])
|
|
while cls_node.children[i+1:]:
|
|
move_node = cls_node.children[i+1]
|
|
suite.append_child(move_node.clone())
|
|
move_node.remove()
|
|
cls_node.append_child(suite)
|
|
node = suite
|
|
|
|
|
|
def fixup_simple_stmt(parent, i, stmt_node):
|
|
""" if there is a semi-colon all the parts count as part of the same
|
|
simple_stmt. We just want the __metaclass__ part so we move
|
|
everything efter the semi-colon into its own simple_stmt node
|
|
"""
|
|
for semi_ind, node in enumerate(stmt_node.children):
|
|
if node.type == token.SEMI: # *sigh*
|
|
break
|
|
else:
|
|
return
|
|
|
|
node.remove() # kill the semicolon
|
|
new_expr = Node(syms.expr_stmt, [])
|
|
new_stmt = Node(syms.simple_stmt, [new_expr])
|
|
while stmt_node.children[semi_ind:]:
|
|
move_node = stmt_node.children[semi_ind]
|
|
new_expr.append_child(move_node.clone())
|
|
move_node.remove()
|
|
parent.insert_child(i, new_stmt)
|
|
new_leaf1 = new_stmt.children[0].children[0]
|
|
old_leaf1 = stmt_node.children[0].children[0]
|
|
new_leaf1.prefix = old_leaf1.prefix
|
|
|
|
|
|
def remove_trailing_newline(node):
|
|
if node.children and node.children[-1].type == token.NEWLINE:
|
|
node.children[-1].remove()
|
|
|
|
|
|
def find_metas(cls_node):
|
|
# find the suite node (Mmm, sweet nodes)
|
|
for node in cls_node.children:
|
|
if node.type == syms.suite:
|
|
break
|
|
else:
|
|
raise ValueError("No class suite!")
|
|
|
|
# look for simple_stmt[ expr_stmt[ Leaf('__metaclass__') ] ]
|
|
for i, simple_node in list(enumerate(node.children)):
|
|
if simple_node.type == syms.simple_stmt and simple_node.children:
|
|
expr_node = simple_node.children[0]
|
|
if expr_node.type == syms.expr_stmt and expr_node.children:
|
|
# Check if the expr_node is a simple assignment.
|
|
left_node = expr_node.children[0]
|
|
if isinstance(left_node, Leaf) and \
|
|
left_node.value == '__metaclass__':
|
|
# We found a assignment to __metaclass__.
|
|
fixup_simple_stmt(node, i, simple_node)
|
|
remove_trailing_newline(simple_node)
|
|
yield (node, i, simple_node)
|
|
|
|
|
|
def fixup_indent(suite):
|
|
""" If an INDENT is followed by a thing with a prefix then nuke the prefix
|
|
Otherwise we get in trouble when removing __metaclass__ at suite start
|
|
"""
|
|
kids = suite.children[::-1]
|
|
# find the first indent
|
|
while kids:
|
|
node = kids.pop()
|
|
if node.type == token.INDENT:
|
|
break
|
|
|
|
# find the first Leaf
|
|
while kids:
|
|
node = kids.pop()
|
|
if isinstance(node, Leaf) and node.type != token.DEDENT:
|
|
if node.prefix:
|
|
node.prefix = ''
|
|
return
|
|
else:
|
|
kids.extend(node.children[::-1])
|
|
|
|
|
|
class FixMetaclass(fixer_base.BaseFix):
|
|
BM_compatible = True
|
|
|
|
PATTERN = """
|
|
classdef<any*>
|
|
"""
|
|
|
|
def transform(self, node, results):
|
|
if not has_metaclass(node):
|
|
return
|
|
|
|
fixup_parse_tree(node)
|
|
|
|
# find metaclasses, keep the last one
|
|
last_metaclass = None
|
|
for suite, i, stmt in find_metas(node):
|
|
last_metaclass = stmt
|
|
stmt.remove()
|
|
|
|
text_type = node.children[0].type # always Leaf(nnn, 'class')
|
|
|
|
# figure out what kind of classdef we have
|
|
if len(node.children) == 7:
|
|
# Node(classdef, ['class', 'name', '(', arglist, ')', ':', suite])
|
|
# 0 1 2 3 4 5 6
|
|
if node.children[3].type == syms.arglist:
|
|
arglist = node.children[3]
|
|
# Node(classdef, ['class', 'name', '(', 'Parent', ')', ':', suite])
|
|
else:
|
|
parent = node.children[3].clone()
|
|
arglist = Node(syms.arglist, [parent])
|
|
node.set_child(3, arglist)
|
|
elif len(node.children) == 6:
|
|
# Node(classdef, ['class', 'name', '(', ')', ':', suite])
|
|
# 0 1 2 3 4 5
|
|
arglist = Node(syms.arglist, [])
|
|
node.insert_child(3, arglist)
|
|
elif len(node.children) == 4:
|
|
# Node(classdef, ['class', 'name', ':', suite])
|
|
# 0 1 2 3
|
|
arglist = Node(syms.arglist, [])
|
|
node.insert_child(2, Leaf(token.RPAR, ')'))
|
|
node.insert_child(2, arglist)
|
|
node.insert_child(2, Leaf(token.LPAR, '('))
|
|
else:
|
|
raise ValueError("Unexpected class definition")
|
|
|
|
# now stick the metaclass in the arglist
|
|
meta_txt = last_metaclass.children[0].children[0]
|
|
meta_txt.value = 'metaclass'
|
|
orig_meta_prefix = meta_txt.prefix
|
|
|
|
if arglist.children:
|
|
arglist.append_child(Leaf(token.COMMA, ','))
|
|
meta_txt.prefix = ' '
|
|
else:
|
|
meta_txt.prefix = ''
|
|
|
|
# compact the expression "metaclass = Meta" -> "metaclass=Meta"
|
|
expr_stmt = last_metaclass.children[0]
|
|
assert expr_stmt.type == syms.expr_stmt
|
|
expr_stmt.children[1].prefix = ''
|
|
expr_stmt.children[2].prefix = ''
|
|
|
|
arglist.append_child(last_metaclass)
|
|
|
|
fixup_indent(suite)
|
|
|
|
# check for empty suite
|
|
if not suite.children:
|
|
# one-liner that was just __metaclass_
|
|
suite.remove()
|
|
pass_leaf = Leaf(text_type, 'pass')
|
|
pass_leaf.prefix = orig_meta_prefix
|
|
node.append_child(pass_leaf)
|
|
node.append_child(Leaf(token.NEWLINE, '\n'))
|
|
|
|
elif len(suite.children) > 1 and \
|
|
(suite.children[-2].type == token.INDENT and
|
|
suite.children[-1].type == token.DEDENT):
|
|
# there was only one line in the class body and it was __metaclass__
|
|
pass_leaf = Leaf(text_type, 'pass')
|
|
suite.insert_child(-1, pass_leaf)
|
|
suite.insert_child(-1, Leaf(token.NEWLINE, '\n'))
|