split_penalty.py 24 KB

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