def lazy_aliasing() -> None: # We should find the first place where the alias is used and put it # right above. This way we don't need to look at the value at all. _, prefix = get_offset_and_prefix(body, skip_assignments=True) name_node = Leaf(token.NAME, name) for _offset, stmt in enumerate(body.children): if name_used_in_node(stmt, name_node): break else: _offset = -1 body.children.insert( _offset, Node( syms.simple_stmt, [ Node( syms.expr_stmt, [ Leaf(token.NAME, name), new(_eq), value, ], ), new(_newline), ], prefix=prefix.lstrip('\n'), ), )
def add_sep_part(sep, pos, lst): if sep is not None and not isNone(sep) and \ not (sep.type == token.STRING and sep.value in ("' '", '" "')): temp = [] for arg in pos: temp.append(_unicode(arg.clone())) if sys.version_info >= (2, 6): warnings.warn( "Calling unicode() on what may be a bytes object") temp.append(Comma()) del temp[-1] sep = sep.clone() sep.prefix = " " args = Node(syms.listmaker, temp) new_list = Node(syms.atom, [Leaf(token.LSQB, "["), args, Leaf(token.RSQB, "]")]) join_arg = Node(syms.trailer, [LParen(), new_list, RParen()]) sep_join = Node(syms.power, [sep, Node(syms.trailer, [Dot(), Name("join")])]) lst.append(sep_join) lst.append(join_arg) else: if pos: pos[0].prefix = " " for arg in pos: lst.append(arg.clone()) lst.append(Comma()) del lst[-1]
def transform(self, node, results): args = [] for i in range(len(node.children[0].children)): if i%2==1: if i!=1: args.append(Leaf(1, '&&')) args.append(Leaf(7, '(')) args.append(node.children[0].children[i].clone()) args.append(Leaf(8, ')')) args = [\ Leaf(1, 'if'),Leaf(7, '('),\ Leaf(1, '!'),Leaf(7, '('),\ Node(syms.and_expr,args),\ Leaf(8, ')'),Leaf(8, ')'),\ Leaf(token.LBRACE, '{'),\ Node(syms.raise_stmt, [ Leaf(1, 'throw'), Leaf(3, '"AssertionError"'), Leaf(1, ';'), ]), Leaf(token.RBRACE, '}'),Leaf(4, '\r\n') ] result = Node(syms.testlist1,args) result.prefix = node.prefix return result
def _nai_alias(alias, node): # Comments below show example imports that match the rule. name = Leaf(token.NAME, alias.name) if not alias.asname or alias.asname == alias.name: # import hay, x, stack # from field import hay, s, stack if node.type in (syms.dotted_as_names, syms.import_as_names): return name in node.children # import x as x # from field import x as x if node.type in (syms.dotted_as_name, syms.import_as_name): return [name, _as, name] == node.children # import x return node == name asname = Leaf(token.NAME, alias.asname) dotted_as_name = Node(syms.dotted_as_name, [name, _as, asname]) # import hay as stack, x as y if node.type == syms.dotted_as_names: return dotted_as_name in node.children import_as_name = Node(syms.import_as_name, [name, _as, asname]) # from field import hay as stack, x as y if node.type == syms.import_as_names: return import_as_name in node.children # import x as y # from field import x as y return node in (dotted_as_name, import_as_name)
def parse(code): """String -> AST Parse the string and return its AST representation. May raise a ParseError exception. """ added_newline = False if not code.endswith("\n"): code += "\n" added_newline = True try: drv = driver.Driver(pygram.python_grammar, pytree.convert) result = drv.parse_string(code, True) except ParseError: log.debug("Had problems parsing:\n%s\n" % quoted_block(code)) raise # Always return a Node, not a Leaf. if isinstance(result, Leaf): result = Node(syms.file_input, [result]) result.added_newline = added_newline return result
def get_dotted_import_replacement(self, name_node, attr_node, mapping=MAPPING, renamed=None): u""" For (http, client) given and httplib being the correct replacement, returns (httplib as client, None) For (test, support) given and test.test_support being the replacement, returns (test, test_support as support) """ full_name = name_node.value + u'.' + attr_node.value replacement = mapping[full_name] if u'.' in replacement: new_name, new_attr = replacement.split(u'.') if renamed is None: return Name(new_name, prefix=name_node.prefix), Node( syms.dotted_as_name, [ Name(new_attr, prefix=attr_node.prefix), Name(u'as', prefix=u" "), attr_node.clone() ]) else: return Name(new_name, prefix=name_node.prefix), Name( new_attr, prefix=attr_node.prefix) else: return Node(syms.dotted_as_name, [ Name(replacement, prefix=name_node.prefix), Name(u'as', prefix=u' '), Name(attr_node.value, prefix=attr_node.prefix) ]), None
def transform(self, node, results): args = [] item_id = 0 while item_id < len(node.children): if (node.children[item_id].__class__.__name__ == "Leaf"): if node.children[item_id].value == ':': #加花括号 args += [ Leaf(token.LBRACE, '{'), node.children[item_id + 1].clone(), Leaf(token.RBRACE, '}'), Leaf(4, '\r\n') ] item_id += 1 #if,elif处理 elif (node.children[item_id].value == 'elif') | (node.children[item_id].value == 'if'): if node.children[item_id].value == 'elif': leaf_temp = Leaf(1, 'if') leaf_temp.prefix = " " args += [Leaf(1, 'else'), leaf_temp] else: args += [node.children[item_id].clone()] args += [Leaf(7, '(')] args += [node.children[item_id + 1].clone()] args += [Leaf(8, ')')] item_id += 1 else: args += [node.children[item_id].clone()] else: print("\nFixerError_if\n") item_id += 1 new = Node(node.type, args) new.prefix = node.prefix return new
def suitify(parent): """ Turn the stuff after the first colon in parent's children into a suite, if it wasn't already """ for node in parent.children: if node.type == syms.suite: # already in the prefered format, do nothing return # One-liners have no suite node, we have to fake one up for i, node in enumerate(parent.children): if node.type == token.COLON: break else: raise ValueError(u"No class suite and no ':'!") # Move everything into a suite node suite = Node(syms.suite, [ Newline(), Leaf(token.INDENT, indentation(node) + indentation_step(node)) ]) one_node = parent.children[i + 1] one_node.remove() one_node.prefix = u'' suite.append_child(one_node) parent.append_child(suite)
def ImportAsName(name, as_name, prefix=None): new_name = Name(name) new_as = Name(u"as", prefix=u" ") new_as_name = Name(as_name, prefix=u" ") new_node = Node(syms.import_as_name, [new_name, new_as, new_as_name]) if prefix is not None: new_node.prefix = prefix return new_node
def _c_subscript(sub): return Node( syms.power, [ convert_annotation(sub.value), Node(syms.trailer, [new(_lsqb), convert_annotation(sub.slice), new(_rsqb)]), ], )
def _CreateCode(indent, package, symbols, comment): """ Create code: from <package> import <symbols> # <comment> """ # children: the children nodes for the final from-import statement children = [ Name('from', prefix=' ' * indent), Name(package, prefix=' '), Name('import', prefix=' '), ] # name_leaf: list of leaf nodes with the symbols to import name_leafs = [] symbols = sorted(symbols) for i, i_symbol in enumerate(symbols): prefix = ' ' if i == 0 else ', ' name_leafs.append(i_symbol.CreateNameNode(prefix)) # nodes_wrap: if true, we need to wrap the import statement nodes_wrap = False line_len = 0 line_len += six.moves.reduce(lambda x, y: x + y, map(len, map(str, children)), 0) line_len += six.moves.reduce(lambda x, y: x + y, map(len, map(str, name_leafs)), 0) if line_len > page_width: # Add parenthesis around the "from" names name_leafs[0].prefix = '' name_leafs.insert(0, Name('(', prefix=' ')) name_leafs.append(Name(')')) nodes_wrap = True # Adds the name_leafs to the children list children += [ Node(pygram.python_symbols.import_as_names, name_leafs) ] # from_import: the final node for the import statement from_import = Node(pygram.python_symbols.import_from, children) # result: a simple-statement node with the import statement and # EOL. new_line = Newline() new_line.prefix = comment result = Node( pygram.python_symbols.simple_stmt, children=[ from_import, new_line, ], ) # Wrap nodes if necessary (see nodes_wrap above) if nodes_wrap: ImportBlock.TextWrapForNode(result, page_width, indent) return result
def touch_import_top(package, name_to_import, node): """Works like `does_tree_import` but adds an import statement at the top if it was not imported (but below any __future__ imports). Calling this multiple times adds them in reverse order. Based on lib2to3.fixer_util.touch_import() """ root = find_root(node) if does_tree_import(package, name_to_import, root): return # Look for __future__ imports and insert below them found = False for name in [ 'absolute_import', 'division', 'print_function', 'unicode_literals' ]: if does_tree_import('__future__', name, root): found = True break if found: # At least one __future__ import. We want to loop until we've seen them # all. start, end = None, None for idx, node in enumerate(root.children): if check_future_import(node): start = idx # Start looping idx2 = start while node: node = node.next_sibling idx2 += 1 if not check_future_import(node): end = idx2 break break assert start is not None assert end is not None insert_pos = end else: # No __future__ imports for idx, node in enumerate(root.children): if node.type == syms.simple_stmt: # and node.children and node.children[0].type == token.STRING): break insert_pos = idx if package is None: import_ = Node(syms.import_name, [ Leaf(token.NAME, u"import"), Leaf(token.NAME, name_to_import, prefix=u" ") ]) else: import_ = FromImport(package, [Leaf(token.NAME, name_to_import, prefix=u" ")]) children = [import_, Newline()] root.insert_child(insert_pos, Node(syms.simple_stmt, children))
def transform(self, node, results): args = [Leaf(1, 'while')] args += [Leaf(7, '(')] args += [n.clone() for n in results["test_content"]] args += [Leaf(8, ')'),Leaf(token.LBRACE, '{')] args += [n.clone() for n in results["content"]] new = Node(node.type,args) new.prefix = node.prefix return new
def _c_attribute(attr): return Node( syms.power, [ _convert_annotation(attr.value), Node(syms.trailer, [new(_dot), Leaf(token.NAME, attr.attr)]), ], )
def form_eqs(node1,node2): result = Node(syms.simple_stmt,[\ Node(syms.expr_stmt,[\ node1.clone(),\ Leaf(22, '='),\ node2.clone()]),\ Leaf(4, '\r\n')\ ]) result.changed() return result
def _c_list(l): contents = [_convert_annotation(elt) for elt in l.elts] for index in range(len(contents) - 1, 0, -1): contents[index].prefix = " " contents.insert(index, new(_comma)) list_literal = [new(_lsqb), new(_rsqb)] if contents: list_literal.insert(1, Node(syms.listmaker, contents)) return Node(syms.atom, list_literal)
def transform_arglist_to_keywords(self, arglist_node, alg_object): """Takes a node that points to argument list and transforms it to all keyword=values @param arglist_node The node that points to the argument list @param alg_object The algorithm object that corresponds to this list """ ordered_props = alg_object.orderedProperties() # Special case where the arglist has no children if len(arglist_node.children) == 0: arglist_node = Node(syms.argument, [Leaf(token.NAME,ordered_props[0]),Leaf(token.EQUAL,"="), Leaf(arglist_node.type,arglist_node.value)]) return arglist_node # Quick check: A 3 arg leaf list with an equals at the 2nd element needs nothing doing if len(arglist_node.children) == 3 and arglist_node.children[1].type == token.EQUAL: return arglist_node # Construct our argument list from the children to make sure we separate out whole comma-separated # sections i.e get embedded lists correct args = [[]] # Each list will be delimited by a comma nargs = 0 index = 0 for node in arglist_node.children: if node.type == token.COMMA: args.append(node) args.append([]) # new arg list index += 2 # include comma nargs += 1 else: args[index].append(node) # Ordered props prop_index = 0 # List has commas so standard enumerate won't do the trick arg_nodes = [] # Holds the final node list for arg_list in args: if isinstance(arg_list, Leaf): # Must be comma from construction above arg_nodes.append(arg_list) else: first = arg_list[0] if not (isinstance(first, Node) and first.type == syms.argument): prop_name = ordered_props[prop_index] children=[Leaf(token.NAME,prop_name),Leaf(token.EQUAL,"=")] children.extend(arg_list) for c in children: c.parent = None # A new node requires all old parent nodes to be None arg_nodes.append(Node(syms.argument, children)) else: for node in arg_list: arg_nodes.append(node) # Move to the next property prop_index += 1 arglist_node.children = arg_nodes return arglist_node
def transform(self, node, results): args = [Leaf(1, 'for')] args += [Leaf(7, '(')] args += [Leaf(1, 'auto')] #c++11 args += [n.clone() for n in results["in_before"]] args += [Leaf(token.COLON, ':')] args += [n.clone() for n in results["in_after"]] args += [Leaf(8, ')'), Leaf(token.LBRACE, '{')] args += [n.clone() for n in results["content"]] new = Node(node.type, args) new.prefix = node.prefix return new
def _c_call(call): contents = [convert_annotation(arg) for arg in call.args] contents.extend(convert_annotation(kwarg) for kwarg in call.keywords) for index in range(len(contents) - 1, 0, -1): contents[index].prefix = " " contents.insert(index, new(_comma)) call_args = [new(_lpar), new(_rpar)] if contents: call_args.insert(1, Node(syms.arglist, contents)) return Node(syms.power, [convert_annotation(call.func), Node(syms.trailer, call_args)])
def fix_main(node): main_list = [] del_list =[] if(len(node.children)==0): return node # 处理文件头多行注释 temp = False if (node.children[0].type==syms.simple_stmt) \ and (node.children[0].children[0].type==3): temp = node.children[0].clone() node.children[0].remove() for i in node.children: if(i.type==syms.funcdef)|(i.type==syms.async_funcdef)|(i.type==syms.classdef)|(i.type==syms.decorated): continue if len(i.children)!=0: if(i.children[0].type==syms.import_name)|(i.children[0].type==syms.import_from): continue main_list.append(i.clone()) del_list.append(i) for i in del_list: i.remove() if temp: node.insert_child(0,temp) args = [ Leaf(1, 'def'), Leaf(1, 'main',prefix=' '), Node(syms.parameters, [ Leaf(7, '('), Leaf(8, ')'), ]), Leaf(11, ':'), Leaf(4, '\r\n') ] args+=main_list args+=[Node(syms.suite, [ Leaf(5, '\t'), Node(syms.simple_stmt, [ Node(syms.return_stmt, [ Leaf(1, 'return '), Leaf(2, '0'), ]), Leaf(4, '\r\n'), ]), Leaf(6, '') ])] main_node = Node(syms.funcdef,args) node.append_child(main_node)
def transform(self, node, results): args = [] if results['first']: args.append(Node(syms.simple_stmt,[\ Leaf(1, 'TODO_PyObject'), results['name'].clone(), Leaf(1, ';'), Leaf(4, '\r\n'),])) if results['out'][0].type==syms.trailer: args.append(deal_cout(results['out'][0].children[1])) args.append(deal_cin(results['name'])) result = Node(syms.testlist1,args) result.prefix = node.prefix return result
def deal_cout(node): return Node(syms.simple_stmt,[\ Node(syms.power, [ Leaf(1, 'std'), Node(syms.trailer, [ Leaf(23, ':'), Leaf(23, ':'), Leaf(1, 'cout'), ]), ]), Leaf(34, '<<'), node.clone(), Leaf(1, ';'), Leaf(4, '\r\n'),])
def future_import2(feature, node): """ An alternative to future_import() which might not work ... """ root = find_root(node) if does_tree_import(u"__future__", feature, node): return insert_pos = 0 for idx, node in enumerate(root.children): if node.type == syms.simple_stmt and node.children and \ node.children[0].type == token.STRING: insert_pos = idx + 1 break for thing_after in root.children[insert_pos:]: if thing_after.type == token.NEWLINE: insert_pos += 1 continue prefix = thing_after.prefix thing_after.prefix = u"" break else: prefix = u"" import_ = FromImport(u"__future__", [Leaf(token.NAME, feature, prefix=u" ")]) children = [import_, Newline()] root.insert_child(insert_pos, Node(syms.simple_stmt, children, prefix=prefix))
def append_credentials(src, access_key, secret_key, target='CREDENTIALS', comment='Added by cornac'): if isinstance(src, str): if not src.endswith('\n'): src += '\n' src = parse_py(src) for varname, atom in iter_assign(src): if varname != target: continue if len(atom.children) == 2: # Atom is an empty dict. dictmaker = Node(syms.dictsetmaker, []) atom.children.insert(1, dictmaker) if atom.children[1].type != syms.dictsetmaker: raise ValueError(f"{target} is not a plain dict.") append_to_dict(atom, key=access_key, value=secret_key, comment=comment) break else: raise ValueError(f"{target} is not set.") return src
def _replace_node(node0: Node, node1: Node) -> None: suite0 = node0.children[-1] assert node_name(suite0) == "suite" for i, x in enumerate(suite0.children): if node_name( x) == "simple_stmt" and x.children[0].type == token.STRING: break has_comment0 = i < (len(suite0.children) - 1) suite1 = node1.children[-1] assert node_name(suite1) == "suite" before_comments1 = [] for j, x in enumerate(suite1.children): before_comments1.append(x) if node_name( x) == "simple_stmt" and x.children[0].type == token.STRING: break has_comment1 = j < (len(suite1.children) - 1) if has_comment1: if has_comment0: suite0.children[i] = suite1.children[j] else: assert suite0.children[0].type == token.NEWLINE assert suite0.children[1].type == token.INDENT suite0.children = before_comments1 + suite0.children[1:] suite0.parent.changed() node0.children = [*node1.children[:-1], node0.children[-1]] node0.parent.changed()
def add_future(node, symbol): root = fixer_util.find_root(node) for idx, node in enumerate(root.children): if node.type == syms.simple_stmt and \ len(node.children) > 0 and node.children[0].type == token.STRING: # skip over docstring continue names = check_future_import(node) if not names: # not a future statement; need to insert before this break if symbol in names: # already imported return import_ = fixer_util.FromImport('__future__', [Leaf(token.NAME, symbol, prefix=" ")]) # Place after any comments or whitespace. (copyright, shebang etc.) import_.prefix = node.prefix node.prefix = '' children = [import_, fixer_util.Newline()] root.insert_child(idx, Node(syms.simple_stmt, children))
def get_annotated_param(node, arg, *, missing_ok=False): if node.type not in (token.NAME, syms.tname): raise NotImplementedError(f"unexpected node token: `{node}`") actual_ann = None if node.type == syms.tname: actual_ann = node.children[2] node = node.children[0] if not isinstance(node, Leaf) or arg.arg != node.value: raise ValueError( f".pyi file expects argument {arg.arg!r} next but argument " + f"{minimize_whitespace(str(node))!r} found in source") if arg.annotation is None: if actual_ann is None: if missing_ok: return new(node) raise ValueError( f".pyi file is missing annotation for {arg.arg!r} and source " + f"doesn't provide it either") ann = new(actual_ann) else: ann = convert_annotation(arg.annotation) ann.prefix = ' ' if actual_ann is not None and actual_ann != ann: ann_str = minimize_whitespace(str(ann)) actual_ann_str = minimize_whitespace(str(actual_ann)) raise ValueError( f"incompatible annotation for {arg.arg!r}. Expected: " + f"{ann_str!r}, actual: {actual_ann_str!r}") return Node(syms.tname, [new(node), new(_colon), ann])
def transform(self, node, results): u""" Call __builtins__.long() with the value and the base of the value. This works because 0b10 is int("10", 2), 0o10 is int("10", 8), etc. """ val = node.value base_ = base(val) if base_ == 8: assert val.strip().startswith(u"0o") or \ val.strip().startswith( u"0O"), u"Invalid format for octal literal" node.changed() node.value = u"".join((u"0", val[2:])) elif base_ == 2: assert val.startswith(u"0") and val[1] in u"bB", \ u"Invalid format for binary literal" # __builtins__.long func_name = Node(syms.power, Attr(Name(u"__builtins__"), Name(u"long"))) # ("...", 2) func_args = [ String(u"".join((u"\"", val.strip()[2:], u"\""))), Comma(), Number(2, prefix=u" ") ] new_node = Call(func_name, func_args, node.prefix) return new_node
def fix_not_a_in_b(node: LN, capture: Capture, filename: Filename): capture["element"].parent = None capture["collection"].parent = None new_comparison = Node( comparison, [ capture["element"], Node(comp_op, [Leaf(1, "not", prefix=" "), Leaf(1, "in", prefix=" ")]), capture["collection"], ], ) new_comparison.parent = node.parent return new_comparison
def future_import(feature, node): """ This seems to work """ root = find_root(node) if does_tree_import(u"__future__", feature, node): return for idx, node in enumerate(root.children): if node.type == syms.simple_stmt and \ len(node.children) > 0 and node.children[0].type == token.STRING: # skip over docstring continue names = check_future_import(node) if not names: # not a future statement; need to insert before this break if feature in names: # already imported return import_ = FromImport(u'__future__', [Leaf(token.NAME, feature, prefix=" ")]) children = [import_, Newline()] root.insert_child(idx, Node(syms.simple_stmt, children))
def transform(self, node, results): comp_for = results.get("comp_for").clone() is_dict = bool(results.get("col")) # is it a dict? n1 = results.get("n1").clone() if is_dict: n2 = results.get("n2").clone() n2.prefix = " " impl_assign = tup((n1, n2)) else: impl_assign = n1 our_gencomp = Node(syms.listmaker, [(impl_assign), (comp_for)]) if is_dict: new_node = Node(syms.power, [Name("dict"), parenthesize(Node(syms.atom, [our_gencomp]))]) else: new_node = Node(syms.power, [Name("set"), parenthesize(Node(syms.atom, [our_gencomp]))]) new_node.prefix = node.prefix return new_node
def insert_annotation(self, arg_type): """Modifies tree to set string arg_type as our type annotation""" # maybe the right way to do this is to insert as next child # in our parent instead? Or could replace self._arg[-1] # with modified version of itself assert self.arg_type is None, 'already annotated' assert not self._wasModified, 'can only set annotation once' self._wasModified = True name = self._name_nodes[-1] assert name.type == token.NAME typed_name = Node(syms.tname, [Leaf(token.NAME, self.name), Leaf(token.COLON, ':'), clean_clone(arg_type, False)]) typed_name.prefix = name.prefix name.replace(typed_name)
def suitify(parent): u""" Turn the stuff after the first colon in parent's children into a suite, if it wasn't already """ for node in parent.children: if node.type == syms.suite: # already in the prefered format, do nothing return # One-liners have no suite node, we have to fake one up for i, node in enumerate(parent.children): if node.type == token.COLON: break else: raise ValueError(u"No class suite and no ':'!") # Move everything into a suite node suite = Node(syms.suite, [Newline(), Leaf(token.INDENT, indentation(node) + indentation_step(node))]) one_node = parent.children[i+1] one_node.remove() one_node.prefix = u'' suite.append_child(one_node) parent.append_child(suite)
def wrap_parens(arg_node: PyNode, checker_fn: callable) -> PyNode or PyLeaf: """ If a node that represents an argument to assert_ function should be grouped, return a new node that adds parentheses around arg_node. Otherwise, return arg_node. :param arg_node: the arg_node to parenthesize :return: the arg_node for the parenthesized expression, or the arg_node itself """ if isinstance(arg_node, PyNode) and checker_fn(arg_node): # log.info('adding parens: "{}" ({}), "{}" ({})'.format(first_child, first_child.type, sibling, sibling.type)) # sometimes arg_node has parent, need to remove it before giving to parenthesize() then re-insert: parent = arg_node.parent if parent is not None: pos_parent = arg_node.remove() new_node = parenthesize(arg_node) parent.insert_child(pos_parent, new_node) else: new_node = parenthesize(arg_node) new_node.prefix = arg_node.prefix arg_node.prefix = '' return new_node return arg_node
def fix_leaves(self, node_to_split): parent_depth = find_indentation(node_to_split) new_indent = "%s%s" % (' ' * 4, parent_depth) # For now, just indent additional lines by 4 more spaces child_leaves = [] combined_prefix = "" prev_leaf = None for index, leaf in enumerate(node_to_split.leaves()): if index and leaf.prefix.count('#'): if not combined_prefix: combined_prefix = "%s#" % new_indent combined_prefix += leaf.prefix.split('#')[-1] # We want to strip all newlines so we can properly insert newlines # where they should be if leaf.type != token.NEWLINE: if leaf.prefix.count('\n') and index: # If the line contains a newline, we need to strip all # whitespace since there were leading indent spaces if (prev_leaf and prev_leaf.type in [token.DOT, token.LPAR] or leaf.type in [token.RPAR]): leaf.prefix = "" else: leaf.prefix = " " # Append any trailing inline comments to the combined # prefix child_leaves.append(leaf) prev_leaf = leaf # Like TextWrapper, but for nodes. We split on MAX_CHARS - 1 since we # may need to insert a leading parenth. It's not great, but it would be # hard to do properly. split_leaves = wrap_leaves(child_leaves, width=MAX_CHARS - 1, subsequent_indent=new_indent) new_node = Node(node_to_split.type, []) # We want to keep track of if we are breaking inside a parenth open_count = 0 need_parens = False for line_index, curr_line_nodes in enumerate(split_leaves): for node_index, curr_line_node in enumerate(curr_line_nodes): if line_index and not node_index: # If first node in non-first line, reset prefix since there # may have been spaces previously curr_line_node.prefix = new_indent new_node.append_child(curr_line_node) if curr_line_node.type in OPENING_TOKENS: open_count += 1 if curr_line_node.type in CLOSING_TOKENS: open_count -= 1 if line_index != len(split_leaves) - 1: # Don't add newline at the end since it it part of the next # sibling new_node.append_child(Leaf(token.NEWLINE, '\n')) # Checks if we ended a line without being surrounded by parens if open_count <= 0: need_parens = True if need_parens: # Parenthesize the parent if we're not inside parenths, braces, # brackets, since we inserted newlines between leaves parenth_before_equals = Leaf(token.EQUAL, "=") in split_leaves[0] self.parenthesize_parent(new_node, parenth_before_equals) node_to_split.replace(new_node) return combined_prefix