]>
Commit | Line | Data |
---|---|---|
1 | import itertools | |
2 | import math | |
3 | import sys | |
4 | from dataclasses import dataclass, field | |
5 | from typing import ( | |
6 | Callable, | |
7 | Dict, | |
8 | Iterator, | |
9 | List, | |
10 | Optional, | |
11 | Sequence, | |
12 | Tuple, | |
13 | TypeVar, | |
14 | Union, | |
15 | cast, | |
16 | ) | |
17 | ||
18 | from black.brackets import COMMA_PRIORITY, DOT_PRIORITY, BracketTracker | |
19 | from black.mode import Mode, Preview | |
20 | from black.nodes import ( | |
21 | BRACKETS, | |
22 | CLOSING_BRACKETS, | |
23 | OPENING_BRACKETS, | |
24 | STANDALONE_COMMENT, | |
25 | TEST_DESCENDANTS, | |
26 | child_towards, | |
27 | is_import, | |
28 | is_multiline_string, | |
29 | is_one_sequence_between, | |
30 | is_type_comment, | |
31 | is_type_ignore_comment, | |
32 | is_with_or_async_with_stmt, | |
33 | replace_child, | |
34 | syms, | |
35 | whitespace, | |
36 | ) | |
37 | from black.strings import str_width | |
38 | from blib2to3.pgen2 import token | |
39 | from blib2to3.pytree import Leaf, Node | |
40 | ||
41 | # types | |
42 | T = TypeVar("T") | |
43 | Index = int | |
44 | LeafID = int | |
45 | LN = Union[Leaf, Node] | |
46 | ||
47 | ||
48 | @dataclass | |
49 | class Line: | |
50 | """Holds leaves and comments. Can be printed with `str(line)`.""" | |
51 | ||
52 | mode: Mode = field(repr=False) | |
53 | depth: int = 0 | |
54 | leaves: List[Leaf] = field(default_factory=list) | |
55 | # keys ordered like `leaves` | |
56 | comments: Dict[LeafID, List[Leaf]] = field(default_factory=dict) | |
57 | bracket_tracker: BracketTracker = field(default_factory=BracketTracker) | |
58 | inside_brackets: bool = False | |
59 | should_split_rhs: bool = False | |
60 | magic_trailing_comma: Optional[Leaf] = None | |
61 | ||
62 | def append( | |
63 | self, leaf: Leaf, preformatted: bool = False, track_bracket: bool = False | |
64 | ) -> None: | |
65 | """Add a new `leaf` to the end of the line. | |
66 | ||
67 | Unless `preformatted` is True, the `leaf` will receive a new consistent | |
68 | whitespace prefix and metadata applied by :class:`BracketTracker`. | |
69 | Trailing commas are maybe removed, unpacked for loop variables are | |
70 | demoted from being delimiters. | |
71 | ||
72 | Inline comments are put aside. | |
73 | """ | |
74 | has_value = leaf.type in BRACKETS or bool(leaf.value.strip()) | |
75 | if not has_value: | |
76 | return | |
77 | ||
78 | if token.COLON == leaf.type and self.is_class_paren_empty: | |
79 | del self.leaves[-2:] | |
80 | if self.leaves and not preformatted: | |
81 | # Note: at this point leaf.prefix should be empty except for | |
82 | # imports, for which we only preserve newlines. | |
83 | leaf.prefix += whitespace( | |
84 | leaf, | |
85 | complex_subscript=self.is_complex_subscript(leaf), | |
86 | mode=self.mode, | |
87 | ) | |
88 | if self.inside_brackets or not preformatted or track_bracket: | |
89 | self.bracket_tracker.mark(leaf) | |
90 | if self.mode.magic_trailing_comma: | |
91 | if self.has_magic_trailing_comma(leaf): | |
92 | self.magic_trailing_comma = leaf | |
93 | elif self.has_magic_trailing_comma(leaf, ensure_removable=True): | |
94 | self.remove_trailing_comma() | |
95 | if not self.append_comment(leaf): | |
96 | self.leaves.append(leaf) | |
97 | ||
98 | def append_safe(self, leaf: Leaf, preformatted: bool = False) -> None: | |
99 | """Like :func:`append()` but disallow invalid standalone comment structure. | |
100 | ||
101 | Raises ValueError when any `leaf` is appended after a standalone comment | |
102 | or when a standalone comment is not the first leaf on the line. | |
103 | """ | |
104 | if self.bracket_tracker.depth == 0: | |
105 | if self.is_comment: | |
106 | raise ValueError("cannot append to standalone comments") | |
107 | ||
108 | if self.leaves and leaf.type == STANDALONE_COMMENT: | |
109 | raise ValueError( | |
110 | "cannot append standalone comments to a populated line" | |
111 | ) | |
112 | ||
113 | self.append(leaf, preformatted=preformatted) | |
114 | ||
115 | @property | |
116 | def is_comment(self) -> bool: | |
117 | """Is this line a standalone comment?""" | |
118 | return len(self.leaves) == 1 and self.leaves[0].type == STANDALONE_COMMENT | |
119 | ||
120 | @property | |
121 | def is_decorator(self) -> bool: | |
122 | """Is this line a decorator?""" | |
123 | return bool(self) and self.leaves[0].type == token.AT | |
124 | ||
125 | @property | |
126 | def is_import(self) -> bool: | |
127 | """Is this an import line?""" | |
128 | return bool(self) and is_import(self.leaves[0]) | |
129 | ||
130 | @property | |
131 | def is_with_or_async_with_stmt(self) -> bool: | |
132 | """Is this a with_stmt line?""" | |
133 | return bool(self) and is_with_or_async_with_stmt(self.leaves[0]) | |
134 | ||
135 | @property | |
136 | def is_class(self) -> bool: | |
137 | """Is this line a class definition?""" | |
138 | return ( | |
139 | bool(self) | |
140 | and self.leaves[0].type == token.NAME | |
141 | and self.leaves[0].value == "class" | |
142 | ) | |
143 | ||
144 | @property | |
145 | def is_stub_class(self) -> bool: | |
146 | """Is this line a class definition with a body consisting only of "..."?""" | |
147 | return self.is_class and self.leaves[-3:] == [ | |
148 | Leaf(token.DOT, ".") for _ in range(3) | |
149 | ] | |
150 | ||
151 | @property | |
152 | def is_def(self) -> bool: | |
153 | """Is this a function definition? (Also returns True for async defs.)""" | |
154 | try: | |
155 | first_leaf = self.leaves[0] | |
156 | except IndexError: | |
157 | return False | |
158 | ||
159 | try: | |
160 | second_leaf: Optional[Leaf] = self.leaves[1] | |
161 | except IndexError: | |
162 | second_leaf = None | |
163 | return (first_leaf.type == token.NAME and first_leaf.value == "def") or ( | |
164 | first_leaf.type == token.ASYNC | |
165 | and second_leaf is not None | |
166 | and second_leaf.type == token.NAME | |
167 | and second_leaf.value == "def" | |
168 | ) | |
169 | ||
170 | @property | |
171 | def is_stub_def(self) -> bool: | |
172 | """Is this line a function definition with a body consisting only of "..."?""" | |
173 | return self.is_def and self.leaves[-4:] == [Leaf(token.COLON, ":")] + [ | |
174 | Leaf(token.DOT, ".") for _ in range(3) | |
175 | ] | |
176 | ||
177 | @property | |
178 | def is_class_paren_empty(self) -> bool: | |
179 | """Is this a class with no base classes but using parentheses? | |
180 | ||
181 | Those are unnecessary and should be removed. | |
182 | """ | |
183 | return ( | |
184 | bool(self) | |
185 | and len(self.leaves) == 4 | |
186 | and self.is_class | |
187 | and self.leaves[2].type == token.LPAR | |
188 | and self.leaves[2].value == "(" | |
189 | and self.leaves[3].type == token.RPAR | |
190 | and self.leaves[3].value == ")" | |
191 | ) | |
192 | ||
193 | @property | |
194 | def is_triple_quoted_string(self) -> bool: | |
195 | """Is the line a triple quoted string?""" | |
196 | return ( | |
197 | bool(self) | |
198 | and self.leaves[0].type == token.STRING | |
199 | and self.leaves[0].value.startswith(('"""', "'''")) | |
200 | ) | |
201 | ||
202 | @property | |
203 | def opens_block(self) -> bool: | |
204 | """Does this line open a new level of indentation.""" | |
205 | if len(self.leaves) == 0: | |
206 | return False | |
207 | return self.leaves[-1].type == token.COLON | |
208 | ||
209 | def is_fmt_pass_converted( | |
210 | self, *, first_leaf_matches: Optional[Callable[[Leaf], bool]] = None | |
211 | ) -> bool: | |
212 | """Is this line converted from fmt off/skip code? | |
213 | ||
214 | If first_leaf_matches is not None, it only returns True if the first | |
215 | leaf of converted code matches. | |
216 | """ | |
217 | if len(self.leaves) != 1: | |
218 | return False | |
219 | leaf = self.leaves[0] | |
220 | if ( | |
221 | leaf.type != STANDALONE_COMMENT | |
222 | or leaf.fmt_pass_converted_first_leaf is None | |
223 | ): | |
224 | return False | |
225 | return first_leaf_matches is None or first_leaf_matches( | |
226 | leaf.fmt_pass_converted_first_leaf | |
227 | ) | |
228 | ||
229 | def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool: | |
230 | """If so, needs to be split before emitting.""" | |
231 | for leaf in self.leaves: | |
232 | if leaf.type == STANDALONE_COMMENT and leaf.bracket_depth <= depth_limit: | |
233 | return True | |
234 | ||
235 | return False | |
236 | ||
237 | def contains_uncollapsable_type_comments(self) -> bool: | |
238 | ignored_ids = set() | |
239 | try: | |
240 | last_leaf = self.leaves[-1] | |
241 | ignored_ids.add(id(last_leaf)) | |
242 | if last_leaf.type == token.COMMA or ( | |
243 | last_leaf.type == token.RPAR and not last_leaf.value | |
244 | ): | |
245 | # When trailing commas or optional parens are inserted by Black for | |
246 | # consistency, comments after the previous last element are not moved | |
247 | # (they don't have to, rendering will still be correct). So we ignore | |
248 | # trailing commas and invisible. | |
249 | last_leaf = self.leaves[-2] | |
250 | ignored_ids.add(id(last_leaf)) | |
251 | except IndexError: | |
252 | return False | |
253 | ||
254 | # A type comment is uncollapsable if it is attached to a leaf | |
255 | # that isn't at the end of the line (since that could cause it | |
256 | # to get associated to a different argument) or if there are | |
257 | # comments before it (since that could cause it to get hidden | |
258 | # behind a comment. | |
259 | comment_seen = False | |
260 | for leaf_id, comments in self.comments.items(): | |
261 | for comment in comments: | |
262 | if is_type_comment(comment): | |
263 | if comment_seen or ( | |
264 | not is_type_ignore_comment(comment) | |
265 | and leaf_id not in ignored_ids | |
266 | ): | |
267 | return True | |
268 | ||
269 | comment_seen = True | |
270 | ||
271 | return False | |
272 | ||
273 | def contains_unsplittable_type_ignore(self) -> bool: | |
274 | if not self.leaves: | |
275 | return False | |
276 | ||
277 | # If a 'type: ignore' is attached to the end of a line, we | |
278 | # can't split the line, because we can't know which of the | |
279 | # subexpressions the ignore was meant to apply to. | |
280 | # | |
281 | # We only want this to apply to actual physical lines from the | |
282 | # original source, though: we don't want the presence of a | |
283 | # 'type: ignore' at the end of a multiline expression to | |
284 | # justify pushing it all onto one line. Thus we | |
285 | # (unfortunately) need to check the actual source lines and | |
286 | # only report an unsplittable 'type: ignore' if this line was | |
287 | # one line in the original code. | |
288 | ||
289 | # Grab the first and last line numbers, skipping generated leaves | |
290 | first_line = next((leaf.lineno for leaf in self.leaves if leaf.lineno != 0), 0) | |
291 | last_line = next( | |
292 | (leaf.lineno for leaf in reversed(self.leaves) if leaf.lineno != 0), 0 | |
293 | ) | |
294 | ||
295 | if first_line == last_line: | |
296 | # We look at the last two leaves since a comma or an | |
297 | # invisible paren could have been added at the end of the | |
298 | # line. | |
299 | for node in self.leaves[-2:]: | |
300 | for comment in self.comments.get(id(node), []): | |
301 | if is_type_ignore_comment(comment): | |
302 | return True | |
303 | ||
304 | return False | |
305 | ||
306 | def contains_multiline_strings(self) -> bool: | |
307 | return any(is_multiline_string(leaf) for leaf in self.leaves) | |
308 | ||
309 | def has_magic_trailing_comma( | |
310 | self, closing: Leaf, ensure_removable: bool = False | |
311 | ) -> bool: | |
312 | """Return True if we have a magic trailing comma, that is when: | |
313 | - there's a trailing comma here | |
314 | - it's not a one-tuple | |
315 | - it's not a single-element subscript | |
316 | Additionally, if ensure_removable: | |
317 | - it's not from square bracket indexing | |
318 | (specifically, single-element square bracket indexing) | |
319 | """ | |
320 | if not ( | |
321 | closing.type in CLOSING_BRACKETS | |
322 | and self.leaves | |
323 | and self.leaves[-1].type == token.COMMA | |
324 | ): | |
325 | return False | |
326 | ||
327 | if closing.type == token.RBRACE: | |
328 | return True | |
329 | ||
330 | if closing.type == token.RSQB: | |
331 | if ( | |
332 | closing.parent | |
333 | and closing.parent.type == syms.trailer | |
334 | and closing.opening_bracket | |
335 | and is_one_sequence_between( | |
336 | closing.opening_bracket, | |
337 | closing, | |
338 | self.leaves, | |
339 | brackets=(token.LSQB, token.RSQB), | |
340 | ) | |
341 | ): | |
342 | return False | |
343 | ||
344 | if not ensure_removable: | |
345 | return True | |
346 | ||
347 | comma = self.leaves[-1] | |
348 | if comma.parent is None: | |
349 | return False | |
350 | return ( | |
351 | comma.parent.type != syms.subscriptlist | |
352 | or closing.opening_bracket is None | |
353 | or not is_one_sequence_between( | |
354 | closing.opening_bracket, | |
355 | closing, | |
356 | self.leaves, | |
357 | brackets=(token.LSQB, token.RSQB), | |
358 | ) | |
359 | ) | |
360 | ||
361 | if self.is_import: | |
362 | return True | |
363 | ||
364 | if closing.opening_bracket is not None and not is_one_sequence_between( | |
365 | closing.opening_bracket, closing, self.leaves | |
366 | ): | |
367 | return True | |
368 | ||
369 | return False | |
370 | ||
371 | def append_comment(self, comment: Leaf) -> bool: | |
372 | """Add an inline or standalone comment to the line.""" | |
373 | if ( | |
374 | comment.type == STANDALONE_COMMENT | |
375 | and self.bracket_tracker.any_open_brackets() | |
376 | ): | |
377 | comment.prefix = "" | |
378 | return False | |
379 | ||
380 | if comment.type != token.COMMENT: | |
381 | return False | |
382 | ||
383 | if not self.leaves: | |
384 | comment.type = STANDALONE_COMMENT | |
385 | comment.prefix = "" | |
386 | return False | |
387 | ||
388 | last_leaf = self.leaves[-1] | |
389 | if ( | |
390 | last_leaf.type == token.RPAR | |
391 | and not last_leaf.value | |
392 | and last_leaf.parent | |
393 | and len(list(last_leaf.parent.leaves())) <= 3 | |
394 | and not is_type_comment(comment) | |
395 | ): | |
396 | # Comments on an optional parens wrapping a single leaf should belong to | |
397 | # the wrapped node except if it's a type comment. Pinning the comment like | |
398 | # this avoids unstable formatting caused by comment migration. | |
399 | if len(self.leaves) < 2: | |
400 | comment.type = STANDALONE_COMMENT | |
401 | comment.prefix = "" | |
402 | return False | |
403 | ||
404 | last_leaf = self.leaves[-2] | |
405 | self.comments.setdefault(id(last_leaf), []).append(comment) | |
406 | return True | |
407 | ||
408 | def comments_after(self, leaf: Leaf) -> List[Leaf]: | |
409 | """Generate comments that should appear directly after `leaf`.""" | |
410 | return self.comments.get(id(leaf), []) | |
411 | ||
412 | def remove_trailing_comma(self) -> None: | |
413 | """Remove the trailing comma and moves the comments attached to it.""" | |
414 | trailing_comma = self.leaves.pop() | |
415 | trailing_comma_comments = self.comments.pop(id(trailing_comma), []) | |
416 | self.comments.setdefault(id(self.leaves[-1]), []).extend( | |
417 | trailing_comma_comments | |
418 | ) | |
419 | ||
420 | def is_complex_subscript(self, leaf: Leaf) -> bool: | |
421 | """Return True iff `leaf` is part of a slice with non-trivial exprs.""" | |
422 | open_lsqb = self.bracket_tracker.get_open_lsqb() | |
423 | if open_lsqb is None: | |
424 | return False | |
425 | ||
426 | subscript_start = open_lsqb.next_sibling | |
427 | ||
428 | if isinstance(subscript_start, Node): | |
429 | if subscript_start.type == syms.listmaker: | |
430 | return False | |
431 | ||
432 | if subscript_start.type == syms.subscriptlist: | |
433 | subscript_start = child_towards(subscript_start, leaf) | |
434 | return subscript_start is not None and any( | |
435 | n.type in TEST_DESCENDANTS for n in subscript_start.pre_order() | |
436 | ) | |
437 | ||
438 | def enumerate_with_length( | |
439 | self, reversed: bool = False | |
440 | ) -> Iterator[Tuple[Index, Leaf, int]]: | |
441 | """Return an enumeration of leaves with their length. | |
442 | ||
443 | Stops prematurely on multiline strings and standalone comments. | |
444 | """ | |
445 | op = cast( | |
446 | Callable[[Sequence[Leaf]], Iterator[Tuple[Index, Leaf]]], | |
447 | enumerate_reversed if reversed else enumerate, | |
448 | ) | |
449 | for index, leaf in op(self.leaves): | |
450 | length = len(leaf.prefix) + len(leaf.value) | |
451 | if "\n" in leaf.value: | |
452 | return # Multiline strings, we can't continue. | |
453 | ||
454 | for comment in self.comments_after(leaf): | |
455 | length += len(comment.value) | |
456 | ||
457 | yield index, leaf, length | |
458 | ||
459 | def clone(self) -> "Line": | |
460 | return Line( | |
461 | mode=self.mode, | |
462 | depth=self.depth, | |
463 | inside_brackets=self.inside_brackets, | |
464 | should_split_rhs=self.should_split_rhs, | |
465 | magic_trailing_comma=self.magic_trailing_comma, | |
466 | ) | |
467 | ||
468 | def __str__(self) -> str: | |
469 | """Render the line.""" | |
470 | if not self: | |
471 | return "\n" | |
472 | ||
473 | indent = " " * self.depth | |
474 | leaves = iter(self.leaves) | |
475 | first = next(leaves) | |
476 | res = f"{first.prefix}{indent}{first.value}" | |
477 | for leaf in leaves: | |
478 | res += str(leaf) | |
479 | for comment in itertools.chain.from_iterable(self.comments.values()): | |
480 | res += str(comment) | |
481 | ||
482 | return res + "\n" | |
483 | ||
484 | def __bool__(self) -> bool: | |
485 | """Return True if the line has leaves or comments.""" | |
486 | return bool(self.leaves or self.comments) | |
487 | ||
488 | ||
489 | @dataclass | |
490 | class RHSResult: | |
491 | """Intermediate split result from a right hand split.""" | |
492 | ||
493 | head: Line | |
494 | body: Line | |
495 | tail: Line | |
496 | opening_bracket: Leaf | |
497 | closing_bracket: Leaf | |
498 | ||
499 | ||
500 | @dataclass | |
501 | class LinesBlock: | |
502 | """Class that holds information about a block of formatted lines. | |
503 | ||
504 | This is introduced so that the EmptyLineTracker can look behind the standalone | |
505 | comments and adjust their empty lines for class or def lines. | |
506 | """ | |
507 | ||
508 | mode: Mode | |
509 | previous_block: Optional["LinesBlock"] | |
510 | original_line: Line | |
511 | before: int = 0 | |
512 | content_lines: List[str] = field(default_factory=list) | |
513 | after: int = 0 | |
514 | ||
515 | def all_lines(self) -> List[str]: | |
516 | empty_line = str(Line(mode=self.mode)) | |
517 | return ( | |
518 | [empty_line * self.before] + self.content_lines + [empty_line * self.after] | |
519 | ) | |
520 | ||
521 | ||
522 | @dataclass | |
523 | class EmptyLineTracker: | |
524 | """Provides a stateful method that returns the number of potential extra | |
525 | empty lines needed before and after the currently processed line. | |
526 | ||
527 | Note: this tracker works on lines that haven't been split yet. It assumes | |
528 | the prefix of the first leaf consists of optional newlines. Those newlines | |
529 | are consumed by `maybe_empty_lines()` and included in the computation. | |
530 | """ | |
531 | ||
532 | mode: Mode | |
533 | previous_line: Optional[Line] = None | |
534 | previous_block: Optional[LinesBlock] = None | |
535 | previous_defs: List[Line] = field(default_factory=list) | |
536 | semantic_leading_comment: Optional[LinesBlock] = None | |
537 | ||
538 | def maybe_empty_lines(self, current_line: Line) -> LinesBlock: | |
539 | """Return the number of extra empty lines before and after the `current_line`. | |
540 | ||
541 | This is for separating `def`, `async def` and `class` with extra empty | |
542 | lines (two on module-level). | |
543 | """ | |
544 | before, after = self._maybe_empty_lines(current_line) | |
545 | previous_after = self.previous_block.after if self.previous_block else 0 | |
546 | before = ( | |
547 | # Black should not insert empty lines at the beginning | |
548 | # of the file | |
549 | 0 | |
550 | if self.previous_line is None | |
551 | else before - previous_after | |
552 | ) | |
553 | block = LinesBlock( | |
554 | mode=self.mode, | |
555 | previous_block=self.previous_block, | |
556 | original_line=current_line, | |
557 | before=before, | |
558 | after=after, | |
559 | ) | |
560 | ||
561 | # Maintain the semantic_leading_comment state. | |
562 | if current_line.is_comment: | |
563 | if self.previous_line is None or ( | |
564 | not self.previous_line.is_decorator | |
565 | # `or before` means this comment already has an empty line before | |
566 | and (not self.previous_line.is_comment or before) | |
567 | and (self.semantic_leading_comment is None or before) | |
568 | ): | |
569 | self.semantic_leading_comment = block | |
570 | # `or before` means this decorator already has an empty line before | |
571 | elif not current_line.is_decorator or before: | |
572 | self.semantic_leading_comment = None | |
573 | ||
574 | self.previous_line = current_line | |
575 | self.previous_block = block | |
576 | return block | |
577 | ||
578 | def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]: | |
579 | max_allowed = 1 | |
580 | if current_line.depth == 0: | |
581 | max_allowed = 1 if self.mode.is_pyi else 2 | |
582 | if current_line.leaves: | |
583 | # Consume the first leaf's extra newlines. | |
584 | first_leaf = current_line.leaves[0] | |
585 | before = first_leaf.prefix.count("\n") | |
586 | before = min(before, max_allowed) | |
587 | first_leaf.prefix = "" | |
588 | else: | |
589 | before = 0 | |
590 | ||
591 | user_had_newline = bool(before) | |
592 | depth = current_line.depth | |
593 | ||
594 | previous_def = None | |
595 | while self.previous_defs and self.previous_defs[-1].depth >= depth: | |
596 | previous_def = self.previous_defs.pop() | |
597 | ||
598 | if previous_def is not None: | |
599 | assert self.previous_line is not None | |
600 | if self.mode.is_pyi: | |
601 | if depth and not current_line.is_def and self.previous_line.is_def: | |
602 | # Empty lines between attributes and methods should be preserved. | |
603 | before = 1 if user_had_newline else 0 | |
604 | elif ( | |
605 | Preview.blank_line_after_nested_stub_class in self.mode | |
606 | and previous_def.is_class | |
607 | and not previous_def.is_stub_class | |
608 | ): | |
609 | before = 1 | |
610 | elif depth: | |
611 | before = 0 | |
612 | else: | |
613 | before = 1 | |
614 | else: | |
615 | if depth: | |
616 | before = 1 | |
617 | elif ( | |
618 | not depth | |
619 | and previous_def.depth | |
620 | and current_line.leaves[-1].type == token.COLON | |
621 | and ( | |
622 | current_line.leaves[0].value | |
623 | not in ("with", "try", "for", "while", "if", "match") | |
624 | ) | |
625 | ): | |
626 | # We shouldn't add two newlines between an indented function and | |
627 | # a dependent non-indented clause. This is to avoid issues with | |
628 | # conditional function definitions that are technically top-level | |
629 | # and therefore get two trailing newlines, but look weird and | |
630 | # inconsistent when they're followed by elif, else, etc. This is | |
631 | # worse because these functions only get *one* preceding newline | |
632 | # already. | |
633 | before = 1 | |
634 | else: | |
635 | before = 2 | |
636 | ||
637 | if current_line.is_decorator or current_line.is_def or current_line.is_class: | |
638 | return self._maybe_empty_lines_for_class_or_def( | |
639 | current_line, before, user_had_newline | |
640 | ) | |
641 | ||
642 | if ( | |
643 | self.previous_line | |
644 | and self.previous_line.is_import | |
645 | and not current_line.is_import | |
646 | and not current_line.is_fmt_pass_converted(first_leaf_matches=is_import) | |
647 | and depth == self.previous_line.depth | |
648 | ): | |
649 | return (before or 1), 0 | |
650 | ||
651 | if ( | |
652 | self.previous_line | |
653 | and self.previous_line.is_class | |
654 | and current_line.is_triple_quoted_string | |
655 | ): | |
656 | if Preview.no_blank_line_before_class_docstring in current_line.mode: | |
657 | return 0, 1 | |
658 | return before, 1 | |
659 | ||
660 | if self.previous_line and self.previous_line.opens_block: | |
661 | return 0, 0 | |
662 | return before, 0 | |
663 | ||
664 | def _maybe_empty_lines_for_class_or_def( # noqa: C901 | |
665 | self, current_line: Line, before: int, user_had_newline: bool | |
666 | ) -> Tuple[int, int]: | |
667 | if not current_line.is_decorator: | |
668 | self.previous_defs.append(current_line) | |
669 | if self.previous_line is None: | |
670 | # Don't insert empty lines before the first line in the file. | |
671 | return 0, 0 | |
672 | ||
673 | if self.previous_line.is_decorator: | |
674 | if self.mode.is_pyi and current_line.is_stub_class: | |
675 | # Insert an empty line after a decorated stub class | |
676 | return 0, 1 | |
677 | ||
678 | return 0, 0 | |
679 | ||
680 | if self.previous_line.depth < current_line.depth and ( | |
681 | self.previous_line.is_class or self.previous_line.is_def | |
682 | ): | |
683 | return 0, 0 | |
684 | ||
685 | comment_to_add_newlines: Optional[LinesBlock] = None | |
686 | if ( | |
687 | self.previous_line.is_comment | |
688 | and self.previous_line.depth == current_line.depth | |
689 | and before == 0 | |
690 | ): | |
691 | slc = self.semantic_leading_comment | |
692 | if ( | |
693 | slc is not None | |
694 | and slc.previous_block is not None | |
695 | and not slc.previous_block.original_line.is_class | |
696 | and not slc.previous_block.original_line.opens_block | |
697 | and slc.before <= 1 | |
698 | ): | |
699 | comment_to_add_newlines = slc | |
700 | else: | |
701 | return 0, 0 | |
702 | ||
703 | if self.mode.is_pyi: | |
704 | if current_line.is_class or self.previous_line.is_class: | |
705 | if self.previous_line.depth < current_line.depth: | |
706 | newlines = 0 | |
707 | elif self.previous_line.depth > current_line.depth: | |
708 | newlines = 1 | |
709 | elif current_line.is_stub_class and self.previous_line.is_stub_class: | |
710 | # No blank line between classes with an empty body | |
711 | newlines = 0 | |
712 | else: | |
713 | newlines = 1 | |
714 | # Remove case `self.previous_line.depth > current_line.depth` below when | |
715 | # this becomes stable. | |
716 | # | |
717 | # Don't inspect the previous line if it's part of the body of the previous | |
718 | # statement in the same level, we always want a blank line if there's | |
719 | # something with a body preceding. | |
720 | elif ( | |
721 | Preview.blank_line_between_nested_and_def_stub_file in current_line.mode | |
722 | and self.previous_line.depth > current_line.depth | |
723 | ): | |
724 | newlines = 1 | |
725 | elif ( | |
726 | current_line.is_def or current_line.is_decorator | |
727 | ) and not self.previous_line.is_def: | |
728 | if current_line.depth: | |
729 | # In classes empty lines between attributes and methods should | |
730 | # be preserved. | |
731 | newlines = min(1, before) | |
732 | else: | |
733 | # Blank line between a block of functions (maybe with preceding | |
734 | # decorators) and a block of non-functions | |
735 | newlines = 1 | |
736 | elif self.previous_line.depth > current_line.depth: | |
737 | newlines = 1 | |
738 | else: | |
739 | newlines = 0 | |
740 | else: | |
741 | newlines = 1 if current_line.depth else 2 | |
742 | # If a user has left no space after a dummy implementation, don't insert | |
743 | # new lines. This is useful for instance for @overload or Protocols. | |
744 | if ( | |
745 | Preview.dummy_implementations in self.mode | |
746 | and self.previous_line.is_stub_def | |
747 | and not user_had_newline | |
748 | ): | |
749 | newlines = 0 | |
750 | if comment_to_add_newlines is not None: | |
751 | previous_block = comment_to_add_newlines.previous_block | |
752 | if previous_block is not None: | |
753 | comment_to_add_newlines.before = ( | |
754 | max(comment_to_add_newlines.before, newlines) - previous_block.after | |
755 | ) | |
756 | newlines = 0 | |
757 | return newlines, 0 | |
758 | ||
759 | ||
760 | def enumerate_reversed(sequence: Sequence[T]) -> Iterator[Tuple[Index, T]]: | |
761 | """Like `reversed(enumerate(sequence))` if that were possible.""" | |
762 | index = len(sequence) - 1 | |
763 | for element in reversed(sequence): | |
764 | yield (index, element) | |
765 | index -= 1 | |
766 | ||
767 | ||
768 | def append_leaves( | |
769 | new_line: Line, old_line: Line, leaves: List[Leaf], preformatted: bool = False | |
770 | ) -> None: | |
771 | """ | |
772 | Append leaves (taken from @old_line) to @new_line, making sure to fix the | |
773 | underlying Node structure where appropriate. | |
774 | ||
775 | All of the leaves in @leaves are duplicated. The duplicates are then | |
776 | appended to @new_line and used to replace their originals in the underlying | |
777 | Node structure. Any comments attached to the old leaves are reattached to | |
778 | the new leaves. | |
779 | ||
780 | Pre-conditions: | |
781 | set(@leaves) is a subset of set(@old_line.leaves). | |
782 | """ | |
783 | for old_leaf in leaves: | |
784 | new_leaf = Leaf(old_leaf.type, old_leaf.value) | |
785 | replace_child(old_leaf, new_leaf) | |
786 | new_line.append(new_leaf, preformatted=preformatted) | |
787 | ||
788 | for comment_leaf in old_line.comments_after(old_leaf): | |
789 | new_line.append(comment_leaf, preformatted=True) | |
790 | ||
791 | ||
792 | def is_line_short_enough( # noqa: C901 | |
793 | line: Line, *, mode: Mode, line_str: str = "" | |
794 | ) -> bool: | |
795 | """For non-multiline strings, return True if `line` is no longer than `line_length`. | |
796 | For multiline strings, looks at the context around `line` to determine | |
797 | if it should be inlined or split up. | |
798 | Uses the provided `line_str` rendering, if any, otherwise computes a new one. | |
799 | """ | |
800 | if not line_str: | |
801 | line_str = line_to_string(line) | |
802 | ||
803 | width = str_width if mode.preview else len | |
804 | ||
805 | if Preview.multiline_string_handling not in mode: | |
806 | return ( | |
807 | width(line_str) <= mode.line_length | |
808 | and "\n" not in line_str # multiline strings | |
809 | and not line.contains_standalone_comments() | |
810 | ) | |
811 | ||
812 | if line.contains_standalone_comments(): | |
813 | return False | |
814 | if "\n" not in line_str: | |
815 | # No multiline strings (MLS) present | |
816 | return width(line_str) <= mode.line_length | |
817 | ||
818 | first, *_, last = line_str.split("\n") | |
819 | if width(first) > mode.line_length or width(last) > mode.line_length: | |
820 | return False | |
821 | ||
822 | # Traverse the AST to examine the context of the multiline string (MLS), | |
823 | # tracking aspects such as depth and comma existence, | |
824 | # to determine whether to split the MLS or keep it together. | |
825 | # Depth (which is based on the existing bracket_depth concept) | |
826 | # is needed to determine nesting level of the MLS. | |
827 | # Includes special case for trailing commas. | |
828 | commas: List[int] = [] # tracks number of commas per depth level | |
829 | multiline_string: Optional[Leaf] = None | |
830 | # store the leaves that contain parts of the MLS | |
831 | multiline_string_contexts: List[LN] = [] | |
832 | ||
833 | max_level_to_update: Union[int, float] = math.inf # track the depth of the MLS | |
834 | for i, leaf in enumerate(line.leaves): | |
835 | if max_level_to_update == math.inf: | |
836 | had_comma: Optional[int] = None | |
837 | if leaf.bracket_depth + 1 > len(commas): | |
838 | commas.append(0) | |
839 | elif leaf.bracket_depth + 1 < len(commas): | |
840 | had_comma = commas.pop() | |
841 | if ( | |
842 | had_comma is not None | |
843 | and multiline_string is not None | |
844 | and multiline_string.bracket_depth == leaf.bracket_depth + 1 | |
845 | ): | |
846 | # Have left the level with the MLS, stop tracking commas | |
847 | max_level_to_update = leaf.bracket_depth | |
848 | if had_comma > 0: | |
849 | # MLS was in parens with at least one comma - force split | |
850 | return False | |
851 | ||
852 | if leaf.bracket_depth <= max_level_to_update and leaf.type == token.COMMA: | |
853 | # Ignore non-nested trailing comma | |
854 | # directly after MLS/MLS-containing expression | |
855 | ignore_ctxs: List[Optional[LN]] = [None] | |
856 | ignore_ctxs += multiline_string_contexts | |
857 | if not (leaf.prev_sibling in ignore_ctxs and i == len(line.leaves) - 1): | |
858 | commas[leaf.bracket_depth] += 1 | |
859 | if max_level_to_update != math.inf: | |
860 | max_level_to_update = min(max_level_to_update, leaf.bracket_depth) | |
861 | ||
862 | if is_multiline_string(leaf): | |
863 | if len(multiline_string_contexts) > 0: | |
864 | # >1 multiline string cannot fit on a single line - force split | |
865 | return False | |
866 | multiline_string = leaf | |
867 | ctx: LN = leaf | |
868 | # fetch the leaf components of the MLS in the AST | |
869 | while str(ctx) in line_str: | |
870 | multiline_string_contexts.append(ctx) | |
871 | if ctx.parent is None: | |
872 | break | |
873 | ctx = ctx.parent | |
874 | ||
875 | # May not have a triple-quoted multiline string at all, | |
876 | # in case of a regular string with embedded newlines and line continuations | |
877 | if len(multiline_string_contexts) == 0: | |
878 | return True | |
879 | ||
880 | return all(val == 0 for val in commas) | |
881 | ||
882 | ||
883 | def can_be_split(line: Line) -> bool: | |
884 | """Return False if the line cannot be split *for sure*. | |
885 | ||
886 | This is not an exhaustive search but a cheap heuristic that we can use to | |
887 | avoid some unfortunate formattings (mostly around wrapping unsplittable code | |
888 | in unnecessary parentheses). | |
889 | """ | |
890 | leaves = line.leaves | |
891 | if len(leaves) < 2: | |
892 | return False | |
893 | ||
894 | if leaves[0].type == token.STRING and leaves[1].type == token.DOT: | |
895 | call_count = 0 | |
896 | dot_count = 0 | |
897 | next = leaves[-1] | |
898 | for leaf in leaves[-2::-1]: | |
899 | if leaf.type in OPENING_BRACKETS: | |
900 | if next.type not in CLOSING_BRACKETS: | |
901 | return False | |
902 | ||
903 | call_count += 1 | |
904 | elif leaf.type == token.DOT: | |
905 | dot_count += 1 | |
906 | elif leaf.type == token.NAME: | |
907 | if not (next.type == token.DOT or next.type in OPENING_BRACKETS): | |
908 | return False | |
909 | ||
910 | elif leaf.type not in CLOSING_BRACKETS: | |
911 | return False | |
912 | ||
913 | if dot_count > 1 and call_count > 1: | |
914 | return False | |
915 | ||
916 | return True | |
917 | ||
918 | ||
919 | def can_omit_invisible_parens( | |
920 | rhs: RHSResult, | |
921 | line_length: int, | |
922 | ) -> bool: | |
923 | """Does `rhs.body` have a shape safe to reformat without optional parens around it? | |
924 | ||
925 | Returns True for only a subset of potentially nice looking formattings but | |
926 | the point is to not return false positives that end up producing lines that | |
927 | are too long. | |
928 | """ | |
929 | line = rhs.body | |
930 | bt = line.bracket_tracker | |
931 | if not bt.delimiters: | |
932 | # Without delimiters the optional parentheses are useless. | |
933 | return True | |
934 | ||
935 | max_priority = bt.max_delimiter_priority() | |
936 | delimiter_count = bt.delimiter_count_with_priority(max_priority) | |
937 | if delimiter_count > 1: | |
938 | # With more than one delimiter of a kind the optional parentheses read better. | |
939 | return False | |
940 | ||
941 | if delimiter_count == 1: | |
942 | if ( | |
943 | Preview.wrap_multiple_context_managers_in_parens in line.mode | |
944 | and max_priority == COMMA_PRIORITY | |
945 | and rhs.head.is_with_or_async_with_stmt | |
946 | ): | |
947 | # For two context manager with statements, the optional parentheses read | |
948 | # better. In this case, `rhs.body` is the context managers part of | |
949 | # the with statement. `rhs.head` is the `with (` part on the previous | |
950 | # line. | |
951 | return False | |
952 | # Otherwise it may also read better, but we don't do it today and requires | |
953 | # careful considerations for all possible cases. See | |
954 | # https://github.com/psf/black/issues/2156. | |
955 | ||
956 | if max_priority == DOT_PRIORITY: | |
957 | # A single stranded method call doesn't require optional parentheses. | |
958 | return True | |
959 | ||
960 | assert len(line.leaves) >= 2, "Stranded delimiter" | |
961 | ||
962 | # With a single delimiter, omit if the expression starts or ends with | |
963 | # a bracket. | |
964 | first = line.leaves[0] | |
965 | second = line.leaves[1] | |
966 | if first.type in OPENING_BRACKETS and second.type not in CLOSING_BRACKETS: | |
967 | if _can_omit_opening_paren(line, first=first, line_length=line_length): | |
968 | return True | |
969 | ||
970 | # Note: we are not returning False here because a line might have *both* | |
971 | # a leading opening bracket and a trailing closing bracket. If the | |
972 | # opening bracket doesn't match our rule, maybe the closing will. | |
973 | ||
974 | penultimate = line.leaves[-2] | |
975 | last = line.leaves[-1] | |
976 | ||
977 | if ( | |
978 | last.type == token.RPAR | |
979 | or last.type == token.RBRACE | |
980 | or ( | |
981 | # don't use indexing for omitting optional parentheses; | |
982 | # it looks weird | |
983 | last.type == token.RSQB | |
984 | and last.parent | |
985 | and last.parent.type != syms.trailer | |
986 | ) | |
987 | ): | |
988 | if penultimate.type in OPENING_BRACKETS: | |
989 | # Empty brackets don't help. | |
990 | return False | |
991 | ||
992 | if is_multiline_string(first): | |
993 | # Additional wrapping of a multiline string in this situation is | |
994 | # unnecessary. | |
995 | return True | |
996 | ||
997 | if _can_omit_closing_paren(line, last=last, line_length=line_length): | |
998 | return True | |
999 | ||
1000 | return False | |
1001 | ||
1002 | ||
1003 | def _can_omit_opening_paren(line: Line, *, first: Leaf, line_length: int) -> bool: | |
1004 | """See `can_omit_invisible_parens`.""" | |
1005 | remainder = False | |
1006 | length = 4 * line.depth | |
1007 | _index = -1 | |
1008 | for _index, leaf, leaf_length in line.enumerate_with_length(): | |
1009 | if leaf.type in CLOSING_BRACKETS and leaf.opening_bracket is first: | |
1010 | remainder = True | |
1011 | if remainder: | |
1012 | length += leaf_length | |
1013 | if length > line_length: | |
1014 | break | |
1015 | ||
1016 | if leaf.type in OPENING_BRACKETS: | |
1017 | # There are brackets we can further split on. | |
1018 | remainder = False | |
1019 | ||
1020 | else: | |
1021 | # checked the entire string and line length wasn't exceeded | |
1022 | if len(line.leaves) == _index + 1: | |
1023 | return True | |
1024 | ||
1025 | return False | |
1026 | ||
1027 | ||
1028 | def _can_omit_closing_paren(line: Line, *, last: Leaf, line_length: int) -> bool: | |
1029 | """See `can_omit_invisible_parens`.""" | |
1030 | length = 4 * line.depth | |
1031 | seen_other_brackets = False | |
1032 | for _index, leaf, leaf_length in line.enumerate_with_length(): | |
1033 | length += leaf_length | |
1034 | if leaf is last.opening_bracket: | |
1035 | if seen_other_brackets or length <= line_length: | |
1036 | return True | |
1037 | ||
1038 | elif leaf.type in OPENING_BRACKETS: | |
1039 | # There are brackets we can further split on. | |
1040 | seen_other_brackets = True | |
1041 | ||
1042 | return False | |
1043 | ||
1044 | ||
1045 | def line_to_string(line: Line) -> str: | |
1046 | """Returns the string representation of @line. | |
1047 | ||
1048 | WARNING: This is known to be computationally expensive. | |
1049 | """ | |
1050 | return str(line).strip("\n") |