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