• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 Google Inc. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Computation of split penalties before/between tokens."""
15
16import re
17
18from lib2to3 import pytree
19
20from yapf.yapflib import format_token
21from yapf.yapflib import py3compat
22from yapf.yapflib import pytree_utils
23from yapf.yapflib import pytree_visitor
24from yapf.yapflib import style
25
26# TODO(morbo): Document the annotations in a centralized place. E.g., the
27# README file.
28UNBREAKABLE = 1000 * 1000
29NAMED_ASSIGN = 11000
30DOTTED_NAME = 4000
31VERY_STRONGLY_CONNECTED = 3500
32STRONGLY_CONNECTED = 3000
33CONNECTED = 500
34
35OR_TEST = 1000
36AND_TEST = 1100
37NOT_TEST = 1200
38COMPARISON = 1300
39STAR_EXPR = 1300
40EXPR = 1400
41XOR_EXPR = 1500
42AND_EXPR = 1700
43SHIFT_EXPR = 1800
44ARITH_EXPR = 1900
45TERM = 2000
46FACTOR = 2100
47POWER = 2200
48ATOM = 2300
49ONE_ELEMENT_ARGUMENT = 2500
50
51
52def ComputeSplitPenalties(tree):
53  """Compute split penalties on tokens in the given parse tree.
54
55  Arguments:
56    tree: the top-level pytree node to annotate with penalties.
57  """
58  _SplitPenaltyAssigner().Visit(tree)
59
60
61class _SplitPenaltyAssigner(pytree_visitor.PyTreeVisitor):
62  """Assigns split penalties to tokens, based on parse tree structure.
63
64  Split penalties are attached as annotations to tokens.
65  """
66
67  def Visit_import_as_names(self, node):  # pyline: disable=invalid-name
68    # import_as_names ::= import_as_name (',' import_as_name)* [',']
69    self.DefaultNodeVisit(node)
70    prev_child = None
71    for child in node.children:
72      if (prev_child and isinstance(prev_child, pytree.Leaf) and
73          prev_child.value == ','):
74        _SetSplitPenalty(child, style.Get('SPLIT_PENALTY_IMPORT_NAMES'))
75      prev_child = child
76
77  def Visit_classdef(self, node):  # pylint: disable=invalid-name
78    # classdef ::= 'class' NAME ['(' [arglist] ')'] ':' suite
79    #
80    # NAME
81    _SetUnbreakable(node.children[1])
82    if len(node.children) > 4:
83      # opening '('
84      _SetUnbreakable(node.children[2])
85    # ':'
86    _SetUnbreakable(node.children[-2])
87    self.DefaultNodeVisit(node)
88
89  def Visit_funcdef(self, node):  # pylint: disable=invalid-name
90    # funcdef ::= 'def' NAME parameters ['->' test] ':' suite
91    #
92    # Can't break before the function name and before the colon. The parameters
93    # are handled by child iteration.
94    colon_idx = 1
95    while pytree_utils.NodeName(node.children[colon_idx]) == 'simple_stmt':
96      colon_idx += 1
97    _SetUnbreakable(node.children[colon_idx])
98    arrow_idx = -1
99    while colon_idx < len(node.children):
100      if isinstance(node.children[colon_idx], pytree.Leaf):
101        if node.children[colon_idx].value == ':':
102          break
103        if node.children[colon_idx].value == '->':
104          arrow_idx = colon_idx
105      colon_idx += 1
106    _SetUnbreakable(node.children[colon_idx])
107    self.DefaultNodeVisit(node)
108    if arrow_idx > 0:
109      _SetSplitPenalty(
110          pytree_utils.LastLeafNode(node.children[arrow_idx - 1]), 0)
111      _SetUnbreakable(node.children[arrow_idx])
112      _SetStronglyConnected(node.children[arrow_idx + 1])
113
114  def Visit_lambdef(self, node):  # pylint: disable=invalid-name
115    # lambdef ::= 'lambda' [varargslist] ':' test
116    # Loop over the lambda up to and including the colon.
117    allow_multiline_lambdas = style.Get('ALLOW_MULTILINE_LAMBDAS')
118    if not allow_multiline_lambdas:
119      for child in node.children:
120        if pytree_utils.NodeName(child) == 'COMMENT':
121          if re.search(r'pylint:.*disable=.*\bg-long-lambda', child.value):
122            allow_multiline_lambdas = True
123            break
124
125    if allow_multiline_lambdas:
126      _SetStronglyConnected(node)
127    else:
128      self._SetUnbreakableOnChildren(node)
129
130  def Visit_parameters(self, node):  # pylint: disable=invalid-name
131    # parameters ::= '(' [typedargslist] ')'
132    self.DefaultNodeVisit(node)
133
134    # Can't break before the opening paren of a parameter list.
135    _SetUnbreakable(node.children[0])
136    if not style.Get('DEDENT_CLOSING_BRACKETS'):
137      _SetStronglyConnected(node.children[-1])
138
139  def Visit_arglist(self, node):  # pylint: disable=invalid-name
140    # arglist ::= argument (',' argument)* [',']
141    self.DefaultNodeVisit(node)
142    index = 1
143    while index < len(node.children):
144      child = node.children[index]
145      if isinstance(child, pytree.Leaf) and child.value == ',':
146        _SetUnbreakable(child)
147      index += 1
148    for child in node.children:
149      if pytree_utils.NodeName(child) == 'atom':
150        _IncreasePenalty(child, CONNECTED)
151
152  def Visit_argument(self, node):  # pylint: disable=invalid-name
153    # argument ::= test [comp_for] | test '=' test  # Really [keyword '='] test
154    self.DefaultNodeVisit(node)
155    index = 1
156    while index < len(node.children) - 1:
157      child = node.children[index]
158      if isinstance(child, pytree.Leaf) and child.value == '=':
159        _SetSplitPenalty(
160            pytree_utils.FirstLeafNode(node.children[index]), NAMED_ASSIGN)
161        _SetSplitPenalty(
162            pytree_utils.FirstLeafNode(node.children[index + 1]), NAMED_ASSIGN)
163      index += 1
164
165  def Visit_tname(self, node):  # pylint: disable=invalid-name
166    # tname ::= NAME [':' test]
167    self.DefaultNodeVisit(node)
168    index = 1
169    while index < len(node.children) - 1:
170      child = node.children[index]
171      if isinstance(child, pytree.Leaf) and child.value == ':':
172        _SetSplitPenalty(
173            pytree_utils.FirstLeafNode(node.children[index]), NAMED_ASSIGN)
174        _SetSplitPenalty(
175            pytree_utils.FirstLeafNode(node.children[index + 1]), NAMED_ASSIGN)
176      index += 1
177
178  def Visit_dotted_name(self, node):  # pylint: disable=invalid-name
179    # dotted_name ::= NAME ('.' NAME)*
180    self._SetUnbreakableOnChildren(node)
181
182  def Visit_dictsetmaker(self, node):  # pylint: disable=invalid-name
183    # dictsetmaker ::= ( (test ':' test
184    #                      (comp_for | (',' test ':' test)* [','])) |
185    #                    (test (comp_for | (',' test)* [','])) )
186    for child in node.children:
187      self.Visit(child)
188      if pytree_utils.NodeName(child) == 'COLON':
189        # This is a key to a dictionary. We don't want to split the key if at
190        # all possible.
191        _SetStronglyConnected(child)
192
193  def Visit_trailer(self, node):  # pylint: disable=invalid-name
194    # trailer ::= '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
195    if node.children[0].value == '.':
196      self._SetUnbreakableOnChildren(node)
197      _SetSplitPenalty(node.children[1], DOTTED_NAME)
198    elif len(node.children) == 2:
199      # Don't split an empty argument list if at all possible.
200      _SetSplitPenalty(node.children[1], VERY_STRONGLY_CONNECTED)
201    elif len(node.children) == 3:
202      name = pytree_utils.NodeName(node.children[1])
203      if name in {'argument', 'comparison'}:
204        # Don't split an argument list with one element if at all possible.
205        _SetStronglyConnected(node.children[1])
206        if (len(node.children[1].children) > 1 and
207            pytree_utils.NodeName(node.children[1].children[1]) == 'comp_for'):
208          # Don't penalize splitting before a comp_for expression.
209          _SetSplitPenalty(pytree_utils.FirstLeafNode(node.children[1]), 0)
210        else:
211          _SetSplitPenalty(
212              pytree_utils.FirstLeafNode(node.children[1]),
213              ONE_ELEMENT_ARGUMENT)
214      elif (pytree_utils.NodeName(node.children[0]) == 'LSQB' and
215            len(node.children[1].children) > 2 and
216            (name.endswith('_test') or name.endswith('_expr'))):
217        _SetStronglyConnected(node.children[1].children[0])
218        _SetStronglyConnected(node.children[1].children[2])
219
220        # Still allow splitting around the operator.
221        split_before = ((name.endswith('_test') and
222                         style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR')) or
223                        (name.endswith('_expr') and
224                         style.Get('SPLIT_BEFORE_BITWISE_OPERATOR')))
225        if split_before:
226          _SetSplitPenalty(
227              pytree_utils.LastLeafNode(node.children[1].children[1]), 0)
228        else:
229          _SetSplitPenalty(
230              pytree_utils.FirstLeafNode(node.children[1].children[2]), 0)
231
232        # Don't split the ending bracket of a subscript list.
233        _SetVeryStronglyConnected(node.children[-1])
234      elif name not in {
235          'arglist', 'argument', 'term', 'or_test', 'and_test', 'comparison',
236          'atom', 'power'
237      }:
238        # Don't split an argument list with one element if at all possible.
239        _SetStronglyConnected(node.children[1], node.children[2])
240
241      if name == 'arglist':
242        _SetStronglyConnected(node.children[-1])
243
244    self.DefaultNodeVisit(node)
245
246  def Visit_power(self, node):  # pylint: disable=invalid-name,missing-docstring
247    # power ::= atom trailer* ['**' factor]
248    self.DefaultNodeVisit(node)
249
250    # When atom is followed by a trailer, we can not break between them.
251    # E.g. arr[idx] - no break allowed between 'arr' and '['.
252    if (len(node.children) > 1 and
253        pytree_utils.NodeName(node.children[1]) == 'trailer'):
254      # children[1] itself is a whole trailer: we don't want to
255      # mark all of it as unbreakable, only its first token: (, [ or .
256      _SetUnbreakable(node.children[1].children[0])
257
258      # A special case when there are more trailers in the sequence. Given:
259      #   atom tr1 tr2
260      # The last token of tr1 and the first token of tr2 comprise an unbreakable
261      # region. For example: foo.bar.baz(1)
262      # We can't put breaks between either of the '.', '(', or '[' and the names
263      # *preceding* them.
264      prev_trailer_idx = 1
265      while prev_trailer_idx < len(node.children) - 1:
266        cur_trailer_idx = prev_trailer_idx + 1
267        cur_trailer = node.children[cur_trailer_idx]
268        if pytree_utils.NodeName(cur_trailer) == 'trailer':
269          # Now we know we have two trailers one after the other
270          prev_trailer = node.children[prev_trailer_idx]
271          if prev_trailer.children[-1].value != ')':
272            # Set the previous node unbreakable if it's not a function call:
273            #   atom tr1() tr2
274            # It may be necessary (though undesirable) to split up a previous
275            # function call's parentheses to the next line.
276            _SetStronglyConnected(prev_trailer.children[-1])
277          _SetStronglyConnected(cur_trailer.children[0])
278          prev_trailer_idx = cur_trailer_idx
279        else:
280          break
281
282    # We don't want to split before the last ')' of a function call. This also
283    # takes care of the special case of:
284    #   atom tr1 tr2 ... trn
285    # where the 'tr#' are trailers that may end in a ')'.
286    for trailer in node.children[1:]:
287      if pytree_utils.NodeName(trailer) != 'trailer':
288        break
289      if trailer.children[0].value in '([':
290        if len(trailer.children) > 2:
291          subtypes = pytree_utils.GetNodeAnnotation(
292              trailer.children[0], pytree_utils.Annotation.SUBTYPE)
293          if subtypes and format_token.Subtype.SUBSCRIPT_BRACKET in subtypes:
294            _SetStronglyConnected(
295                pytree_utils.FirstLeafNode(trailer.children[1]))
296
297          last_child_node = pytree_utils.LastLeafNode(trailer)
298          if last_child_node.value.strip().startswith('#'):
299            last_child_node = last_child_node.prev_sibling
300          if not style.Get('DEDENT_CLOSING_BRACKETS'):
301            last = pytree_utils.LastLeafNode(last_child_node.prev_sibling)
302            if last.value != ',':
303              if last_child_node.value == ']':
304                _SetUnbreakable(last_child_node)
305              else:
306                _SetSplitPenalty(last_child_node, VERY_STRONGLY_CONNECTED)
307        else:
308          # If the trailer's children are '()', then make it a strongly
309          # connected region.  It's sometimes necessary, though undesirable, to
310          # split the two.
311          _SetStronglyConnected(trailer.children[-1])
312
313    # If the original source has a "builder" style calls, then we should allow
314    # the reformatter to retain that.
315    _AllowBuilderStyleCalls(node)
316
317  def Visit_subscript(self, node):  # pylint: disable=invalid-name
318    # subscript ::= test | [test] ':' [test] [sliceop]
319    _SetStronglyConnected(*node.children)
320    self.DefaultNodeVisit(node)
321
322  def Visit_comp_for(self, node):  # pylint: disable=invalid-name
323    # comp_for ::= 'for' exprlist 'in' testlist_safe [comp_iter]
324    _SetSplitPenalty(pytree_utils.FirstLeafNode(node), 0)
325    _SetStronglyConnected(*node.children[1:])
326    self.DefaultNodeVisit(node)
327
328  def Visit_comp_if(self, node):  # pylint: disable=invalid-name
329    # comp_if ::= 'if' old_test [comp_iter]
330    _SetSplitPenalty(node.children[0],
331                     style.Get('SPLIT_PENALTY_BEFORE_IF_EXPR'))
332    _SetStronglyConnected(*node.children[1:])
333    self.DefaultNodeVisit(node)
334
335  def Visit_or_test(self, node):  # pylint: disable=invalid-name
336    # or_test ::= and_test ('or' and_test)*
337    self.DefaultNodeVisit(node)
338    _IncreasePenalty(node, OR_TEST)
339    index = 1
340    while index + 1 < len(node.children):
341      if style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR'):
342        _DecrementSplitPenalty(
343            pytree_utils.FirstLeafNode(node.children[index]), OR_TEST)
344      else:
345        _DecrementSplitPenalty(
346            pytree_utils.FirstLeafNode(node.children[index + 1]), OR_TEST)
347      index += 2
348
349  def Visit_and_test(self, node):  # pylint: disable=invalid-name
350    # and_test ::= not_test ('and' not_test)*
351    self.DefaultNodeVisit(node)
352    _IncreasePenalty(node, AND_TEST)
353    index = 1
354    while index + 1 < len(node.children):
355      if style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR'):
356        _DecrementSplitPenalty(
357            pytree_utils.FirstLeafNode(node.children[index]), AND_TEST)
358      else:
359        _DecrementSplitPenalty(
360            pytree_utils.FirstLeafNode(node.children[index + 1]), AND_TEST)
361      index += 2
362
363  def Visit_not_test(self, node):  # pylint: disable=invalid-name
364    # not_test ::= 'not' not_test | comparison
365    self.DefaultNodeVisit(node)
366    _IncreasePenalty(node, NOT_TEST)
367
368  def Visit_comparison(self, node):  # pylint: disable=invalid-name
369    # comparison ::= expr (comp_op expr)*
370    self.DefaultNodeVisit(node)
371    if len(node.children) == 3 and _StronglyConnectedCompOp(node):
372      _SetSplitPenalty(
373          pytree_utils.FirstLeafNode(node.children[1]), STRONGLY_CONNECTED)
374      _SetSplitPenalty(
375          pytree_utils.FirstLeafNode(node.children[2]), STRONGLY_CONNECTED)
376    else:
377      _IncreasePenalty(node, COMPARISON)
378
379  def Visit_star_expr(self, node):  # pylint: disable=invalid-name
380    # star_expr ::= '*' expr
381    self.DefaultNodeVisit(node)
382    _IncreasePenalty(node, STAR_EXPR)
383
384  def Visit_expr(self, node):  # pylint: disable=invalid-name
385    # expr ::= xor_expr ('|' xor_expr)*
386    self.DefaultNodeVisit(node)
387    _IncreasePenalty(node, EXPR)
388    index = 1
389    while index < len(node.children) - 1:
390      child = node.children[index]
391      if isinstance(child, pytree.Leaf) and child.value == '|':
392        if style.Get('SPLIT_BEFORE_BITWISE_OPERATOR'):
393          _SetSplitPenalty(child, style.Get('SPLIT_PENALTY_BITWISE_OPERATOR'))
394        else:
395          _SetSplitPenalty(
396              pytree_utils.FirstLeafNode(node.children[index + 1]),
397              style.Get('SPLIT_PENALTY_BITWISE_OPERATOR'))
398      index += 1
399
400  def Visit_xor_expr(self, node):  # pylint: disable=invalid-name
401    # xor_expr ::= and_expr ('^' and_expr)*
402    self.DefaultNodeVisit(node)
403    _IncreasePenalty(node, XOR_EXPR)
404
405  def Visit_and_expr(self, node):  # pylint: disable=invalid-name
406    # and_expr ::= shift_expr ('&' shift_expr)*
407    self.DefaultNodeVisit(node)
408    _IncreasePenalty(node, AND_EXPR)
409
410  def Visit_shift_expr(self, node):  # pylint: disable=invalid-name
411    # shift_expr ::= arith_expr (('<<'|'>>') arith_expr)*
412    self.DefaultNodeVisit(node)
413    _IncreasePenalty(node, SHIFT_EXPR)
414
415  _ARITH_OPS = frozenset({'PLUS', 'MINUS'})
416
417  def Visit_arith_expr(self, node):  # pylint: disable=invalid-name
418    # arith_expr ::= term (('+'|'-') term)*
419    self.DefaultNodeVisit(node)
420    _IncreasePenalty(node, ARITH_EXPR)
421
422    index = 1
423    while index < len(node.children) - 1:
424      child = node.children[index]
425      if pytree_utils.NodeName(child) in self._ARITH_OPS:
426        next_node = pytree_utils.FirstLeafNode(node.children[index + 1])
427        _SetSplitPenalty(next_node, ARITH_EXPR)
428      index += 1
429
430  _TERM_OPS = frozenset({'STAR', 'AT', 'SLASH', 'PERCENT', 'DOUBLESLASH'})
431
432  def Visit_term(self, node):  # pylint: disable=invalid-name
433    # term ::= factor (('*'|'@'|'/'|'%'|'//') factor)*
434    self.DefaultNodeVisit(node)
435    _IncreasePenalty(node, TERM)
436
437    index = 1
438    while index < len(node.children) - 1:
439      child = node.children[index]
440      if pytree_utils.NodeName(child) in self._TERM_OPS:
441        next_node = pytree_utils.FirstLeafNode(node.children[index + 1])
442        _SetSplitPenalty(next_node, TERM)
443      index += 1
444
445  def Visit_factor(self, node):  # pyline: disable=invalid-name
446    # factor ::= ('+'|'-'|'~') factor | power
447    self.DefaultNodeVisit(node)
448    _IncreasePenalty(node, FACTOR)
449
450  def Visit_atom(self, node):  # pylint: disable=invalid-name
451    # atom ::= ('(' [yield_expr|testlist_gexp] ')'
452    #           '[' [listmaker] ']' |
453    #           '{' [dictsetmaker] '}')
454    self.DefaultNodeVisit(node)
455    if node.children[0].value == '(':
456      if node.children[-1].value == ')':
457        if pytree_utils.NodeName(node.parent) == 'if_stmt':
458          _SetSplitPenalty(node.children[-1], STRONGLY_CONNECTED)
459        else:
460          if len(node.children) > 2:
461            _SetSplitPenalty(pytree_utils.FirstLeafNode(node.children[1]), EXPR)
462          _SetSplitPenalty(node.children[-1], ATOM)
463    elif node.children[0].value in '[{' and len(node.children) == 2:
464      # Keep empty containers together if we can.
465      _SetUnbreakable(node.children[-1])
466
467  def Visit_testlist_gexp(self, node):  # pylint: disable=invalid-name
468    self.DefaultNodeVisit(node)
469    prev_was_comma = False
470    for child in node.children:
471      if isinstance(child, pytree.Leaf) and child.value == ',':
472        _SetUnbreakable(child)
473        prev_was_comma = True
474      else:
475        if prev_was_comma:
476          _SetSplitPenalty(pytree_utils.FirstLeafNode(child), 0)
477        prev_was_comma = False
478
479  ############################################################################
480  # Helper methods that set the annotations.
481
482  def _SetUnbreakableOnChildren(self, node):
483    """Set an UNBREAKABLE penalty annotation on children of node."""
484    for child in node.children:
485      self.Visit(child)
486    start = 2 if hasattr(node.children[0], 'is_pseudo') else 1
487    for i in py3compat.range(start, len(node.children)):
488      _SetUnbreakable(node.children[i])
489
490
491def _SetUnbreakable(node):
492  """Set an UNBREAKABLE penalty annotation for the given node."""
493  _RecAnnotate(node, pytree_utils.Annotation.SPLIT_PENALTY, UNBREAKABLE)
494
495
496def _SetStronglyConnected(*nodes):
497  """Set a STRONGLY_CONNECTED penalty annotation for the given nodes."""
498  for node in nodes:
499    _RecAnnotate(node, pytree_utils.Annotation.SPLIT_PENALTY,
500                 STRONGLY_CONNECTED)
501
502
503def _SetVeryStronglyConnected(*nodes):
504  """Set a VERY_STRONGLY_CONNECTED penalty annotation for the given nodes."""
505  for node in nodes:
506    _RecAnnotate(node, pytree_utils.Annotation.SPLIT_PENALTY,
507                 VERY_STRONGLY_CONNECTED)
508
509
510def _SetExpressionPenalty(node, penalty):
511  """Set a penalty annotation on children nodes."""
512
513  def RecExpression(node, first_child_leaf):
514    if node is first_child_leaf:
515      return
516
517    if isinstance(node, pytree.Leaf):
518      if node.value in {'(', 'for', 'if'}:
519        return
520      penalty_annotation = pytree_utils.GetNodeAnnotation(
521          node, pytree_utils.Annotation.SPLIT_PENALTY, default=0)
522      if penalty_annotation < penalty:
523        _SetSplitPenalty(node, penalty)
524    else:
525      for child in node.children:
526        RecExpression(child, first_child_leaf)
527
528  RecExpression(node, pytree_utils.FirstLeafNode(node))
529
530
531def _IncreasePenalty(node, amt):
532  """Increase a penalty annotation on children nodes."""
533
534  def RecExpression(node, first_child_leaf):
535    if node is first_child_leaf:
536      return
537
538    if isinstance(node, pytree.Leaf):
539      if node.value in {'(', 'for', 'if'}:
540        return
541      penalty = pytree_utils.GetNodeAnnotation(
542          node, pytree_utils.Annotation.SPLIT_PENALTY, default=0)
543      _SetSplitPenalty(node, penalty + amt)
544    else:
545      for child in node.children:
546        RecExpression(child, first_child_leaf)
547
548  RecExpression(node, pytree_utils.FirstLeafNode(node))
549
550
551def _RecAnnotate(tree, annotate_name, annotate_value):
552  """Recursively set the given annotation on all leafs of the subtree.
553
554  Takes care to only increase the penalty. If the node already has a higher
555  or equal penalty associated with it, this is a no-op.
556
557  Args:
558    tree: subtree to annotate
559    annotate_name: name of the annotation to set
560    annotate_value: value of the annotation to set
561  """
562  for child in tree.children:
563    _RecAnnotate(child, annotate_name, annotate_value)
564  if isinstance(tree, pytree.Leaf):
565    cur_annotate = pytree_utils.GetNodeAnnotation(
566        tree, annotate_name, default=0)
567    if cur_annotate < annotate_value:
568      pytree_utils.SetNodeAnnotation(tree, annotate_name, annotate_value)
569
570
571def _StronglyConnectedCompOp(op):
572  if (len(op.children[1].children) == 2 and
573      pytree_utils.NodeName(op.children[1]) == 'comp_op' and
574      pytree_utils.FirstLeafNode(op.children[1]).value == 'not' and
575      pytree_utils.LastLeafNode(op.children[1]).value == 'in'):
576    return True
577  if (isinstance(op.children[1], pytree.Leaf) and
578      op.children[1].value in {'==', 'in'}):
579    return True
580  return False
581
582
583def _DecrementSplitPenalty(node, amt):
584  penalty = pytree_utils.GetNodeAnnotation(
585      node, pytree_utils.Annotation.SPLIT_PENALTY, default=amt)
586  penalty = penalty - amt if amt < penalty else 0
587  _SetSplitPenalty(node, penalty)
588
589
590def _SetSplitPenalty(node, penalty):
591  pytree_utils.SetNodeAnnotation(node, pytree_utils.Annotation.SPLIT_PENALTY,
592                                 penalty)
593
594
595def _AllowBuilderStyleCalls(node):
596  """Allow splitting before '.' if it's a builder style function call."""
597
598  def RecGetLeaves(node):
599    if isinstance(node, pytree.Leaf):
600      return [node]
601    children = []
602    for child in node.children:
603      children += RecGetLeaves(child)
604    return children
605
606  list_of_children = RecGetLeaves(node)
607  prev_child = None
608  for child in list_of_children:
609    if child.value == '.':
610      if prev_child.lineno != child.lineno:
611        _SetSplitPenalty(child, 0)
612    prev_child = child
613