def Visit_trailer(self, node): # pylint: disable=invalid-name # trailer ::= '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME if node.children[0].value == '.': before = style.Get('SPLIT_BEFORE_DOT') _SetSplitPenalty(node.children[0], STRONGLY_CONNECTED if before else DOTTED_NAME) _SetSplitPenalty(node.children[1], DOTTED_NAME if before else STRONGLY_CONNECTED) elif len(node.children) == 2: # Don't split an empty argument list if at all possible. _SetSplitPenalty(node.children[1], VERY_STRONGLY_CONNECTED) elif len(node.children) == 3: name = pytree_utils.NodeName(node.children[1]) if name in {'argument', 'comparison'}: # Don't split an argument list with one element if at all possible. _SetStronglyConnected(node.children[1]) if (len(node.children[1].children) > 1 and pytree_utils.NodeName(node.children[1].children[1]) == 'comp_for'): # Don't penalize splitting before a comp_for expression. _SetSplitPenalty(pytree_utils.FirstLeafNode(node.children[1]), 0) else: _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[1]), ONE_ELEMENT_ARGUMENT) elif (pytree_utils.NodeName(node.children[0]) == 'LSQB' and len(node.children[1].children) > 2 and (name.endswith('_test') or name.endswith('_expr'))): _SetStronglyConnected(node.children[1].children[0]) _SetStronglyConnected(node.children[1].children[2]) # Still allow splitting around the operator. split_before = ((name.endswith('_test') and style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR')) or (name.endswith('_expr') and style.Get('SPLIT_BEFORE_BITWISE_OPERATOR'))) if split_before: _SetSplitPenalty( pytree_utils.LastLeafNode(node.children[1].children[1]), 0) else: _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[1].children[2]), 0) # Don't split the ending bracket of a subscript list. _SetVeryStronglyConnected(node.children[-1]) elif name not in { 'arglist', 'argument', 'term', 'or_test', 'and_test', 'comparison', 'atom', 'power' }: # Don't split an argument list with one element if at all possible. subtypes = pytree_utils.GetNodeAnnotation( pytree_utils.FirstLeafNode(node), pytree_utils.Annotation.SUBTYPE) if subtypes and format_token.Subtype.SUBSCRIPT_BRACKET in subtypes: _IncreasePenalty(node, SUBSCRIPT) else: _SetStronglyConnected(node.children[1], node.children[2]) if name == 'arglist': _SetStronglyConnected(node.children[-1]) self.DefaultNodeVisit(node)
def _InsertPseudoParentheses(node): """Insert pseudo parentheses so that dicts can be formatted correctly.""" comment_node = None if isinstance(node, pytree.Node): if node.children[-1].type == grammar_token.COMMENT: comment_node = node.children[-1].clone() node.children[-1].remove() first = pytree_utils.FirstLeafNode(node) last = pytree_utils.LastLeafNode(node) if first == last and first.type == grammar_token.COMMENT: # A comment was inserted before the value, which is a pytree.Leaf. # Encompass the dictionary's value into an ATOM node. last = first.next_sibling last_clone = last.clone() new_node = pytree.Node(syms.atom, [first.clone(), last_clone]) for orig_leaf, clone_leaf in zip(last.leaves(), last_clone.leaves()): pytree_utils.CopyYapfAnnotations(orig_leaf, clone_leaf) if hasattr(orig_leaf, 'is_pseudo'): clone_leaf.is_pseudo = orig_leaf.is_pseudo node.replace(new_node) node = new_node last.remove() first = pytree_utils.FirstLeafNode(node) last = pytree_utils.LastLeafNode(node) lparen = pytree.Leaf(grammar_token.LPAR, u'(', context=('', (first.get_lineno(), first.column - 1))) last_lineno = last.get_lineno() if last.type == grammar_token.STRING and '\n' in last.value: last_lineno += last.value.count('\n') if last.type == grammar_token.STRING and '\n' in last.value: last_column = len(last.value.split('\n')[-1]) + 1 else: last_column = last.column + len(last.value) + 1 rparen = pytree.Leaf(grammar_token.RPAR, u')', context=('', (last_lineno, last_column))) lparen.is_pseudo = True rparen.is_pseudo = True if isinstance(node, pytree.Node): node.insert_child(0, lparen) node.append_child(rparen) if comment_node: node.append_child(comment_node) _AppendFirstLeafTokenSubtype(node, subtypes.DICTIONARY_VALUE) else: clone = node.clone() for orig_leaf, clone_leaf in zip(node.leaves(), clone.leaves()): pytree_utils.CopyYapfAnnotations(orig_leaf, clone_leaf) new_node = pytree.Node(syms.atom, [lparen, clone, rparen]) node.replace(new_node) _AppendFirstLeafTokenSubtype(clone, subtypes.DICTIONARY_VALUE)
def Visit_subscriptlist(self, node): # pylint: disable=invalid-name # subscriptlist ::= subscript (',' subscript)* [','] self.DefaultNodeVisit(node) _SetSplitPenalty(pytree_utils.FirstLeafNode(node), 0) prev_child = None for child in node.children: if prev_child and pytree_utils.NodeName(prev_child) == 'COMMA': _SetSplitPenalty(pytree_utils.FirstLeafNode(child), 0) prev_child = child
def Visit_comparison(self, node): # pylint: disable=invalid-name # comparison ::= expr (comp_op expr)* self.DefaultNodeVisit(node) if len(node.children) == 3 and _StronglyConnectedCompOp(node): _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[1]), STRONGLY_CONNECTED) _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[2]), STRONGLY_CONNECTED) else: _IncreasePenalty(node, COMPARISON)
def Visit_tname(self, node): # pylint: disable=invalid-name # tname ::= NAME [':' test] self.DefaultNodeVisit(node) for index in py3compat.range(1, len(node.children) - 1): child = node.children[index] if isinstance(child, pytree.Leaf) and child.value == ':': _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[index]), NAMED_ASSIGN) _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[index + 1]), NAMED_ASSIGN)
def Visit_argument(self, node): # pylint: disable=invalid-name # argument ::= test [comp_for] | test '=' test # Really [keyword '='] test self.DefaultNodeVisit(node) for index in py3compat.range(1, len(node.children) - 1): child = node.children[index] if isinstance(child, pytree.Leaf) and child.value == '=': _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[index]), NAMED_ASSIGN) _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[index + 1]), NAMED_ASSIGN)
def Visit_and_test(self, node): # pylint: disable=invalid-name # and_test ::= not_test ('and' not_test)* self.DefaultNodeVisit(node) _IncreasePenalty(node, AND_TEST) index = 1 while index + 1 < len(node.children): if style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR'): _DecrementSplitPenalty( pytree_utils.FirstLeafNode(node.children[index]), AND_TEST) else: _DecrementSplitPenalty( pytree_utils.FirstLeafNode(node.children[index + 1]), AND_TEST) index += 2
def _StronglyConnectedCompOp(op): if (len(op.children[1].children) == 2 and pytree_utils.NodeName(op.children[1]) == 'comp_op'): if (pytree_utils.FirstLeafNode(op.children[1]).value == 'not' and pytree_utils.LastLeafNode(op.children[1]).value == 'in'): return True if (pytree_utils.FirstLeafNode(op.children[1]).value == 'is' and pytree_utils.LastLeafNode(op.children[1]).value == 'not'): return True if (isinstance(op.children[1], pytree.Leaf) and op.children[1].value in {'==', 'in'}): return True return False
def Visit_trailer(self, node): # pylint: disable=invalid-name for child in node.children: self.Visit(child) if len(node.children) != 3: return if pytree_utils.NodeName(node.children[0]) != 'LPAR': return if pytree_utils.NodeName(node.children[1]) == 'arglist': for child in node.children[1].children: pytree_utils.SetOpeningBracket( pytree_utils.FirstLeafNode(child), node.children[0]) else: pytree_utils.SetOpeningBracket( pytree_utils.FirstLeafNode(node.children[1]), node.children[0])
def _SetBitwiseOperandPenalty(node, op): for index in py3compat.range(1, len(node.children) - 1): child = node.children[index] if isinstance(child, pytree.Leaf) and child.value == op: if style.Get('SPLIT_BEFORE_BITWISE_OPERATOR'): _SetSplitPenalty(child, style.Get('SPLIT_PENALTY_BITWISE_OPERATOR')) else: _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[index + 1]), style.Get('SPLIT_PENALTY_BITWISE_OPERATOR'))
def _SetExpressionOperandPenalty(node, ops): for index in py3compat.range(1, len(node.children) - 1): child = node.children[index] if pytree_utils.NodeName(child) in ops: if style.Get('SPLIT_BEFORE_ARITHMETIC_OPERATOR'): _SetSplitPenalty(child, style.Get('SPLIT_PENALTY_ARITHMETIC_OPERATOR')) else: _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[index + 1]), style.Get('SPLIT_PENALTY_ARITHMETIC_OPERATOR'))
def Visit_testlist_gexp(self, node): # pylint: disable=invalid-name self.DefaultNodeVisit(node) prev_was_comma = False for child in node.children: if isinstance(child, pytree.Leaf) and child.value == ',': _SetUnbreakable(child) prev_was_comma = True else: if prev_was_comma: _SetSplitPenalty(pytree_utils.FirstLeafNode(child), TOGETHER) prev_was_comma = False
def Visit_atom(self, node): # pylint: disable=invalid-name for child in node.children: self.Visit(child) if len(node.children) != 3: return if node.children[0].type != grammar_token.LPAR: return for child in node.children[1].children: pytree_utils.SetOpeningBracket(pytree_utils.FirstLeafNode(child), node.children[0])
def Visit_term(self, node): # pylint: disable=invalid-name # term ::= factor (('*'|'@'|'/'|'%'|'//') factor)* self.DefaultNodeVisit(node) _IncreasePenalty(node, TERM) index = 1 while index < len(node.children) - 1: child = node.children[index] if pytree_utils.NodeName(child) in self._TERM_OPS: next_node = pytree_utils.FirstLeafNode(node.children[index + 1]) _SetSplitPenalty(next_node, TERM) index += 1
def Visit_arith_expr(self, node): # pylint: disable=invalid-name # arith_expr ::= term (('+'|'-') term)* self.DefaultNodeVisit(node) _IncreasePenalty(node, ARITH_EXPR) index = 1 while index < len(node.children) - 1: child = node.children[index] if pytree_utils.NodeName(child) in self._ARITH_OPS: next_node = pytree_utils.FirstLeafNode(node.children[index + 1]) _SetSplitPenalty(next_node, ARITH_EXPR) index += 1
def Visit_expr(self, node): # pylint: disable=invalid-name # expr ::= xor_expr ('|' xor_expr)* self.DefaultNodeVisit(node) _IncreasePenalty(node, EXPR) index = 1 while index < len(node.children) - 1: child = node.children[index] if isinstance(child, pytree.Leaf) and child.value == '|': if style.Get('SPLIT_BEFORE_BITWISE_OPERATOR'): _SetSplitPenalty(child, style.Get('SPLIT_PENALTY_BITWISE_OPERATOR')) else: _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[index + 1]), style.Get('SPLIT_PENALTY_BITWISE_OPERATOR')) index += 1
def DefaultNodeVisit(self, node): """Override the default visitor for Node. This will set the blank lines required if the last entity was a class or function. Arguments: node: (pytree.Node) The node to visit. """ if self.last_was_class_or_function: if pytree_utils.NodeName(node) in _PYTHON_STATEMENTS: leaf = pytree_utils.FirstLeafNode(node) self._SetNumNewlines(leaf, self._GetNumNewlines(leaf)) self.last_was_class_or_function = False super(_BlankLineCalculator, self).DefaultNodeVisit(node)
def Visit_dictsetmaker(self, node): # pylint: disable=invalid-name # dictsetmaker ::= (test ':' test (comp_for | # (',' test ':' test)* [','])) | # (test (comp_for | (',' test)* [','])) for child in node.children: self.Visit(child) comp_for = False dict_maker = False for child in node.children: if pytree_utils.NodeName(child) == 'comp_for': comp_for = True _AppendFirstLeafTokenSubtype( child, format_token.Subtype.DICT_SET_GENERATOR) elif pytree_utils.NodeName(child) in ('COLON', 'DOUBLESTAR'): dict_maker = True if not comp_for and dict_maker: last_was_colon = False unpacking = False for child in node.children: if pytree_utils.NodeName(child) == 'DOUBLESTAR': _AppendFirstLeafTokenSubtype( child, format_token.Subtype.KWARGS_STAR_STAR) if last_was_colon: if style.Get('INDENT_DICTIONARY_VALUE'): _InsertPseudoParentheses(child) else: _AppendFirstLeafTokenSubtype( child, format_token.Subtype.DICTIONARY_VALUE) elif (isinstance(child, pytree.Node) or (not child.value.startswith('#') and child.value not in '{:,')): # Mark the first leaf of a key entry as a DICTIONARY_KEY. We # normally want to split before them if the dictionary cannot exist # on a single line. if not unpacking or pytree_utils.FirstLeafNode( child).value == '**': _AppendFirstLeafTokenSubtype( child, format_token.Subtype.DICTIONARY_KEY) _AppendSubtypeRec(child, format_token.Subtype.DICTIONARY_KEY_PART) last_was_colon = pytree_utils.NodeName(child) == 'COLON' if pytree_utils.NodeName(child) == 'DOUBLESTAR': unpacking = True elif last_was_colon: unpacking = False
def Visit_atom(self, node): # pylint: disable=invalid-name # atom ::= ('(' [yield_expr|testlist_gexp] ')' # '[' [listmaker] ']' | # '{' [dictsetmaker] '}') self.DefaultNodeVisit(node) if node.children[0].value == '(': if node.children[-1].value == ')': if pytree_utils.NodeName(node.parent) == 'if_stmt': _SetSplitPenalty(node.children[-1], STRONGLY_CONNECTED) else: if len(node.children) > 2: _SetSplitPenalty(pytree_utils.FirstLeafNode(node.children[1]), EXPR) _SetSplitPenalty(node.children[-1], ATOM) elif node.children[0].value in '[{' and len(node.children) == 2: # Keep empty containers together if we can. _SetUnbreakable(node.children[-1])
def _IncreasePenalty(node, amt): """Increase a penalty annotation on children nodes.""" def RecExpression(node, first_child_leaf): if node is first_child_leaf: return if isinstance(node, pytree.Leaf): if node.value in {'(', 'for'}: return penalty = pytree_utils.GetNodeAnnotation( node, pytree_utils.Annotation.SPLIT_PENALTY, default=0) _SetSplitPenalty(node, penalty + amt) else: for child in node.children: RecExpression(child, first_child_leaf) RecExpression(node, pytree_utils.FirstLeafNode(node))
def _SetExpressionPenalty(node, penalty): """Set a penalty annotation on children nodes.""" def RecExpression(node, first_child_leaf): if node is first_child_leaf: return if isinstance(node, pytree.Leaf): if node.value in {'(', 'for', 'if'}: return penalty_annotation = pytree_utils.GetNodeAnnotation( node, pytree_utils.Annotation.SPLIT_PENALTY, default=0) if penalty_annotation < penalty: _SetSplitPenalty(node, penalty) else: for child in node.children: RecExpression(child, first_child_leaf) RecExpression(node, pytree_utils.FirstLeafNode(node))
def _StartsInZerothColumn(node): return (pytree_utils.FirstLeafNode(node).column == 0 or (_AsyncFunction(node) and node.prev_sibling.column == 0))
def first_leaf_is_comment(node): first_leaf = pytree_utils.FirstLeafNode(node) return pytree_utils.NodeName(first_leaf) == 'COMMENT'
def _SetMustSplitOnFirstLeaf(node): """Set the "must split" annotation on the first leaf node.""" pytree_utils.SetNodeAnnotation(pytree_utils.FirstLeafNode(node), pytree_utils.Annotation.MUST_SPLIT, True)
def Visit_comp_for(self, node): # pylint: disable=invalid-name # comp_for ::= 'for' exprlist 'in' testlist_safe [comp_iter] _SetSplitPenalty(pytree_utils.FirstLeafNode(node), 0) _SetStronglyConnected(*node.children[1:]) self.DefaultNodeVisit(node)
def Visit_power(self, node): # pylint: disable=invalid-name,missing-docstring # power ::= atom trailer* ['**' factor] self.DefaultNodeVisit(node) # When atom is followed by a trailer, we can not break between them. # E.g. arr[idx] - no break allowed between 'arr' and '['. if (len(node.children) > 1 and pytree_utils.NodeName(node.children[1]) == 'trailer'): # children[1] itself is a whole trailer: we don't want to # mark all of it as unbreakable, only its first token: (, [ or . first = pytree_utils.FirstLeafNode(node.children[1]) if first.value != '.': _SetUnbreakable(node.children[1].children[0]) # A special case when there are more trailers in the sequence. Given: # atom tr1 tr2 # The last token of tr1 and the first token of tr2 comprise an unbreakable # region. For example: foo.bar.baz(1) # We can't put breaks between either of the '.', '(', or '[' and the names # *preceding* them. prev_trailer_idx = 1 while prev_trailer_idx < len(node.children) - 1: cur_trailer_idx = prev_trailer_idx + 1 cur_trailer = node.children[cur_trailer_idx] if pytree_utils.NodeName(cur_trailer) == 'trailer': # Now we know we have two trailers one after the other prev_trailer = node.children[prev_trailer_idx] if prev_trailer.children[-1].value != ')': # Set the previous node unbreakable if it's not a function call: # atom tr1() tr2 # It may be necessary (though undesirable) to split up a previous # function call's parentheses to the next line. _SetStronglyConnected(prev_trailer.children[-1]) _SetStronglyConnected(cur_trailer.children[0]) prev_trailer_idx = cur_trailer_idx else: break # We don't want to split before the last ')' of a function call. This also # takes care of the special case of: # atom tr1 tr2 ... trn # where the 'tr#' are trailers that may end in a ')'. for trailer in node.children[1:]: if pytree_utils.NodeName(trailer) != 'trailer': break if trailer.children[0].value in '([': if len(trailer.children) > 2: subtypes = pytree_utils.GetNodeAnnotation( trailer.children[0], pytree_utils.Annotation.SUBTYPE) if subtypes and format_token.Subtype.SUBSCRIPT_BRACKET in subtypes: _SetStronglyConnected( pytree_utils.FirstLeafNode(trailer.children[1])) last_child_node = pytree_utils.LastLeafNode(trailer) if last_child_node.value.strip().startswith('#'): last_child_node = last_child_node.prev_sibling if not (style.Get('INDENT_CLOSING_BRACKETS') or style.Get('DEDENT_CLOSING_BRACKETS')): last = pytree_utils.LastLeafNode(last_child_node.prev_sibling) if last.value != ',': if last_child_node.value == ']': _SetUnbreakable(last_child_node) else: _SetSplitPenalty(last_child_node, VERY_STRONGLY_CONNECTED) else: # If the trailer's children are '()', then make it a strongly # connected region. It's sometimes necessary, though undesirable, to # split the two. _SetStronglyConnected(trailer.children[-1])