| Index: third_party/google-endpoints/libfuturize/fixes/fix_metaclass.py
|
| diff --git a/third_party/google-endpoints/libfuturize/fixes/fix_metaclass.py b/third_party/google-endpoints/libfuturize/fixes/fix_metaclass.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2ac41c9728d1bd96ce5c8bb76c5a53fb2b96ce2b
|
| --- /dev/null
|
| +++ b/third_party/google-endpoints/libfuturize/fixes/fix_metaclass.py
|
| @@ -0,0 +1,262 @@
|
| +# coding: utf-8
|
| +"""Fixer for __metaclass__ = X -> (future.utils.with_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.
|
| +"""
|
| +# This is a derived work of Lib/lib2to3/fixes/fix_metaclass.py under the
|
| +# copyright of the Python Software Foundation, licensed under the Python
|
| +# Software Foundation License 2.
|
| +#
|
| +# Copyright notice:
|
| +#
|
| +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
| +# 2011, 2012, 2013 Python Software Foundation. All rights reserved.
|
| +#
|
| +# Full license text: http://docs.python.org/3.4/license.html
|
| +
|
| +# Author: Jack Diederich, Daniel Neuhäuser
|
| +
|
| +# Local imports
|
| +from lib2to3 import fixer_base
|
| +from lib2to3.pygram import token
|
| +from lib2to3.fixer_util import Name, syms, Node, Leaf, touch_import, Call, \
|
| + String, Comma, parenthesize
|
| +
|
| +
|
| +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 preferred 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 == u'__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 = u''
|
| + 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, u')'))
|
| + node.insert_child(2, arglist)
|
| + node.insert_child(2, Leaf(token.LPAR, u'('))
|
| + 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
|
| +
|
| + # Was: touch_import(None, u'future.utils', node)
|
| + touch_import(u'future.utils', u'with_metaclass', node)
|
| +
|
| + metaclass = last_metaclass.children[0].children[2].clone()
|
| + metaclass.prefix = u''
|
| +
|
| + arguments = [metaclass]
|
| +
|
| + if arglist.children:
|
| + if len(arglist.children) == 1:
|
| + base = arglist.children[0].clone()
|
| + base.prefix = u' '
|
| + else:
|
| + # Unfortunately six.with_metaclass() only allows one base
|
| + # class, so we have to dynamically generate a base class if
|
| + # there is more than one.
|
| + bases = parenthesize(arglist.clone())
|
| + bases.prefix = u' '
|
| + base = Call(Name('type'), [
|
| + String("'NewBase'"),
|
| + Comma(),
|
| + bases,
|
| + Comma(),
|
| + Node(
|
| + syms.atom,
|
| + [Leaf(token.LBRACE, u'{'), Leaf(token.RBRACE, u'}')],
|
| + prefix=u' '
|
| + )
|
| + ], prefix=u' ')
|
| + arguments.extend([Comma(), base])
|
| +
|
| + arglist.replace(Call(
|
| + Name(u'with_metaclass', prefix=arglist.prefix),
|
| + arguments
|
| + ))
|
| +
|
| + fixup_indent(suite)
|
| +
|
| + # check for empty suite
|
| + if not suite.children:
|
| + # one-liner that was just __metaclass_
|
| + suite.remove()
|
| + pass_leaf = Leaf(text_type, u'pass')
|
| + pass_leaf.prefix = orig_meta_prefix
|
| + node.append_child(pass_leaf)
|
| + node.append_child(Leaf(token.NEWLINE, u'\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, u'pass')
|
| + suite.insert_child(-1, pass_leaf)
|
| + suite.insert_child(-1, Leaf(token.NEWLINE, u'\n'))
|
|
|