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_comparison(self, node): # pylint: disable=invalid-name # comparison ::= expr (comp_op expr)* # comp_op ::= '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not in'|'is'|'is not' for child in node.children: self.Visit(child) if (isinstance(child, pytree.Leaf) and child.value in {'<', '>', '==', '>=', '<=', '<>', '!=', 'in', 'is'}): _AppendTokenSubtype(child, format_token.Subtype.BINARY_OPERATOR) elif pytree_utils.NodeName(child) == 'comp_op': for grandchild in child.children: _AppendTokenSubtype(grandchild, format_token.Subtype.BINARY_OPERATOR)
def HasSubtype(node): """Return True if the arg list has a named assign subtype.""" if isinstance(node, pytree.Leaf): return node_subtype in pytree_utils.GetNodeAnnotation( node, pytree_utils.Annotation.SUBTYPE, set()) for child in node.children: node_name = pytree_utils.NodeName(child) if node_name not in {'atom', 'arglist', 'power'}: if HasSubtype(child): return True return False
def Visit_dictsetmaker(self, node): # pylint: disable=invalid-name # dictsetmaker ::= (test ':' test (comp_for | # (',' test ':' test)* [','])) | # (test (comp_for | (',' test)* [','])) dict_maker = False if len(node.children) > 1: index = 0 while index < len(node.children): if node.children[index].type != token.COMMENT: break index += 1 if index < len(node.children): child = node.children[index + 1] dict_maker = isinstance(child, pytree.Leaf) and child.value == ':' last_was_comma = False last_was_colon = False for child in node.children: if pytree_utils.NodeName(child) == 'comp_for': _AppendFirstLeafTokenSubtype( child, format_token.Subtype.DICT_SET_GENERATOR) else: if dict_maker: if last_was_comma: _AppendFirstLeafTokenSubtype( child, format_token.Subtype.DICTIONARY_KEY) elif last_was_colon: if pytree_utils.NodeName(child) == 'power': _AppendSubtypeRec(child, format_token.Subtype.NONE) else: _AppendFirstLeafTokenSubtype( child, format_token.Subtype.DICTIONARY_VALUE) if style.Get('INDENT_DICTIONARY_VALUE'): _InsertPseudoParentheses(child) last_was_comma = isinstance(child, pytree.Leaf) and child.value == ',' last_was_colon = isinstance(child, pytree.Leaf) and child.value == ':' self.Visit(child)
def _FindStmtParent(node): """Find the nearest parent of node that is a statement node. Arguments: node: node to start from Returns: Nearest parent (or node itself, if suitable). """ if pytree_utils.NodeName(node) in _STATEMENT_NODES: return node else: return _FindStmtParent(node.parent)
def HasDefaultOrNamedAssignSubtype(node): """Return True if the arg list has a named assign subtype.""" if isinstance(node, pytree.Leaf): if (format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN in pytree_utils.GetNodeAnnotation(node, pytree_utils.Annotation.SUBTYPE, set())): return True return False has_subtype = False for child in node.children: if pytree_utils.NodeName(child) != 'arglist': has_subtype |= HasDefaultOrNamedAssignSubtype(child) return has_subtype
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_simple_stmt(self, node): # A 'simple_stmt' conveniently represents a non-compound Python statement, # i.e. a statement that does not contain other statements. # When compound nodes have a single statement as their suite, the parser # can leave it in the tree directly without creating a suite. But we have # to increase depth in these cases as well. However, don't increase the # depth of we have a simple_stmt that's a comment node. This represents a # standalone comment and in the case of it coming directly after the # funcdef, it is a "top" comment for the whole function. # TODO(eliben): add more relevant compound statements here. single_stmt_suite = (node.parent and pytree_utils.NodeName(node.parent) in { 'if_stmt', 'while_stmt', 'for_stmt', 'try_stmt', 'expect_clause', 'with_stmt', 'funcdef', 'classdef' }) is_comment_stmt = pytree_utils.NodeName(node.children[0]) == 'COMMENT' if single_stmt_suite and not is_comment_stmt: self._cur_depth += 1 self._StartNewLine() self.DefaultNodeVisit(node) if single_stmt_suite and not is_comment_stmt: self._cur_depth -= 1
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) == 'COLON': dict_maker = True if not comp_for and dict_maker: last_was_colon = False for child in node.children: if dict_maker: if last_was_colon: if style.Get('INDENT_DICTIONARY_VALUE'): _InsertPseudoParentheses(child) else: _AppendFirstLeafTokenSubtype( child, format_token.Subtype.DICTIONARY_VALUE) elif (child is not None and (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. _AppendFirstLeafTokenSubtype( child, format_token.Subtype.DICTIONARY_KEY) last_was_colon = pytree_utils.NodeName(child) == 'COLON'
def Visit_trailer(self, node): # pylint: disable=invalid-name # trailer ::= '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME self.DefaultNodeVisit(node) if node.children[0].value == '.': self._SetStronglyConnected(node.children[0], node.children[-1]) elif node.children[0].value == '[': self._SetStronglyConnected(node.children[-1]) elif len(node.children) == 2: # Don't split an empty argument list if at all possible. self._SetStronglyConnected(node.children[1]) elif len(node.children) == 3: if pytree_utils.NodeName(node.children[1]) == 'NAME': # Don't split an argument list with one element if at all possible. self._SetStronglyConnected(node.children[1], node.children[2])
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], UNBREAKABLE) else: _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 Visit_funcdef(self, node): # pylint: disable=invalid-name index = self._SetBlankLinesBetweenCommentAndClassFunc(node) if (py3compat.PY3 and node.prev_sibling and pytree_utils.NodeName(node.prev_sibling) == 'ASYNC'): # Move the number of blank lines to the async keyword. num_newlines = pytree_utils.GetNodeAnnotation( node.children[0], pytree_utils.Annotation.NEWLINES) self._SetNumNewlines(node.prev_sibling, num_newlines) self._SetNumNewlines(node.children[0], None) self.last_was_decorator = False self.function_level += 1 for child in node.children[index:]: self.Visit(child) self.function_level -= 1 self.last_was_class_or_function = True
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 = _GetFirstChildLeaf(node) self._SetNumNewlines(leaf, self._GetNumNewlines(leaf)) self.last_was_class_or_function = False super(_BlankLineCalculator, self).DefaultNodeVisit(node)
def Visit_typedargslist(self, node): # pylint: disable=invalid-name # typedargslist ::= # ((tfpdef ['=' test] ',')* # ('*' [tname] (',' tname ['=' test])* [',' '**' tname] # | '**' tname) # | tfpdef ['=' test] (',' tfpdef ['=' test])* [',']) self._ProcessArgLists(node) _SetArgListSubtype(node, subtypes.DEFAULT_OR_NAMED_ASSIGN, subtypes.DEFAULT_OR_NAMED_ASSIGN_ARG_LIST) tname = False if not node.children: return _AppendFirstLeafTokenSubtype(node.children[0], subtypes.PARAMETER_START) _AppendLastLeafTokenSubtype(node.children[-1], subtypes.PARAMETER_STOP) tname = pytree_utils.NodeName(node.children[0]) == 'tname' for i in range(1, len(node.children)): prev_child = node.children[i - 1] child = node.children[i] if prev_child.type == grammar_token.COMMA: _AppendFirstLeafTokenSubtype(child, subtypes.PARAMETER_START) elif child.type == grammar_token.COMMA: _AppendLastLeafTokenSubtype(prev_child, subtypes.PARAMETER_STOP) if pytree_utils.NodeName(child) == 'tname': tname = True _SetArgListSubtype(child, subtypes.TYPED_NAME, subtypes.TYPED_NAME_ARG_LIST) elif child.type == grammar_token.COMMA: tname = False elif child.type == grammar_token.EQUAL and tname: _AppendTokenSubtype(child, subtype=subtypes.TYPED_NAME) tname = False
def Visit_lambdef(self, node): # pylint: disable=invalid-name # lambdef ::= 'lambda' [varargslist] ':' test # Loop over the lambda up to and including the colon. allow_multiline_lambdas = style.Get('ALLOW_MULTILINE_LAMBDAS') if not allow_multiline_lambdas: for child in node.children: if pytree_utils.NodeName(child) == 'COMMENT': if re.search(r'pylint:.*disable=.*\bg-long-lambda', child.value): allow_multiline_lambdas = True break if allow_multiline_lambdas: _SetExpressionPenalty(node, STRONGLY_CONNECTED) else: _SetExpressionPenalty(node, VERY_STRONGLY_CONNECTED)
def Visit_lambdef(self, node): # pylint: disable=invalid-name # lambdef ::= 'lambda' [varargslist] ':' test # Loop over the lambda up to and including the colon. allow_multiline_lambdas = style.Get('ALLOW_MULTILINE_LAMBDAS') if not allow_multiline_lambdas: for child in node.children: if pytree_utils.NodeName(child) == 'COMMENT': if re.search(r'pylint:.*disable=.*\bg-long-lambda', child.value): allow_multiline_lambdas = True break if allow_multiline_lambdas: _SetStronglyConnected(node) else: self._SetUnbreakableOnChildren(node)
def Visit_trailer(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 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 Visit_funcdef(self, node): # pylint: disable=invalid-name # funcdef ::= 'def' NAME parameters ['->' test] ':' suite # # Can't break before the function name and before the colon. The parameters # are handled by child iteration. colon_idx = 1 while pytree_utils.NodeName(node.children[colon_idx]) == 'simple_stmt': colon_idx += 1 self._SetUnbreakable(node.children[colon_idx]) while colon_idx < len(node.children): if (isinstance(node.children[colon_idx], pytree.Leaf) and node.children[colon_idx].value == ':'): break colon_idx += 1 self._SetUnbreakable(node.children[colon_idx]) 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, subtypes.DICT_SET_GENERATOR) elif child.type in (grammar_token.COLON, grammar_token.DOUBLESTAR): dict_maker = True if not comp_for and dict_maker: last_was_colon = False unpacking = False for child in node.children: if child.type == grammar_token.DOUBLESTAR: _AppendFirstLeafTokenSubtype(child, subtypes.KWARGS_STAR_STAR) if last_was_colon: if style.Get('INDENT_DICTIONARY_VALUE'): _InsertPseudoParentheses(child) else: _AppendFirstLeafTokenSubtype(child, subtypes.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, subtypes.DICTIONARY_KEY) _AppendSubtypeRec(child, subtypes.DICTIONARY_KEY_PART) last_was_colon = child.type == grammar_token.COLON if child.type == grammar_token.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[0].lineno == node.children[-1].lineno: self._SetExpressionPenalty(node, CONTIGUOUS_LIST) if (pytree_utils.NodeName(node.parent) == 'if_stmt' and node.children[-1].value == ')'): pytree_utils.SetNodeAnnotation( node.children[-1], pytree_utils.Annotation.SPLIT_PENALTY, UNBREAKABLE) elif node.children[0].value in '[{': # Keep empty containers together if we can. if len(node.children) == 2: self._SetStronglyConnected(node.children[-1])
def _SetDefaultOrNamedAssignArgListSubtype(self, node): def HasDefaultOrNamedAssignSubtype(node): if isinstance(node, pytree.Leaf): if (format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN in self._GetFirstLeafTokenSubtypes(node)): return True return False has_subtype = False for child in node.children: has_subtype |= HasDefaultOrNamedAssignSubtype(child) return has_subtype if HasDefaultOrNamedAssignSubtype(node): for child in node.children: if pytree_utils.NodeName(child) != 'COMMA': self._AppendFirstLeafTokenSubtype( child, format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN_ARG_LIST)
def _FindNodeWithStandaloneLineParent(node): """Find a node whose parent is a 'standalone line' node. See the comment above _STANDALONE_LINE_NODES for more details. Arguments: node: node to start from Returns: Suitable node that's either the node itself or one of its ancestors. """ if pytree_utils.NodeName(node.parent) in _STANDALONE_LINE_NODES: return node else: # This is guaranteed to terminate because 'file_input' is the root node of # any pytree. return _FindNodeWithStandaloneLineParent(node.parent)
def _DetermineMustSplitAnnotation(node): """Enforce a split in the list if the list ends with a comma.""" if not (isinstance(node.children[-1], pytree.Leaf) and node.children[-1].value == ','): return num_children = len(node.children) index = 0 while index < num_children - 1: child = node.children[index] if isinstance(child, pytree.Leaf) and child.value == ',': next_child = node.children[index + 1] if pytree_utils.NodeName(next_child) == 'COMMENT': index += 1 if index >= num_children: break _SetMustSplitOnFirstLeaf(node.children[index + 1]) index += 1
def Visit_atom(self, node): # pylint: disable=invalid-name # atom ::= ('(' [yield_expr|testlist_gexp] ')' # '[' [listmaker] ']' | # '{' [dictsetmaker] '}') self.DefaultNodeVisit(node) if (node.children[0].value == '(' and not hasattr(node.children[0], 'is_pseudo')): 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 Visit_arglist(self, node): # pylint: disable=invalid-name # arglist ::= argument (',' argument)* [','] if node.children[0].type == grammar_token.STAR: # Python 3 treats a star expression as a specific expression type. # Process it in that method. self.Visit_star_expr(node) return self.DefaultNodeVisit(node) for index in py3compat.range(1, len(node.children)): child = node.children[index] if isinstance(child, pytree.Leaf) and child.value == ',': _SetUnbreakable(child) for child in node.children: if pytree_utils.NodeName(child) == 'atom': _IncreasePenalty(child, CONNECTED)
def Visit_trailer(self, node): # pylint: disable=invalid-name # trailer ::= '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME self.DefaultNodeVisit(node) if node.children[0].value == '.': self._SetUnbreakableOnChildren(node, num_children=len(node.children)) elif node.children[0].value == '[': for child in node.children: self._SetExpressionPenalty(node, SUBSCRIPT_LIST) self._SetUnbreakable(node.children[0]) self._SetUnbreakable(node.children[-1]) elif len(node.children) == 2: # Don't split an empty argument list if at all possible. self._SetStronglyConnected(node.children[1]) elif len(node.children) == 3: if pytree_utils.NodeName(node.children[1]) == 'NAME': # Don't split an argument list with one element if at all possible. self._SetStronglyConnected(node.children[1], node.children[2])
def _SetArgListSubtype(node, node_subtype, list_subtype): """Set named assign subtype on elements in a arg list.""" def HasSubtype(node): """Return True if the arg list has a named assign subtype.""" if isinstance(node, pytree.Leaf): if node_subtype in pytree_utils.GetNodeAnnotation( node, pytree_utils.Annotation.SUBTYPE, set()): return True return False has_subtype = False for child in node.children: if pytree_utils.NodeName(child) != 'arglist': has_subtype |= HasSubtype(child) return has_subtype if HasSubtype(node): for child in node.children: if pytree_utils.NodeName(child) != 'COMMA': _AppendFirstLeafTokenSubtype(child, list_subtype)
def Visit_dictsetmaker(self, node): # pylint: disable=invalid-name # dictsetmaker ::= (test ':' test (comp_for | # (',' test ':' test)* [','])) | # (test (comp_for | (',' test)* [','])) dict_maker = (len(node.children) > 1 and isinstance(node.children[1], pytree.Leaf) and node.children[1].value == ':') last_was_comma = False for child in node.children: self.Visit(child) if pytree_utils.NodeName(child) == 'comp_for': self._SetFirstLeafTokenSubtype( child, format_token.Subtype.DICT_SET_GENERATOR) else: if dict_maker and last_was_comma: self._SetFirstLeafTokenSubtype( child, format_token.Subtype.DICTIONARY_KEY) last_was_comma = isinstance(child, pytree.Leaf) and child.value == ','
def _DetermineMustSplitAnnotation(node): """Enforce a split in the list if the list ends with a comma.""" if not _ContainsComments(node): if sum(1 for ch in node.children if pytree_utils.NodeName(ch) == 'COMMA') < 2: return if (not isinstance(node.children[-1], pytree.Leaf) or node.children[-1].value != ','): return num_children = len(node.children) index = 0 _SetMustSplitOnFirstLeaf(node.children[0]) while index < num_children - 1: child = node.children[index] if isinstance(child, pytree.Leaf) and child.value == ',': next_child = node.children[index + 1] if next_child.type == grammar_token.COMMENT: index += 1 if index >= num_children - 1: break _SetMustSplitOnFirstLeaf(node.children[index + 1]) index += 1