def transform(self, node, results): exc = results["exc"].clone() val = results["val"].clone() tb = results["tb"].clone() exc.prefix = u"" val.prefix = tb.prefix = u" " touch_import(None, u'six', node) return Call(Name(u"six.reraise"), [exc, Comma(), val, Comma(), tb], prefix=node.prefix)
def add_provide_context_to_python_operator(node: LN, capture: Capture, filename: Filename) -> None: fn_args = capture['function_arguments'][0] fn_args.append_child(Comma()) provide_context_arg = KeywordArg(Name('provide_context'), Name('True')) provide_context_arg.prefix = fn_args.children[0].prefix fn_args.append_child(provide_context_arg)
def join_comma(entries: List[LN]) -> List[LN]: """Join a list with a comma token""" out = [entries[0]] if len(entries) > 1: for entry in entries[1:]: out.append(Comma()) out.append(entry) return out
def join_comma(entries): """Return a copy of a list with comma tokens between items""" out = [entries[0]] if len(entries) > 1: for entry in entries[1:]: out.append(Comma()) out.append(entry) return out
def _Call(self, name, args=None, prefix=None): """Help the next test""" children = [] if isinstance(args, list): for arg in args: children.append(arg) children.append(Comma()) children.pop() return Call(Name(name), children, prefix)
def add_provide_context_to_python_operator(node: LN, capture: Capture, filename: Filename) -> None: fn_args = capture['function_arguments'][0] if len(fn_args.children) > 0 and (not isinstance(fn_args.children[-1], Leaf) or fn_args.children[-1].type != token.COMMA): fn_args.append_child(Comma()) provide_context_arg = KeywordArg(Name('provide_context'), Name('True')) provide_context_arg.prefix = fn_args.children[0].prefix fn_args.append_child(provide_context_arg)
def process_format_format( node: LN, capture: Capture, filename: Filename # noqa: U100 ) -> Optional[LN]: formatstring = capture["formatstr"] if formatstring.type != token.STRING: print( "Not formatting {filename}:{node.get_lineno()} because indirect format" ) return None num_specifiers = len(RE_FORMAT_SPECIFIER.findall(formatstring.value)) understood = RE_UNDERSTOOD_FORMAT.findall(formatstring.value) if len(understood) != num_specifiers: print( f"Not formatting {filename}:{node.get_lineno()} because complex format specifiers:\n {formatstring.value.strip()}" ) return None # Basic {} formatstring.value = re.sub(r"{}", "%s", formatstring.value) # {:.3f} and friends formatstring.value = re.sub(r"(?<!{){:(\d*\.\d+f)}", r"%\1", formatstring.value) # {:>12} formatstring.value = re.sub(r"(?<!{){:>(\d+)}", r"%\1s", formatstring.value) # if RE_NONPLAIN_FORMAT.search(formatstring.value): # return None # print_node(node, capture=capture) # breakpoint() if isinstance(capture["arglist"], Leaf): arguments = [capture["arglist"]] else: arguments = list(capture["arglist"].children) # Clear the parents of moved nodes for child in arguments: child.remove() formatstring.remove() # Rewrite the format-string specifier formatstring.value = formatstring.value.replace("{}", "%s") # breakpoint() # Build the arglist capture["formatblock"].replace( Node(python_symbols.arglist, [formatstring, Comma()] + arguments)) # RE_NONPLAIN_FORMAT # print_node(node, capture=capture) # breakpoint() return None
def ListNode(*args): parts = [] for arg in args: if parts: parts.append(Space()) arg = arg.clone() arg.prefix = arg.prefix.lstrip() parts.append(arg) parts.append(Comma()) parts.pop() return Node(SYMBOL.atom, [LBrace(), *parts, RBrace()])
def modify_attr(node: LN, capture: Capture, filename: Filename) -> Optional[LN]: node.replace( Call( Name("getattr"), args=[ capture["obj"].clone(), Comma(), Space(), String('"' + capture["attr"].value + '"'), ], ))
def TupleNode(*args): parts = [] for arg in args: if parts: parts.append(Space()) arg = arg.clone() arg.prefix = arg.prefix.lstrip() parts.append(arg) parts.append(Comma()) if len(parts) != 2: parts.pop() return Node(SYMBOL.atom, [LParen(), *parts, RParen()])
def process_percent_format( node: LN, capture: Capture, filename: Filename # noqa: U100 ) -> Optional[LN]: # if capture["formatstr"] has more than one format point, don't # expand it for now # if capture["formatstr"]: # print_node(node, capture=capture) specifiers = None if capture["formatstr"].type == token.STRING: if RE_MAPPINGKEY.search(capture["formatstr"].value): print( f"Not formatting {filename}:{node.get_lineno()} as appears to have mapping key" ) return None specifiers = RE_SPECIFIERS.findall(capture["formatstr"].value) try: # Access precise chain of lookups for the inline-tuple case if capture["vars"].children[0].value == "(": inner = capture["vars"].children[1] if isinstance(inner, Node): format_args = inner.children elif isinstance(inner, Leaf): format_args = [inner] else: raise RuntimeError("Unknown type") else: # It's not this specific case, treat it as one item raise IndexError except IndexError: # Not a tuple, if we have more than one specifier then we can't # reliably rewrite this. if specifiers and len(specifiers) > 1: print( f"Not formatting {filename}:{node.get_lineno()} because unsafe rewriting indirect tuple" ) return None # We aren't something with a sub-tuple, assume single-argument format_args = [capture["vars"]] # Don't rewrite if # Prepare these node for transplant for child in format_args: child.parent = None capture["formatstr"].parent = None capture["call"].children[1] = Node( python_symbols.arglist, [capture["formatstr"], Comma()] + format_args) return None
def transform_import(self, node, results): """Transform for the basic import case. Replaces the old import name with a comma separated list of its replacements. """ import_mod = results.get("module") pref = import_mod.prefix names = [] # create a Node list of the replacement modules for name in MAPPING[import_mod.value][:-1]: names.extend([Name(name[0], prefix=pref), Comma()]) names.append(Name(MAPPING[import_mod.value][-1][0], prefix=pref)) import_mod.replace(names)
def on_delete_modifier(node, capture, filename) -> None: if not capture: return args: Node = capture.get("function_arguments")[0] has_on_delete = False for node in args.children: if isinstance(node, Node) and node.type == 261: if node.children[0].value == "on_delete": has_on_delete = True if not has_on_delete: if args.children[-1].value != ",": args.append_child(Comma()) args.append_child( KeywordArg(Name("on_delete", prefix=" "), Name("models.CASCADE")) )
def Assert(test, message=None, **kwargs): """Build an assertion statement""" if not isinstance(test, list): test = [test] test[0].prefix = " " if message is not None: if not isinstance(message, list): message = [message] message.insert(0, Comma()) message[1].prefix = " " return Node( syms.assert_stmt, [Leaf(TOKEN.NAME, "assert")] + test + (message or []), **kwargs, )
def handle_assert_regex(node, capture, arguments): """ self.assertRegex(text, pattern, msg) --> assert re.search(pattern, text), msg self.assertNotRegex(text, pattern, msg) --> assert not re.search(pattern, text), msg """ function_name = capture["function_name"].value invert = function_name in INVERT_FUNCTIONS function_name = SYNONYMS.get(function_name, function_name) num_arguments = ARGUMENTS[function_name] if len(arguments) not in (num_arguments, num_arguments + 1): # Not sure what this is. Leave it alone. return None if len(arguments) == num_arguments: message = None else: message = arguments.pop() if message.type == syms.argument: # keyword argument (e.g. `msg=abc`) message = message.children[2].clone() arguments[0].prefix = " " arguments[1].prefix = "" # Adds a 'import re' if there wasn't one already touch_import(None, "re", node) op_tokens = [] if invert: op_tokens.append(keyword("not")) assert_test_nodes = [ Node( syms.power, op_tokens + Attr(keyword("re"), keyword("search", prefix="")) + [ArgList([arguments[1], Comma(), arguments[0]])], ) ] return Assert(assert_test_nodes, message.clone() if message else None, prefix=node.prefix)
def pytest_approx(node, capture, filename): target_value = listify(capture['target_value'])[0].clone() target_value.prefix = '' abs_tolerance = capture['abs_tolerance'].clone() abs_tolerance.prefix = '' op_value = listify(capture['op'])[0].value # Adds a 'import pytest' if there wasn't one already touch_import(None, "pytest", node) if op_value in ('<', '<='): # as you'd expect in an assert statement operator = Leaf(TOKEN.EQEQUAL, '==', prefix=' ') else: # probably in an if statement operator = Leaf(TOKEN.NOTEQUAL, '!=', prefix=' ') node.replace( Node( syms.comparison, [ capture['lhs'].clone(), operator, Node( syms.power, [ kw('pytest'), Node( syms.trailer, [Leaf(TOKEN.DOT, ".", prefix=''), kw('approx', prefix='')], prefix='', ), ArgList( [ target_value, Comma(), KeywordArg(kw('abs'), abs_tolerance), ] ), ], ), ], prefix=node.prefix, ) )
def add_argument(filename, trailer_node, key, value): """ add "key=value" to arglist in trailer_node, if arglist already contains key, reassign key to value """ if trailer_node.type != python_symbols.trailer and len( trailer_node.children) != 3: log_warning( filename, trailer_node.get_lineno(), "node type is not trailer or len(children) != 3. you may need to call norm_arglist first." ) return arglist_node = trailer_node.children[1] if arglist_node.type != python_symbols.arglist: log_warning(filename, trailer_node.get_lineno(), "trailer_node.children[1] is not arglist.") return found_key = False for node in arglist_node.children: if node.type != python_symbols.argument: continue _key_node = node.children[0] if _key_node.value == key: found_key = True _value_node = node.children[2] if _value_node.value != value: _value_node_copy = _value_node.clone() _value_node_copy.type = token.NAME _value_node_copy.value = value _value_node.replace(_value_node_copy) log_warning( filename, arglist_node.get_lineno(), 'argument "{}" is reassigned to "{}"'.format(key, value)) break if not found_key: key_node = Name(key) value_node = Name(value) if arglist_node.children: arglist_node.append_child(Comma()) key_node.prefix = " " arg_node = KeywordArg(key_node, value_node) arglist_node.append_child(arg_node) log_warning(filename, arglist_node.get_lineno(), 'add argument "{}={}"'.format(key, value))
def rewrite_print(node, capture, filename): """Given a print node, rewrite to a logger.debug""" params = capture["function_parameters"] # Args is either a Leaf or an arglist Node here arglist = params.children[1] # Extract the arguments from this list if isinstance(arglist, Node) and arglist.type == syms.arglist: args = [x for x in arglist.children if not x.type == token.COMMA] # Remove kwargs for now non_kwargs = [x for x in args if not x.type == syms.argument] # Read out things like sep here sep = " " if not len(non_kwargs) == len(args): first_leaf = next(node.leaves()) print( "Warning: {}:{} Not handling keyword argument loggers".format( filename, first_leaf.lineno)) return None # If more than one child, we need to construct a new joiner string if len(non_kwargs) > 1: # Instead of placing a new, modify the first if it's a string if arglist.children[0].type == token.STRING: value = arglist.children[0].value last_char = value[-1] new_end = sep + sep.join(["%s"] * (len(non_kwargs) - 1)) arglist.children[0].value = value[:-1] + new_end + last_char else: arglist.insert_child( 0, String('"' + sep.join(["%s"] * len(non_kwargs)) + '"')) arglist.insert_child(1, Comma()) arglist.children[2].prefix = " " + arglist.children[2].prefix # Use the possibly rewritten parameters in the new call new_node = Attr(Name("logger"), Name("debug")) new_node[0].prefix = node.children[0].prefix node.children = new_node + [params]
def convert_regex_to_path_modifier(node: Node, capture: Capture, filename: Filename) -> None: # Replace the import if is_import(node): name_leafs = [ Name("path", prefix=" "), Comma(), Name("re_path", prefix=" "), ] node.replace([FromImport("django.url", name_leafs=name_leafs)]) # And function calls from url to path, re_path if capture and "function_arguments" in capture: function_node: Node = next(node.leaves()) args = capture.get("function_arguments") regex_leaf: Leaf = next(args[0].leaves()) converted = update_regex_to_path(regex_leaf.value) if converted == regex_leaf.value: function_node.replace(Name("re_path", prefix=function_node.prefix)) else: function_node.replace(Name("path", prefix=function_node.prefix)) regex_leaf.value = update_regex_to_path(regex_leaf.value)
def transform(self, node, results): # First, find the sys import. We'll just hope it's global scope. if "sys_import" in results: if self.sys_import is None: self.sys_import = results["sys_import"] return func = results["func"].clone() func.prefix = "" register = pytree.Node(syms.power, Attr(Name("atexit"), Name("register"))) call = Call(register, [func], node.prefix) node.replace(call) if self.sys_import is None: # That's interesting. self.warning( node, "Can't find sys import; Please add an atexit " "import at the top of your file.", ) return # Now add an atexit import after the sys import. names = self.sys_import.children[1] if names.type == syms.dotted_as_names: names.append_child(Comma()) names.append_child(Name("atexit", " ")) else: containing_stmt = self.sys_import.parent position = containing_stmt.children.index(self.sys_import) stmt_container = containing_stmt.parent new_import = pytree.Node( syms.import_name, [Name("import"), Name("atexit", " ")] ) new = pytree.Node(syms.simple_stmt, [new_import]) containing_stmt.insert_child(position + 1, Newline()) containing_stmt.insert_child(position + 2, new)
def transform_member(self, node, results): """Transform for imports of specific module elements. Replaces the module to be imported from with the appropriate new module. """ mod_member = results.get("mod_member") pref = mod_member.prefix member = results.get("member") # Simple case with only a single member being imported if member: # this may be a list of length one, or just a node if isinstance(member, list): member = member[0] new_name = None for change in MAPPING[mod_member.value]: if member.value in change[1]: new_name = change[0] break if new_name: mod_member.replace(Name(new_name, prefix=pref)) else: self.cannot_convert(node, "This is an invalid module element") # Multiple members being imported else: # a dictionary for replacements, order matters modules = [] mod_dict = {} members = results["members"] for member in members: # we only care about the actual members if member.type == syms.import_as_name: as_name = member.children[2].value member_name = member.children[0].value else: member_name = member.value as_name = None if member_name != ",": for change in MAPPING[mod_member.value]: if member_name in change[1]: if change[0] not in mod_dict: modules.append(change[0]) mod_dict.setdefault(change[0], []).append(member) new_nodes = [] indentation = find_indentation(node) first = True def handle_name(name, prefix): if name.type == syms.import_as_name: kids = [ Name(name.children[0].value, prefix=prefix), name.children[1].clone(), name.children[2].clone(), ] return [Node(syms.import_as_name, kids)] return [Name(name.value, prefix=prefix)] for module in modules: elts = mod_dict[module] names = [] for elt in elts[:-1]: names.extend(handle_name(elt, pref)) names.append(Comma()) names.extend(handle_name(elts[-1], pref)) new = FromImport(module, names) if not first or node.parent.prefix.endswith(indentation): new.prefix = indentation new_nodes.append(new) first = False if new_nodes: nodes = [] for new_node in new_nodes[:-1]: nodes.extend([new_node, Newline()]) nodes.append(new_nodes[-1]) node.replace(nodes) else: self.cannot_convert(node, "All module elements are invalid")
def make_pytest_raises_blocks(node, capture, filename): """ Turns this: try: ... pytest.fail(...) except: pass Into: with pytest.raises(Exception): ... Not only is this prettier, but the former is a bug since pytest.fail() raises an exception. """ exc_class = capture.get('exc_class', None) if exc_class: exc_class = exc_class.clone() exc_class.prefix = '' raises_args = [exc_class] else: raises_args = [kw('Exception', prefix='')] reason = capture.get('reason') if reason: assert len(reason) == 1 reason = KeywordArg(kw('message'), reason[0].clone()) raises_args = [Node(syms.arglist, raises_args + [Comma(), reason])] raises_args = [LParen()] + raises_args + [RParen()] capture['fail_stmt'].remove() try_suite = capture['try_suite'].clone() with_stmt = Node( syms.with_stmt, [ kw('with', prefix=''), Node( syms.power, [ kw('pytest'), Node(syms.trailer, [Dot(), kw('raises', prefix='')]), Node(syms.trailer, raises_args), ], ), Leaf(TOKEN.COLON, ':'), try_suite, ], prefix=node.prefix, ) # Trailing whitespace and any comments after the if statement are captured # in the prefix for the dedent node. Copy it to the following node. dedent = capture["dedent"] next_node = node.next_sibling # This extra newline avoids syntax errors in some cases (where the try # statement is at the end of another suite) # I don't really know why those occur. # Should clean this stuff up with `black` later. node.replace([with_stmt, Newline()]) next_node.prefix = dedent.prefix
def assertalmostequal_to_assert(node, capture, arguments): function_name = capture["function_name"].value invert = function_name in INVERT_FUNCTIONS function_name = SYNONYMS.get(function_name, function_name) nargs = len(arguments) if nargs < 2 or nargs > 5: return None def get_kwarg_value(index, name): idx = 0 for arg in arguments: if arg.type == syms.argument: if arg.children[0].value == name: return arg.children[2].clone() else: if idx == index: return arg.clone() idx += 1 return None first = get_kwarg_value(0, "first") second = get_kwarg_value(1, "second") if first is None or second is None: # Not sure what this is, leave it alone return places = get_kwarg_value(2, "places") msg = get_kwarg_value(3, "msg") delta = get_kwarg_value(4, "delta") if delta is not None: try: abs_delta = float(delta.value) except ValueError: # this should be a number, give up. return else: if places is None: places = 7 else: try: places = int(places.value) except (ValueError, AttributeError): # this should be an int, give up. return abs_delta = "1e-%d" % places arguments[1].prefix = "" if invert: op_token = Leaf(TOKEN.NOTEQUAL, "!=", prefix=" ") else: op_token = Leaf(TOKEN.EQEQUAL, "==", prefix=" ") assert_test_nodes = [ Node( syms.comparison, [ arguments[0], op_token, Node( syms.power, Attr(keyword("pytest"), keyword("approx", prefix="")) + [ ArgList([ arguments[1], Comma(), KeywordArg(keyword("abs"), Leaf(TOKEN.NUMBER, abs_delta)), ]) ], ), ], ) ] # Adds a 'import pytest' if there wasn't one already touch_import(None, "pytest", node) return Assert(assert_test_nodes, msg.clone() if msg else None, prefix=node.prefix)
def assertmethod_to_assert(node, capture, arguments): """ self.assertEqual(foo, bar, msg) --> assert foo == bar, msg self.assertTrue(foo, msg) --> assert foo, msg self.assertIsNotNone(foo, msg) --> assert foo is not None, msg .. etc """ function_name = capture["function_name"].value invert = function_name in INVERT_FUNCTIONS function_name = SYNONYMS.get(function_name, function_name) num_arguments = ARGUMENTS[function_name] if len(arguments) not in (num_arguments, num_arguments + 1): # Not sure what this is. Leave it alone. return None if len(arguments) == num_arguments: message = None else: message = arguments.pop() if message.type == syms.argument: # keyword argument (e.g. `msg=abc`) message = message.children[2].clone() if function_name == "assertIsInstance": arguments[0].prefix = "" assert_test_nodes = [ Call(keyword("isinstance"), [arguments[0], Comma(), arguments[1]]) ] if invert: assert_test_nodes.insert(0, keyword("not")) elif function_name == "assertAlmostEqual": arguments[1].prefix = "" # TODO: insert the `import pytest` at the top of the file if invert: op_token = Leaf(TOKEN.NOTEQUAL, "!=", prefix=" ") else: op_token = Leaf(TOKEN.EQEQUAL, "==", prefix=" ") assert_test_nodes = [ Node( syms.comparison, [ arguments[0], op_token, Node( syms.power, Attr(keyword("pytest"), keyword("approx", prefix="")) + [ ArgList([ arguments[1], Comma(), KeywordArg(keyword("abs"), Leaf(TOKEN.NUMBER, "1e-7")), ]) ], ), ], ) ] # Adds a 'import pytest' if there wasn't one already touch_import(None, "pytest", node) else: op_tokens = OPERATORS[function_name] if not isinstance(op_tokens, list): op_tokens = [op_tokens] op_tokens = [o.clone() for o in op_tokens] if invert: if not op_tokens: op_tokens.append(keyword("not")) elif op_tokens[0].type == TOKEN.NAME and op_tokens[0].value == "is": op_tokens[0] = Node( syms.comp_op, [keyword("is"), keyword("not")], prefix=" ") elif op_tokens[0].type == TOKEN.NAME and op_tokens[0].value == "in": op_tokens[0] = Node( syms.comp_op, [keyword("not"), keyword("in")], prefix=" ") elif op_tokens[0].type == TOKEN.EQEQUAL: op_tokens[0] = Leaf(TOKEN.NOTEQUAL, "!=", prefix=" ") if num_arguments == 2: # a != b, etc. assert_test_nodes = [arguments[0]] + op_tokens + [arguments[1]] elif function_name == "assertTrue": assert_test_nodes = op_tokens + [arguments[0]] # not a elif function_name == "assertIsNone": # a is not None assert_test_nodes = [arguments[0]] + op_tokens return Assert(assert_test_nodes, message.clone() if message else None, prefix=node.prefix)
def encapsulate_transform( node: LN, capture: Capture, filename: Filename ) -> None: if "attr_assignment" in capture: leaf = capture["attr_name"] leaf.replace(Name(new_name, prefix=leaf.prefix)) if make_property: # TODO: capture and use type annotation from original assignment class_node = get_class(node) suite = find_first(class_node, SYMBOL.suite) assert isinstance(suite, Node) indent_node = find_first(suite, TOKEN.INDENT) assert isinstance(indent_node, Leaf) indent = indent_node.value getter = Node( SYMBOL.decorated, [ Node( SYMBOL.decorator, [ Leaf(TOKEN.AT, "@"), Name("property"), Leaf(TOKEN.NEWLINE, "\n"), ], ), Node( SYMBOL.funcdef, [ Name("def", indent), Name(old_name, prefix=" "), Node( SYMBOL.parameters, [LParen(), Name("self"), RParen()], ), Leaf(TOKEN.COLON, ":"), Node( SYMBOL.suite, [ Newline(), Leaf(TOKEN.INDENT, indent.value + " "), Node( SYMBOL.simple_stmt, [ Node( SYMBOL.return_stmt, [ Name("return"), Node( SYMBOL.power, Attr( Name("self"), Name(new_name), ), prefix=" ", ), ], ), Newline(), ], ), Leaf(TOKEN.DEDENT, "\n" + indent), ], ), ], prefix=indent, ), ], ) setter = Node( SYMBOL.decorated, [ Node( SYMBOL.decorator, [ Leaf(TOKEN.AT, "@"), Node( SYMBOL.dotted_name, [Name(old_name), Dot(), Name("setter")], ), Leaf(TOKEN.NEWLINE, "\n"), ], ), Node( SYMBOL.funcdef, [ Name("def", indent), Name(old_name, prefix=" "), Node( SYMBOL.parameters, [ LParen(), Node( SYMBOL.typedargslist, [ Name("self"), Comma(), Name("value", prefix=" "), ], ), RParen(), ], ), Leaf(TOKEN.COLON, ":"), Node( SYMBOL.suite, [ Newline(), Leaf(TOKEN.INDENT, indent + " "), Node( SYMBOL.simple_stmt, [ Node( SYMBOL.expr_stmt, [ Node( SYMBOL.power, Attr( Name("self"), Name(new_name), ), ), Leaf( TOKEN.EQUAL, "=", prefix=" ", ), Name("value", prefix=" "), ], ), Newline(), ], ), Leaf(TOKEN.DEDENT, "\n" + indent), ], ), ], prefix=indent, ), ], ) suite.insert_child(-1, getter) suite.insert_child(-1, setter) prev = find_previous(getter, TOKEN.DEDENT, recursive=True) curr = find_last(setter, TOKEN.DEDENT, recursive=True) assert isinstance(prev, Leaf) and isinstance(curr, Leaf) prev.prefix, curr.prefix = curr.prefix, prev.prefix prev.value, curr.value = curr.value, prev.value
def transform(self, node, results): if not has_metaclass(node): return # pragma: no cover 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") # pragma: no cover touch_import(None, u'six', node) metaclass = last_metaclass.children[0].children[2].clone() metaclass.prefix = u'' arguments = [metaclass] if arglist.children: bases = arglist.clone() bases.prefix = u' ' arguments.extend([Comma(), bases]) arglist.replace( Call(Name(u'six.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 = last_metaclass.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'))
def default_transformer(node: LN, capture: Capture, filename: Filename): fp = capture.get("function_parameters") if fp and fp.children[1].type == SYMBOL.arglist: arg_node = KeywordArg(Name("trans_arg"), Number("1")) fp.children[1].append_child(Comma()) fp.children[1].append_child(arg_node)