Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/libfuturize/fixes/fix_metaclass.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
| author | shellac |
|---|---|
| date | Sat, 02 May 2020 07:14:21 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:26e78fe6e8c4 |
|---|---|
| 1 # coding: utf-8 | |
| 2 """Fixer for __metaclass__ = X -> (future.utils.with_metaclass(X)) methods. | |
| 3 | |
| 4 The various forms of classef (inherits nothing, inherits once, inherints | |
| 5 many) don't parse the same in the CST so we look at ALL classes for | |
| 6 a __metaclass__ and if we find one normalize the inherits to all be | |
| 7 an arglist. | |
| 8 | |
| 9 For one-liner classes ('class X: pass') there is no indent/dedent so | |
| 10 we normalize those into having a suite. | |
| 11 | |
| 12 Moving the __metaclass__ into the classdef can also cause the class | |
| 13 body to be empty so there is some special casing for that as well. | |
| 14 | |
| 15 This fixer also tries very hard to keep original indenting and spacing | |
| 16 in all those corner cases. | |
| 17 """ | |
| 18 # This is a derived work of Lib/lib2to3/fixes/fix_metaclass.py under the | |
| 19 # copyright of the Python Software Foundation, licensed under the Python | |
| 20 # Software Foundation License 2. | |
| 21 # | |
| 22 # Copyright notice: | |
| 23 # | |
| 24 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, | |
| 25 # 2011, 2012, 2013 Python Software Foundation. All rights reserved. | |
| 26 # | |
| 27 # Full license text: http://docs.python.org/3.4/license.html | |
| 28 | |
| 29 # Author: Jack Diederich, Daniel Neuhäuser | |
| 30 | |
| 31 # Local imports | |
| 32 from lib2to3 import fixer_base | |
| 33 from lib2to3.pygram import token | |
| 34 from lib2to3.fixer_util import Name, syms, Node, Leaf, touch_import, Call, \ | |
| 35 String, Comma, parenthesize | |
| 36 | |
| 37 | |
| 38 def has_metaclass(parent): | |
| 39 """ we have to check the cls_node without changing it. | |
| 40 There are two possiblities: | |
| 41 1) clsdef => suite => simple_stmt => expr_stmt => Leaf('__meta') | |
| 42 2) clsdef => simple_stmt => expr_stmt => Leaf('__meta') | |
| 43 """ | |
| 44 for node in parent.children: | |
| 45 if node.type == syms.suite: | |
| 46 return has_metaclass(node) | |
| 47 elif node.type == syms.simple_stmt and node.children: | |
| 48 expr_node = node.children[0] | |
| 49 if expr_node.type == syms.expr_stmt and expr_node.children: | |
| 50 left_side = expr_node.children[0] | |
| 51 if isinstance(left_side, Leaf) and \ | |
| 52 left_side.value == '__metaclass__': | |
| 53 return True | |
| 54 return False | |
| 55 | |
| 56 | |
| 57 def fixup_parse_tree(cls_node): | |
| 58 """ one-line classes don't get a suite in the parse tree so we add | |
| 59 one to normalize the tree | |
| 60 """ | |
| 61 for node in cls_node.children: | |
| 62 if node.type == syms.suite: | |
| 63 # already in the preferred format, do nothing | |
| 64 return | |
| 65 | |
| 66 # !%@#! oneliners have no suite node, we have to fake one up | |
| 67 for i, node in enumerate(cls_node.children): | |
| 68 if node.type == token.COLON: | |
| 69 break | |
| 70 else: | |
| 71 raise ValueError("No class suite and no ':'!") | |
| 72 | |
| 73 # move everything into a suite node | |
| 74 suite = Node(syms.suite, []) | |
| 75 while cls_node.children[i+1:]: | |
| 76 move_node = cls_node.children[i+1] | |
| 77 suite.append_child(move_node.clone()) | |
| 78 move_node.remove() | |
| 79 cls_node.append_child(suite) | |
| 80 node = suite | |
| 81 | |
| 82 | |
| 83 def fixup_simple_stmt(parent, i, stmt_node): | |
| 84 """ if there is a semi-colon all the parts count as part of the same | |
| 85 simple_stmt. We just want the __metaclass__ part so we move | |
| 86 everything efter the semi-colon into its own simple_stmt node | |
| 87 """ | |
| 88 for semi_ind, node in enumerate(stmt_node.children): | |
| 89 if node.type == token.SEMI: # *sigh* | |
| 90 break | |
| 91 else: | |
| 92 return | |
| 93 | |
| 94 node.remove() # kill the semicolon | |
| 95 new_expr = Node(syms.expr_stmt, []) | |
| 96 new_stmt = Node(syms.simple_stmt, [new_expr]) | |
| 97 while stmt_node.children[semi_ind:]: | |
| 98 move_node = stmt_node.children[semi_ind] | |
| 99 new_expr.append_child(move_node.clone()) | |
| 100 move_node.remove() | |
| 101 parent.insert_child(i, new_stmt) | |
| 102 new_leaf1 = new_stmt.children[0].children[0] | |
| 103 old_leaf1 = stmt_node.children[0].children[0] | |
| 104 new_leaf1.prefix = old_leaf1.prefix | |
| 105 | |
| 106 | |
| 107 def remove_trailing_newline(node): | |
| 108 if node.children and node.children[-1].type == token.NEWLINE: | |
| 109 node.children[-1].remove() | |
| 110 | |
| 111 | |
| 112 def find_metas(cls_node): | |
| 113 # find the suite node (Mmm, sweet nodes) | |
| 114 for node in cls_node.children: | |
| 115 if node.type == syms.suite: | |
| 116 break | |
| 117 else: | |
| 118 raise ValueError("No class suite!") | |
| 119 | |
| 120 # look for simple_stmt[ expr_stmt[ Leaf('__metaclass__') ] ] | |
| 121 for i, simple_node in list(enumerate(node.children)): | |
| 122 if simple_node.type == syms.simple_stmt and simple_node.children: | |
| 123 expr_node = simple_node.children[0] | |
| 124 if expr_node.type == syms.expr_stmt and expr_node.children: | |
| 125 # Check if the expr_node is a simple assignment. | |
| 126 left_node = expr_node.children[0] | |
| 127 if isinstance(left_node, Leaf) and \ | |
| 128 left_node.value == u'__metaclass__': | |
| 129 # We found a assignment to __metaclass__. | |
| 130 fixup_simple_stmt(node, i, simple_node) | |
| 131 remove_trailing_newline(simple_node) | |
| 132 yield (node, i, simple_node) | |
| 133 | |
| 134 | |
| 135 def fixup_indent(suite): | |
| 136 """ If an INDENT is followed by a thing with a prefix then nuke the prefix | |
| 137 Otherwise we get in trouble when removing __metaclass__ at suite start | |
| 138 """ | |
| 139 kids = suite.children[::-1] | |
| 140 # find the first indent | |
| 141 while kids: | |
| 142 node = kids.pop() | |
| 143 if node.type == token.INDENT: | |
| 144 break | |
| 145 | |
| 146 # find the first Leaf | |
| 147 while kids: | |
| 148 node = kids.pop() | |
| 149 if isinstance(node, Leaf) and node.type != token.DEDENT: | |
| 150 if node.prefix: | |
| 151 node.prefix = u'' | |
| 152 return | |
| 153 else: | |
| 154 kids.extend(node.children[::-1]) | |
| 155 | |
| 156 | |
| 157 class FixMetaclass(fixer_base.BaseFix): | |
| 158 BM_compatible = True | |
| 159 | |
| 160 PATTERN = """ | |
| 161 classdef<any*> | |
| 162 """ | |
| 163 | |
| 164 def transform(self, node, results): | |
| 165 if not has_metaclass(node): | |
| 166 return | |
| 167 | |
| 168 fixup_parse_tree(node) | |
| 169 | |
| 170 # find metaclasses, keep the last one | |
| 171 last_metaclass = None | |
| 172 for suite, i, stmt in find_metas(node): | |
| 173 last_metaclass = stmt | |
| 174 stmt.remove() | |
| 175 | |
| 176 text_type = node.children[0].type # always Leaf(nnn, 'class') | |
| 177 | |
| 178 # figure out what kind of classdef we have | |
| 179 if len(node.children) == 7: | |
| 180 # Node(classdef, ['class', 'name', '(', arglist, ')', ':', suite]) | |
| 181 # 0 1 2 3 4 5 6 | |
| 182 if node.children[3].type == syms.arglist: | |
| 183 arglist = node.children[3] | |
| 184 # Node(classdef, ['class', 'name', '(', 'Parent', ')', ':', suite]) | |
| 185 else: | |
| 186 parent = node.children[3].clone() | |
| 187 arglist = Node(syms.arglist, [parent]) | |
| 188 node.set_child(3, arglist) | |
| 189 elif len(node.children) == 6: | |
| 190 # Node(classdef, ['class', 'name', '(', ')', ':', suite]) | |
| 191 # 0 1 2 3 4 5 | |
| 192 arglist = Node(syms.arglist, []) | |
| 193 node.insert_child(3, arglist) | |
| 194 elif len(node.children) == 4: | |
| 195 # Node(classdef, ['class', 'name', ':', suite]) | |
| 196 # 0 1 2 3 | |
| 197 arglist = Node(syms.arglist, []) | |
| 198 node.insert_child(2, Leaf(token.RPAR, u')')) | |
| 199 node.insert_child(2, arglist) | |
| 200 node.insert_child(2, Leaf(token.LPAR, u'(')) | |
| 201 else: | |
| 202 raise ValueError("Unexpected class definition") | |
| 203 | |
| 204 # now stick the metaclass in the arglist | |
| 205 meta_txt = last_metaclass.children[0].children[0] | |
| 206 meta_txt.value = 'metaclass' | |
| 207 orig_meta_prefix = meta_txt.prefix | |
| 208 | |
| 209 # Was: touch_import(None, u'future.utils', node) | |
| 210 touch_import(u'future.utils', u'with_metaclass', node) | |
| 211 | |
| 212 metaclass = last_metaclass.children[0].children[2].clone() | |
| 213 metaclass.prefix = u'' | |
| 214 | |
| 215 arguments = [metaclass] | |
| 216 | |
| 217 if arglist.children: | |
| 218 if len(arglist.children) == 1: | |
| 219 base = arglist.children[0].clone() | |
| 220 base.prefix = u' ' | |
| 221 else: | |
| 222 # Unfortunately six.with_metaclass() only allows one base | |
| 223 # class, so we have to dynamically generate a base class if | |
| 224 # there is more than one. | |
| 225 bases = parenthesize(arglist.clone()) | |
| 226 bases.prefix = u' ' | |
| 227 base = Call(Name('type'), [ | |
| 228 String("'NewBase'"), | |
| 229 Comma(), | |
| 230 bases, | |
| 231 Comma(), | |
| 232 Node( | |
| 233 syms.atom, | |
| 234 [Leaf(token.LBRACE, u'{'), Leaf(token.RBRACE, u'}')], | |
| 235 prefix=u' ' | |
| 236 ) | |
| 237 ], prefix=u' ') | |
| 238 arguments.extend([Comma(), base]) | |
| 239 | |
| 240 arglist.replace(Call( | |
| 241 Name(u'with_metaclass', prefix=arglist.prefix), | |
| 242 arguments | |
| 243 )) | |
| 244 | |
| 245 fixup_indent(suite) | |
| 246 | |
| 247 # check for empty suite | |
| 248 if not suite.children: | |
| 249 # one-liner that was just __metaclass_ | |
| 250 suite.remove() | |
| 251 pass_leaf = Leaf(text_type, u'pass') | |
| 252 pass_leaf.prefix = orig_meta_prefix | |
| 253 node.append_child(pass_leaf) | |
| 254 node.append_child(Leaf(token.NEWLINE, u'\n')) | |
| 255 | |
| 256 elif len(suite.children) > 1 and \ | |
| 257 (suite.children[-2].type == token.INDENT and | |
| 258 suite.children[-1].type == token.DEDENT): | |
| 259 # there was only one line in the class body and it was __metaclass__ | |
| 260 pass_leaf = Leaf(text_type, u'pass') | |
| 261 suite.insert_child(-1, pass_leaf) | |
| 262 suite.insert_child(-1, Leaf(token.NEWLINE, u'\n')) |
