home *** CD-ROM | disk | FTP | other *** search
/ Freelog 125 / Freelog_MarsAvril2015_No125.iso / Bureautique / OpenOffice / Apache_OpenOffice_4.1.1_Win_x86_install_fr.exe / openoffice1.cab / fix_metaclass.py < prev    next >
Text File  |  2014-07-29  |  8KB  |  229 lines

  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 preferred 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 after 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.prefix = old_leaf1.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.                 # Check if the expr_node is a simple assignment.
  114.                 left_node = expr_node.children[0]
  115.                 if isinstance(left_node, Leaf) and \
  116.                         left_node.value == u'__metaclass__':
  117.                     # We found a assignment to __metaclass__.
  118.                     fixup_simple_stmt(node, i, simple_node)
  119.                     remove_trailing_newline(simple_node)
  120.                     yield (node, i, simple_node)
  121.  
  122.  
  123. def fixup_indent(suite):
  124.     """ If an INDENT is followed by a thing with a prefix then nuke the prefix
  125.         Otherwise we get in trouble when removing __metaclass__ at suite start
  126.     """
  127.     kids = suite.children[::-1]
  128.     # find the first indent
  129.     while kids:
  130.         node = kids.pop()
  131.         if node.type == token.INDENT:
  132.             break
  133.  
  134.     # find the first Leaf
  135.     while kids:
  136.         node = kids.pop()
  137.         if isinstance(node, Leaf) and node.type != token.DEDENT:
  138.             if node.prefix:
  139.                 node.prefix = u''
  140.             return
  141.         else:
  142.             kids.extend(node.children[::-1])
  143.  
  144.  
  145. class FixMetaclass(fixer_base.BaseFix):
  146.     BM_compatible = True
  147.  
  148.     PATTERN = """
  149.     classdef<any*>
  150.     """
  151.  
  152.     def transform(self, node, results):
  153.         if not has_metaclass(node):
  154.             return
  155.  
  156.         fixup_parse_tree(node)
  157.  
  158.         # find metaclasses, keep the last one
  159.         last_metaclass = None
  160.         for suite, i, stmt in find_metas(node):
  161.             last_metaclass = stmt
  162.             stmt.remove()
  163.  
  164.         text_type = node.children[0].type # always Leaf(nnn, 'class')
  165.  
  166.         # figure out what kind of classdef we have
  167.         if len(node.children) == 7:
  168.             # Node(classdef, ['class', 'name', '(', arglist, ')', ':', suite])
  169.             #                 0        1       2    3        4    5    6
  170.             if node.children[3].type == syms.arglist:
  171.                 arglist = node.children[3]
  172.             # Node(classdef, ['class', 'name', '(', 'Parent', ')', ':', suite])
  173.             else:
  174.                 parent = node.children[3].clone()
  175.                 arglist = Node(syms.arglist, [parent])
  176.                 node.set_child(3, arglist)
  177.         elif len(node.children) == 6:
  178.             # Node(classdef, ['class', 'name', '(',  ')', ':', suite])
  179.             #                 0        1       2     3    4    5
  180.             arglist = Node(syms.arglist, [])
  181.             node.insert_child(3, arglist)
  182.         elif len(node.children) == 4:
  183.             # Node(classdef, ['class', 'name', ':', suite])
  184.             #                 0        1       2    3
  185.             arglist = Node(syms.arglist, [])
  186.             node.insert_child(2, Leaf(token.RPAR, u')'))
  187.             node.insert_child(2, arglist)
  188.             node.insert_child(2, Leaf(token.LPAR, u'('))
  189.         else:
  190.             raise ValueError("Unexpected class definition")
  191.  
  192.         # now stick the metaclass in the arglist
  193.         meta_txt = last_metaclass.children[0].children[0]
  194.         meta_txt.value = 'metaclass'
  195.         orig_meta_prefix = meta_txt.prefix
  196.  
  197.         if arglist.children:
  198.             arglist.append_child(Leaf(token.COMMA, u','))
  199.             meta_txt.prefix = u' '
  200.         else:
  201.             meta_txt.prefix = u''
  202.  
  203.         # compact the expression "metaclass = Meta" -> "metaclass=Meta"
  204.         expr_stmt = last_metaclass.children[0]
  205.         assert expr_stmt.type == syms.expr_stmt
  206.         expr_stmt.children[1].prefix = u''
  207.         expr_stmt.children[2].prefix = u''
  208.  
  209.         arglist.append_child(last_metaclass)
  210.  
  211.         fixup_indent(suite)
  212.  
  213.         # check for empty suite
  214.         if not suite.children:
  215.             # one-liner that was just __metaclass_
  216.             suite.remove()
  217.             pass_leaf = Leaf(text_type, u'pass')
  218.             pass_leaf.prefix = orig_meta_prefix
  219.             node.append_child(pass_leaf)
  220.             node.append_child(Leaf(token.NEWLINE, u'\n'))
  221.  
  222.         elif len(suite.children) > 1 and \
  223.                  (suite.children[-2].type == token.INDENT and
  224.                   suite.children[-1].type == token.DEDENT):
  225.             # there was only one line in the class body and it was __metaclass__
  226.             pass_leaf = Leaf(text_type, u'pass')
  227.             suite.insert_child(-1, pass_leaf)
  228.             suite.insert_child(-1, Leaf(token.NEWLINE, u'\n'))
  229.