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