]> git.madduck.net Git - etc/vim.git/blob - black.py

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

[blib2to3] Support non-ASCII identifiers
[etc/vim.git] / black.py
1 #!/usr/bin/env python3
2
3 import asyncio
4 from asyncio.base_events import BaseEventLoop
5 from concurrent.futures import Executor, ProcessPoolExecutor
6 from enum import Enum
7 from functools import partial, wraps
8 import keyword
9 import logging
10 from multiprocessing import Manager
11 import os
12 from pathlib import Path
13 import re
14 import tokenize
15 import signal
16 import sys
17 from typing import (
18     Any,
19     Callable,
20     Dict,
21     Generic,
22     Iterable,
23     Iterator,
24     List,
25     Optional,
26     Set,
27     Tuple,
28     Type,
29     TypeVar,
30     Union,
31 )
32
33 from attr import dataclass, Factory
34 import click
35
36 # lib2to3 fork
37 from blib2to3.pytree import Node, Leaf, type_repr
38 from blib2to3 import pygram, pytree
39 from blib2to3.pgen2 import driver, token
40 from blib2to3.pgen2.parse import ParseError
41
42 __version__ = "18.4a0"
43 DEFAULT_LINE_LENGTH = 88
44 # types
45 syms = pygram.python_symbols
46 FileContent = str
47 Encoding = str
48 Depth = int
49 NodeType = int
50 LeafID = int
51 Priority = int
52 Index = int
53 LN = Union[Leaf, Node]
54 SplitFunc = Callable[["Line", bool], Iterator["Line"]]
55 out = partial(click.secho, bold=True, err=True)
56 err = partial(click.secho, fg="red", err=True)
57
58
59 class NothingChanged(UserWarning):
60     """Raised by :func:`format_file` when reformatted code is the same as source."""
61
62
63 class CannotSplit(Exception):
64     """A readable split that fits the allotted line length is impossible.
65
66     Raised by :func:`left_hand_split`, :func:`right_hand_split`, and
67     :func:`delimiter_split`.
68     """
69
70
71 class FormatError(Exception):
72     """Base exception for `# fmt: on` and `# fmt: off` handling.
73
74     It holds the number of bytes of the prefix consumed before the format
75     control comment appeared.
76     """
77
78     def __init__(self, consumed: int) -> None:
79         super().__init__(consumed)
80         self.consumed = consumed
81
82     def trim_prefix(self, leaf: Leaf) -> None:
83         leaf.prefix = leaf.prefix[self.consumed:]
84
85     def leaf_from_consumed(self, leaf: Leaf) -> Leaf:
86         """Returns a new Leaf from the consumed part of the prefix."""
87         unformatted_prefix = leaf.prefix[:self.consumed]
88         return Leaf(token.NEWLINE, unformatted_prefix)
89
90
91 class FormatOn(FormatError):
92     """Found a comment like `# fmt: on` in the file."""
93
94
95 class FormatOff(FormatError):
96     """Found a comment like `# fmt: off` in the file."""
97
98
99 class WriteBack(Enum):
100     NO = 0
101     YES = 1
102     DIFF = 2
103
104
105 @click.command()
106 @click.option(
107     "-l",
108     "--line-length",
109     type=int,
110     default=DEFAULT_LINE_LENGTH,
111     help="How many character per line to allow.",
112     show_default=True,
113 )
114 @click.option(
115     "--check",
116     is_flag=True,
117     help=(
118         "Don't write the files back, just return the status.  Return code 0 "
119         "means nothing would change.  Return code 1 means some files would be "
120         "reformatted.  Return code 123 means there was an internal error."
121     ),
122 )
123 @click.option(
124     "--diff",
125     is_flag=True,
126     help="Don't write the files back, just output a diff for each file on stdout.",
127 )
128 @click.option(
129     "--fast/--safe",
130     is_flag=True,
131     help="If --fast given, skip temporary sanity checks. [default: --safe]",
132 )
133 @click.option(
134     "-q",
135     "--quiet",
136     is_flag=True,
137     help=(
138         "Don't emit non-error messages to stderr. Errors are still emitted, "
139         "silence those with 2>/dev/null."
140     ),
141 )
142 @click.version_option(version=__version__)
143 @click.argument(
144     "src",
145     nargs=-1,
146     type=click.Path(
147         exists=True, file_okay=True, dir_okay=True, readable=True, allow_dash=True
148     ),
149 )
150 @click.pass_context
151 def main(
152     ctx: click.Context,
153     line_length: int,
154     check: bool,
155     diff: bool,
156     fast: bool,
157     quiet: bool,
158     src: List[str],
159 ) -> None:
160     """The uncompromising code formatter."""
161     sources: List[Path] = []
162     for s in src:
163         p = Path(s)
164         if p.is_dir():
165             sources.extend(gen_python_files_in_dir(p))
166         elif p.is_file():
167             # if a file was explicitly given, we don't care about its extension
168             sources.append(p)
169         elif s == "-":
170             sources.append(Path("-"))
171         else:
172             err(f"invalid path: {s}")
173     if check and diff:
174         exc = click.ClickException("Options --check and --diff are mutually exclusive")
175         exc.exit_code = 2
176         raise exc
177
178     if check:
179         write_back = WriteBack.NO
180     elif diff:
181         write_back = WriteBack.DIFF
182     else:
183         write_back = WriteBack.YES
184     if len(sources) == 0:
185         ctx.exit(0)
186     elif len(sources) == 1:
187         p = sources[0]
188         report = Report(check=check, quiet=quiet)
189         try:
190             if not p.is_file() and str(p) == "-":
191                 changed = format_stdin_to_stdout(
192                     line_length=line_length, fast=fast, write_back=write_back
193                 )
194             else:
195                 changed = format_file_in_place(
196                     p, line_length=line_length, fast=fast, write_back=write_back
197                 )
198             report.done(p, changed)
199         except Exception as exc:
200             report.failed(p, str(exc))
201         ctx.exit(report.return_code)
202     else:
203         loop = asyncio.get_event_loop()
204         executor = ProcessPoolExecutor(max_workers=os.cpu_count())
205         return_code = 1
206         try:
207             return_code = loop.run_until_complete(
208                 schedule_formatting(
209                     sources, line_length, write_back, fast, quiet, loop, executor
210                 )
211             )
212         finally:
213             shutdown(loop)
214             ctx.exit(return_code)
215
216
217 async def schedule_formatting(
218     sources: List[Path],
219     line_length: int,
220     write_back: WriteBack,
221     fast: bool,
222     quiet: bool,
223     loop: BaseEventLoop,
224     executor: Executor,
225 ) -> int:
226     """Run formatting of `sources` in parallel using the provided `executor`.
227
228     (Use ProcessPoolExecutors for actual parallelism.)
229
230     `line_length`, `write_back`, and `fast` options are passed to
231     :func:`format_file_in_place`.
232     """
233     lock = None
234     if write_back == WriteBack.DIFF:
235         # For diff output, we need locks to ensure we don't interleave output
236         # from different processes.
237         manager = Manager()
238         lock = manager.Lock()
239     tasks = {
240         src: loop.run_in_executor(
241             executor, format_file_in_place, src, line_length, fast, write_back, lock
242         )
243         for src in sources
244     }
245     _task_values = list(tasks.values())
246     loop.add_signal_handler(signal.SIGINT, cancel, _task_values)
247     loop.add_signal_handler(signal.SIGTERM, cancel, _task_values)
248     await asyncio.wait(tasks.values())
249     cancelled = []
250     report = Report(check=write_back is WriteBack.NO, quiet=quiet)
251     for src, task in tasks.items():
252         if not task.done():
253             report.failed(src, "timed out, cancelling")
254             task.cancel()
255             cancelled.append(task)
256         elif task.cancelled():
257             cancelled.append(task)
258         elif task.exception():
259             report.failed(src, str(task.exception()))
260         else:
261             report.done(src, task.result())
262     if cancelled:
263         await asyncio.gather(*cancelled, loop=loop, return_exceptions=True)
264     elif not quiet:
265         out("All done! ✨ 🍰 ✨")
266     if not quiet:
267         click.echo(str(report))
268     return report.return_code
269
270
271 def format_file_in_place(
272     src: Path,
273     line_length: int,
274     fast: bool,
275     write_back: WriteBack = WriteBack.NO,
276     lock: Any = None,  # multiprocessing.Manager().Lock() is some crazy proxy
277 ) -> bool:
278     """Format file under `src` path. Return True if changed.
279
280     If `write_back` is True, write reformatted code back to stdout.
281     `line_length` and `fast` options are passed to :func:`format_file_contents`.
282     """
283     with tokenize.open(src) as src_buffer:
284         src_contents = src_buffer.read()
285     try:
286         dst_contents = format_file_contents(
287             src_contents, line_length=line_length, fast=fast
288         )
289     except NothingChanged:
290         return False
291
292     if write_back == write_back.YES:
293         with open(src, "w", encoding=src_buffer.encoding) as f:
294             f.write(dst_contents)
295     elif write_back == write_back.DIFF:
296         src_name = f"{src.name}  (original)"
297         dst_name = f"{src.name}  (formatted)"
298         diff_contents = diff(src_contents, dst_contents, src_name, dst_name)
299         if lock:
300             lock.acquire()
301         try:
302             sys.stdout.write(diff_contents)
303         finally:
304             if lock:
305                 lock.release()
306     return True
307
308
309 def format_stdin_to_stdout(
310     line_length: int, fast: bool, write_back: WriteBack = WriteBack.NO
311 ) -> bool:
312     """Format file on stdin. Return True if changed.
313
314     If `write_back` is True, write reformatted code back to stdout.
315     `line_length` and `fast` arguments are passed to :func:`format_file_contents`.
316     """
317     src = sys.stdin.read()
318     try:
319         dst = format_file_contents(src, line_length=line_length, fast=fast)
320         return True
321
322     except NothingChanged:
323         dst = src
324         return False
325
326     finally:
327         if write_back == WriteBack.YES:
328             sys.stdout.write(dst)
329         elif write_back == WriteBack.DIFF:
330             src_name = "<stdin>  (original)"
331             dst_name = "<stdin>  (formatted)"
332             sys.stdout.write(diff(src, dst, src_name, dst_name))
333
334
335 def format_file_contents(
336     src_contents: str, line_length: int, fast: bool
337 ) -> FileContent:
338     """Reformat contents a file and return new contents.
339
340     If `fast` is False, additionally confirm that the reformatted code is
341     valid by calling :func:`assert_equivalent` and :func:`assert_stable` on it.
342     `line_length` is passed to :func:`format_str`.
343     """
344     if src_contents.strip() == "":
345         raise NothingChanged
346
347     dst_contents = format_str(src_contents, line_length=line_length)
348     if src_contents == dst_contents:
349         raise NothingChanged
350
351     if not fast:
352         assert_equivalent(src_contents, dst_contents)
353         assert_stable(src_contents, dst_contents, line_length=line_length)
354     return dst_contents
355
356
357 def format_str(src_contents: str, line_length: int) -> FileContent:
358     """Reformat a string and return new contents.
359
360     `line_length` determines how many characters per line are allowed.
361     """
362     src_node = lib2to3_parse(src_contents)
363     dst_contents = ""
364     lines = LineGenerator()
365     elt = EmptyLineTracker()
366     py36 = is_python36(src_node)
367     empty_line = Line()
368     after = 0
369     for current_line in lines.visit(src_node):
370         for _ in range(after):
371             dst_contents += str(empty_line)
372         before, after = elt.maybe_empty_lines(current_line)
373         for _ in range(before):
374             dst_contents += str(empty_line)
375         for line in split_line(current_line, line_length=line_length, py36=py36):
376             dst_contents += str(line)
377     return dst_contents
378
379
380 GRAMMARS = [
381     pygram.python_grammar_no_print_statement_no_exec_statement,
382     pygram.python_grammar_no_print_statement,
383     pygram.python_grammar_no_exec_statement,
384     pygram.python_grammar,
385 ]
386
387
388 def lib2to3_parse(src_txt: str) -> Node:
389     """Given a string with source, return the lib2to3 Node."""
390     grammar = pygram.python_grammar_no_print_statement
391     if src_txt[-1] != "\n":
392         nl = "\r\n" if "\r\n" in src_txt[:1024] else "\n"
393         src_txt += nl
394     for grammar in GRAMMARS:
395         drv = driver.Driver(grammar, pytree.convert)
396         try:
397             result = drv.parse_string(src_txt, True)
398             break
399
400         except ParseError as pe:
401             lineno, column = pe.context[1]
402             lines = src_txt.splitlines()
403             try:
404                 faulty_line = lines[lineno - 1]
405             except IndexError:
406                 faulty_line = "<line number missing in source>"
407             exc = ValueError(f"Cannot parse: {lineno}:{column}: {faulty_line}")
408     else:
409         raise exc from None
410
411     if isinstance(result, Leaf):
412         result = Node(syms.file_input, [result])
413     return result
414
415
416 def lib2to3_unparse(node: Node) -> str:
417     """Given a lib2to3 node, return its string representation."""
418     code = str(node)
419     return code
420
421
422 T = TypeVar("T")
423
424
425 class Visitor(Generic[T]):
426     """Basic lib2to3 visitor that yields things of type `T` on `visit()`."""
427
428     def visit(self, node: LN) -> Iterator[T]:
429         """Main method to visit `node` and its children.
430
431         It tries to find a `visit_*()` method for the given `node.type`, like
432         `visit_simple_stmt` for Node objects or `visit_INDENT` for Leaf objects.
433         If no dedicated `visit_*()` method is found, chooses `visit_default()`
434         instead.
435
436         Then yields objects of type `T` from the selected visitor.
437         """
438         if node.type < 256:
439             name = token.tok_name[node.type]
440         else:
441             name = type_repr(node.type)
442         yield from getattr(self, f"visit_{name}", self.visit_default)(node)
443
444     def visit_default(self, node: LN) -> Iterator[T]:
445         """Default `visit_*()` implementation. Recurses to children of `node`."""
446         if isinstance(node, Node):
447             for child in node.children:
448                 yield from self.visit(child)
449
450
451 @dataclass
452 class DebugVisitor(Visitor[T]):
453     tree_depth: int = 0
454
455     def visit_default(self, node: LN) -> Iterator[T]:
456         indent = " " * (2 * self.tree_depth)
457         if isinstance(node, Node):
458             _type = type_repr(node.type)
459             out(f"{indent}{_type}", fg="yellow")
460             self.tree_depth += 1
461             for child in node.children:
462                 yield from self.visit(child)
463
464             self.tree_depth -= 1
465             out(f"{indent}/{_type}", fg="yellow", bold=False)
466         else:
467             _type = token.tok_name.get(node.type, str(node.type))
468             out(f"{indent}{_type}", fg="blue", nl=False)
469             if node.prefix:
470                 # We don't have to handle prefixes for `Node` objects since
471                 # that delegates to the first child anyway.
472                 out(f" {node.prefix!r}", fg="green", bold=False, nl=False)
473             out(f" {node.value!r}", fg="blue", bold=False)
474
475     @classmethod
476     def show(cls, code: str) -> None:
477         """Pretty-print the lib2to3 AST of a given string of `code`.
478
479         Convenience method for debugging.
480         """
481         v: DebugVisitor[None] = DebugVisitor()
482         list(v.visit(lib2to3_parse(code)))
483
484
485 KEYWORDS = set(keyword.kwlist)
486 WHITESPACE = {token.DEDENT, token.INDENT, token.NEWLINE}
487 FLOW_CONTROL = {"return", "raise", "break", "continue"}
488 STATEMENT = {
489     syms.if_stmt,
490     syms.while_stmt,
491     syms.for_stmt,
492     syms.try_stmt,
493     syms.except_clause,
494     syms.with_stmt,
495     syms.funcdef,
496     syms.classdef,
497 }
498 STANDALONE_COMMENT = 153
499 LOGIC_OPERATORS = {"and", "or"}
500 COMPARATORS = {
501     token.LESS,
502     token.GREATER,
503     token.EQEQUAL,
504     token.NOTEQUAL,
505     token.LESSEQUAL,
506     token.GREATEREQUAL,
507 }
508 MATH_OPERATORS = {
509     token.PLUS,
510     token.MINUS,
511     token.STAR,
512     token.SLASH,
513     token.VBAR,
514     token.AMPER,
515     token.PERCENT,
516     token.CIRCUMFLEX,
517     token.TILDE,
518     token.LEFTSHIFT,
519     token.RIGHTSHIFT,
520     token.DOUBLESTAR,
521     token.DOUBLESLASH,
522 }
523 VARARGS = {token.STAR, token.DOUBLESTAR}
524 COMPREHENSION_PRIORITY = 20
525 COMMA_PRIORITY = 10
526 LOGIC_PRIORITY = 5
527 STRING_PRIORITY = 4
528 COMPARATOR_PRIORITY = 3
529 MATH_PRIORITY = 1
530
531
532 @dataclass
533 class BracketTracker:
534     """Keeps track of brackets on a line."""
535
536     depth: int = 0
537     bracket_match: Dict[Tuple[Depth, NodeType], Leaf] = Factory(dict)
538     delimiters: Dict[LeafID, Priority] = Factory(dict)
539     previous: Optional[Leaf] = None
540
541     def mark(self, leaf: Leaf) -> None:
542         """Mark `leaf` with bracket-related metadata. Keep track of delimiters.
543
544         All leaves receive an int `bracket_depth` field that stores how deep
545         within brackets a given leaf is. 0 means there are no enclosing brackets
546         that started on this line.
547
548         If a leaf is itself a closing bracket, it receives an `opening_bracket`
549         field that it forms a pair with. This is a one-directional link to
550         avoid reference cycles.
551
552         If a leaf is a delimiter (a token on which Black can split the line if
553         needed) and it's on depth 0, its `id()` is stored in the tracker's
554         `delimiters` field.
555         """
556         if leaf.type == token.COMMENT:
557             return
558
559         if leaf.type in CLOSING_BRACKETS:
560             self.depth -= 1
561             opening_bracket = self.bracket_match.pop((self.depth, leaf.type))
562             leaf.opening_bracket = opening_bracket
563         leaf.bracket_depth = self.depth
564         if self.depth == 0:
565             delim = is_split_before_delimiter(leaf, self.previous)
566             if delim and self.previous is not None:
567                 self.delimiters[id(self.previous)] = delim
568             else:
569                 delim = is_split_after_delimiter(leaf, self.previous)
570                 if delim:
571                     self.delimiters[id(leaf)] = delim
572         if leaf.type in OPENING_BRACKETS:
573             self.bracket_match[self.depth, BRACKET[leaf.type]] = leaf
574             self.depth += 1
575         self.previous = leaf
576
577     def any_open_brackets(self) -> bool:
578         """Return True if there is an yet unmatched open bracket on the line."""
579         return bool(self.bracket_match)
580
581     def max_delimiter_priority(self, exclude: Iterable[LeafID] = ()) -> int:
582         """Return the highest priority of a delimiter found on the line.
583
584         Values are consistent with what `is_delimiter()` returns.
585         """
586         return max(v for k, v in self.delimiters.items() if k not in exclude)
587
588
589 @dataclass
590 class Line:
591     """Holds leaves and comments. Can be printed with `str(line)`."""
592
593     depth: int = 0
594     leaves: List[Leaf] = Factory(list)
595     comments: List[Tuple[Index, Leaf]] = Factory(list)
596     bracket_tracker: BracketTracker = Factory(BracketTracker)
597     inside_brackets: bool = False
598     has_for: bool = False
599     _for_loop_variable: bool = False
600
601     def append(self, leaf: Leaf, preformatted: bool = False) -> None:
602         """Add a new `leaf` to the end of the line.
603
604         Unless `preformatted` is True, the `leaf` will receive a new consistent
605         whitespace prefix and metadata applied by :class:`BracketTracker`.
606         Trailing commas are maybe removed, unpacked for loop variables are
607         demoted from being delimiters.
608
609         Inline comments are put aside.
610         """
611         has_value = leaf.value.strip()
612         if not has_value:
613             return
614
615         if self.leaves and not preformatted:
616             # Note: at this point leaf.prefix should be empty except for
617             # imports, for which we only preserve newlines.
618             leaf.prefix += whitespace(leaf)
619         if self.inside_brackets or not preformatted:
620             self.maybe_decrement_after_for_loop_variable(leaf)
621             self.bracket_tracker.mark(leaf)
622             self.maybe_remove_trailing_comma(leaf)
623             self.maybe_increment_for_loop_variable(leaf)
624
625         if not self.append_comment(leaf):
626             self.leaves.append(leaf)
627
628     def append_safe(self, leaf: Leaf, preformatted: bool = False) -> None:
629         """Like :func:`append()` but disallow invalid standalone comment structure.
630
631         Raises ValueError when any `leaf` is appended after a standalone comment
632         or when a standalone comment is not the first leaf on the line.
633         """
634         if self.bracket_tracker.depth == 0:
635             if self.is_comment:
636                 raise ValueError("cannot append to standalone comments")
637
638             if self.leaves and leaf.type == STANDALONE_COMMENT:
639                 raise ValueError(
640                     "cannot append standalone comments to a populated line"
641                 )
642
643         self.append(leaf, preformatted=preformatted)
644
645     @property
646     def is_comment(self) -> bool:
647         """Is this line a standalone comment?"""
648         return len(self.leaves) == 1 and self.leaves[0].type == STANDALONE_COMMENT
649
650     @property
651     def is_decorator(self) -> bool:
652         """Is this line a decorator?"""
653         return bool(self) and self.leaves[0].type == token.AT
654
655     @property
656     def is_import(self) -> bool:
657         """Is this an import line?"""
658         return bool(self) and is_import(self.leaves[0])
659
660     @property
661     def is_class(self) -> bool:
662         """Is this line a class definition?"""
663         return (
664             bool(self)
665             and self.leaves[0].type == token.NAME
666             and self.leaves[0].value == "class"
667         )
668
669     @property
670     def is_def(self) -> bool:
671         """Is this a function definition? (Also returns True for async defs.)"""
672         try:
673             first_leaf = self.leaves[0]
674         except IndexError:
675             return False
676
677         try:
678             second_leaf: Optional[Leaf] = self.leaves[1]
679         except IndexError:
680             second_leaf = None
681         return (
682             (first_leaf.type == token.NAME and first_leaf.value == "def")
683             or (
684                 first_leaf.type == token.ASYNC
685                 and second_leaf is not None
686                 and second_leaf.type == token.NAME
687                 and second_leaf.value == "def"
688             )
689         )
690
691     @property
692     def is_flow_control(self) -> bool:
693         """Is this line a flow control statement?
694
695         Those are `return`, `raise`, `break`, and `continue`.
696         """
697         return (
698             bool(self)
699             and self.leaves[0].type == token.NAME
700             and self.leaves[0].value in FLOW_CONTROL
701         )
702
703     @property
704     def is_yield(self) -> bool:
705         """Is this line a yield statement?"""
706         return (
707             bool(self)
708             and self.leaves[0].type == token.NAME
709             and self.leaves[0].value == "yield"
710         )
711
712     @property
713     def contains_standalone_comments(self) -> bool:
714         """If so, needs to be split before emitting."""
715         for leaf in self.leaves:
716             if leaf.type == STANDALONE_COMMENT:
717                 return True
718
719         return False
720
721     def maybe_remove_trailing_comma(self, closing: Leaf) -> bool:
722         """Remove trailing comma if there is one and it's safe."""
723         if not (
724             self.leaves
725             and self.leaves[-1].type == token.COMMA
726             and closing.type in CLOSING_BRACKETS
727         ):
728             return False
729
730         if closing.type == token.RBRACE:
731             self.remove_trailing_comma()
732             return True
733
734         if closing.type == token.RSQB:
735             comma = self.leaves[-1]
736             if comma.parent and comma.parent.type == syms.listmaker:
737                 self.remove_trailing_comma()
738                 return True
739
740         # For parens let's check if it's safe to remove the comma.  If the
741         # trailing one is the only one, we might mistakenly change a tuple
742         # into a different type by removing the comma.
743         depth = closing.bracket_depth + 1
744         commas = 0
745         opening = closing.opening_bracket
746         for _opening_index, leaf in enumerate(self.leaves):
747             if leaf is opening:
748                 break
749
750         else:
751             return False
752
753         for leaf in self.leaves[_opening_index + 1:]:
754             if leaf is closing:
755                 break
756
757             bracket_depth = leaf.bracket_depth
758             if bracket_depth == depth and leaf.type == token.COMMA:
759                 commas += 1
760                 if leaf.parent and leaf.parent.type == syms.arglist:
761                     commas += 1
762                     break
763
764         if commas > 1:
765             self.remove_trailing_comma()
766             return True
767
768         return False
769
770     def maybe_increment_for_loop_variable(self, leaf: Leaf) -> bool:
771         """In a for loop, or comprehension, the variables are often unpacks.
772
773         To avoid splitting on the comma in this situation, increase the depth of
774         tokens between `for` and `in`.
775         """
776         if leaf.type == token.NAME and leaf.value == "for":
777             self.has_for = True
778             self.bracket_tracker.depth += 1
779             self._for_loop_variable = True
780             return True
781
782         return False
783
784     def maybe_decrement_after_for_loop_variable(self, leaf: Leaf) -> bool:
785         """See `maybe_increment_for_loop_variable` above for explanation."""
786         if self._for_loop_variable and leaf.type == token.NAME and leaf.value == "in":
787             self.bracket_tracker.depth -= 1
788             self._for_loop_variable = False
789             return True
790
791         return False
792
793     def append_comment(self, comment: Leaf) -> bool:
794         """Add an inline or standalone comment to the line."""
795         if (
796             comment.type == STANDALONE_COMMENT
797             and self.bracket_tracker.any_open_brackets()
798         ):
799             comment.prefix = ""
800             return False
801
802         if comment.type != token.COMMENT:
803             return False
804
805         after = len(self.leaves) - 1
806         if after == -1:
807             comment.type = STANDALONE_COMMENT
808             comment.prefix = ""
809             return False
810
811         else:
812             self.comments.append((after, comment))
813             return True
814
815     def comments_after(self, leaf: Leaf) -> Iterator[Leaf]:
816         """Generate comments that should appear directly after `leaf`."""
817         for _leaf_index, _leaf in enumerate(self.leaves):
818             if leaf is _leaf:
819                 break
820
821         else:
822             return
823
824         for index, comment_after in self.comments:
825             if _leaf_index == index:
826                 yield comment_after
827
828     def remove_trailing_comma(self) -> None:
829         """Remove the trailing comma and moves the comments attached to it."""
830         comma_index = len(self.leaves) - 1
831         for i in range(len(self.comments)):
832             comment_index, comment = self.comments[i]
833             if comment_index == comma_index:
834                 self.comments[i] = (comma_index - 1, comment)
835         self.leaves.pop()
836
837     def __str__(self) -> str:
838         """Render the line."""
839         if not self:
840             return "\n"
841
842         indent = "    " * self.depth
843         leaves = iter(self.leaves)
844         first = next(leaves)
845         res = f"{first.prefix}{indent}{first.value}"
846         for leaf in leaves:
847             res += str(leaf)
848         for _, comment in self.comments:
849             res += str(comment)
850         return res + "\n"
851
852     def __bool__(self) -> bool:
853         """Return True if the line has leaves or comments."""
854         return bool(self.leaves or self.comments)
855
856
857 class UnformattedLines(Line):
858     """Just like :class:`Line` but stores lines which aren't reformatted."""
859
860     def append(self, leaf: Leaf, preformatted: bool = True) -> None:
861         """Just add a new `leaf` to the end of the lines.
862
863         The `preformatted` argument is ignored.
864
865         Keeps track of indentation `depth`, which is useful when the user
866         says `# fmt: on`. Otherwise, doesn't do anything with the `leaf`.
867         """
868         try:
869             list(generate_comments(leaf))
870         except FormatOn as f_on:
871             self.leaves.append(f_on.leaf_from_consumed(leaf))
872             raise
873
874         self.leaves.append(leaf)
875         if leaf.type == token.INDENT:
876             self.depth += 1
877         elif leaf.type == token.DEDENT:
878             self.depth -= 1
879
880     def __str__(self) -> str:
881         """Render unformatted lines from leaves which were added with `append()`.
882
883         `depth` is not used for indentation in this case.
884         """
885         if not self:
886             return "\n"
887
888         res = ""
889         for leaf in self.leaves:
890             res += str(leaf)
891         return res
892
893     def append_comment(self, comment: Leaf) -> bool:
894         """Not implemented in this class. Raises `NotImplementedError`."""
895         raise NotImplementedError("Unformatted lines don't store comments separately.")
896
897     def maybe_remove_trailing_comma(self, closing: Leaf) -> bool:
898         """Does nothing and returns False."""
899         return False
900
901     def maybe_increment_for_loop_variable(self, leaf: Leaf) -> bool:
902         """Does nothing and returns False."""
903         return False
904
905
906 @dataclass
907 class EmptyLineTracker:
908     """Provides a stateful method that returns the number of potential extra
909     empty lines needed before and after the currently processed line.
910
911     Note: this tracker works on lines that haven't been split yet.  It assumes
912     the prefix of the first leaf consists of optional newlines.  Those newlines
913     are consumed by `maybe_empty_lines()` and included in the computation.
914     """
915     previous_line: Optional[Line] = None
916     previous_after: int = 0
917     previous_defs: List[int] = Factory(list)
918
919     def maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
920         """Return the number of extra empty lines before and after the `current_line`.
921
922         This is for separating `def`, `async def` and `class` with extra empty
923         lines (two on module-level), as well as providing an extra empty line
924         after flow control keywords to make them more prominent.
925         """
926         if isinstance(current_line, UnformattedLines):
927             return 0, 0
928
929         before, after = self._maybe_empty_lines(current_line)
930         before -= self.previous_after
931         self.previous_after = after
932         self.previous_line = current_line
933         return before, after
934
935     def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
936         max_allowed = 1
937         if current_line.depth == 0:
938             max_allowed = 2
939         if current_line.leaves:
940             # Consume the first leaf's extra newlines.
941             first_leaf = current_line.leaves[0]
942             before = first_leaf.prefix.count("\n")
943             before = min(before, max_allowed)
944             first_leaf.prefix = ""
945         else:
946             before = 0
947         depth = current_line.depth
948         while self.previous_defs and self.previous_defs[-1] >= depth:
949             self.previous_defs.pop()
950             before = 1 if depth else 2
951         is_decorator = current_line.is_decorator
952         if is_decorator or current_line.is_def or current_line.is_class:
953             if not is_decorator:
954                 self.previous_defs.append(depth)
955             if self.previous_line is None:
956                 # Don't insert empty lines before the first line in the file.
957                 return 0, 0
958
959             if self.previous_line and self.previous_line.is_decorator:
960                 # Don't insert empty lines between decorators.
961                 return 0, 0
962
963             newlines = 2
964             if current_line.depth:
965                 newlines -= 1
966             return newlines, 0
967
968         if current_line.is_flow_control:
969             return before, 1
970
971         if (
972             self.previous_line
973             and self.previous_line.is_import
974             and not current_line.is_import
975             and depth == self.previous_line.depth
976         ):
977             return (before or 1), 0
978
979         if (
980             self.previous_line
981             and self.previous_line.is_yield
982             and (not current_line.is_yield or depth != self.previous_line.depth)
983         ):
984             return (before or 1), 0
985
986         return before, 0
987
988
989 @dataclass
990 class LineGenerator(Visitor[Line]):
991     """Generates reformatted Line objects.  Empty lines are not emitted.
992
993     Note: destroys the tree it's visiting by mutating prefixes of its leaves
994     in ways that will no longer stringify to valid Python code on the tree.
995     """
996     current_line: Line = Factory(Line)
997
998     def line(self, indent: int = 0, type: Type[Line] = Line) -> Iterator[Line]:
999         """Generate a line.
1000
1001         If the line is empty, only emit if it makes sense.
1002         If the line is too long, split it first and then generate.
1003
1004         If any lines were generated, set up a new current_line.
1005         """
1006         if not self.current_line:
1007             if self.current_line.__class__ == type:
1008                 self.current_line.depth += indent
1009             else:
1010                 self.current_line = type(depth=self.current_line.depth + indent)
1011             return  # Line is empty, don't emit. Creating a new one unnecessary.
1012
1013         complete_line = self.current_line
1014         self.current_line = type(depth=complete_line.depth + indent)
1015         yield complete_line
1016
1017     def visit(self, node: LN) -> Iterator[Line]:
1018         """Main method to visit `node` and its children.
1019
1020         Yields :class:`Line` objects.
1021         """
1022         if isinstance(self.current_line, UnformattedLines):
1023             # File contained `# fmt: off`
1024             yield from self.visit_unformatted(node)
1025
1026         else:
1027             yield from super().visit(node)
1028
1029     def visit_default(self, node: LN) -> Iterator[Line]:
1030         """Default `visit_*()` implementation. Recurses to children of `node`."""
1031         if isinstance(node, Leaf):
1032             any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
1033             try:
1034                 for comment in generate_comments(node):
1035                     if any_open_brackets:
1036                         # any comment within brackets is subject to splitting
1037                         self.current_line.append(comment)
1038                     elif comment.type == token.COMMENT:
1039                         # regular trailing comment
1040                         self.current_line.append(comment)
1041                         yield from self.line()
1042
1043                     else:
1044                         # regular standalone comment
1045                         yield from self.line()
1046
1047                         self.current_line.append(comment)
1048                         yield from self.line()
1049
1050             except FormatOff as f_off:
1051                 f_off.trim_prefix(node)
1052                 yield from self.line(type=UnformattedLines)
1053                 yield from self.visit(node)
1054
1055             except FormatOn as f_on:
1056                 # This only happens here if somebody says "fmt: on" multiple
1057                 # times in a row.
1058                 f_on.trim_prefix(node)
1059                 yield from self.visit_default(node)
1060
1061             else:
1062                 normalize_prefix(node, inside_brackets=any_open_brackets)
1063                 if node.type == token.STRING:
1064                     normalize_string_quotes(node)
1065                 if node.type not in WHITESPACE:
1066                     self.current_line.append(node)
1067         yield from super().visit_default(node)
1068
1069     def visit_INDENT(self, node: Node) -> Iterator[Line]:
1070         """Increase indentation level, maybe yield a line."""
1071         # In blib2to3 INDENT never holds comments.
1072         yield from self.line(+1)
1073         yield from self.visit_default(node)
1074
1075     def visit_DEDENT(self, node: Node) -> Iterator[Line]:
1076         """Decrease indentation level, maybe yield a line."""
1077         # DEDENT has no value. Additionally, in blib2to3 it never holds comments.
1078         yield from self.line(-1)
1079
1080     def visit_stmt(self, node: Node, keywords: Set[str]) -> Iterator[Line]:
1081         """Visit a statement.
1082
1083         This implementation is shared for `if`, `while`, `for`, `try`, `except`,
1084         `def`, `with`, and `class`.
1085
1086         The relevant Python language `keywords` for a given statement will be NAME
1087         leaves within it. This methods puts those on a separate line.
1088         """
1089         for child in node.children:
1090             if child.type == token.NAME and child.value in keywords:  # type: ignore
1091                 yield from self.line()
1092
1093             yield from self.visit(child)
1094
1095     def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
1096         """Visit a statement without nested statements."""
1097         is_suite_like = node.parent and node.parent.type in STATEMENT
1098         if is_suite_like:
1099             yield from self.line(+1)
1100             yield from self.visit_default(node)
1101             yield from self.line(-1)
1102
1103         else:
1104             yield from self.line()
1105             yield from self.visit_default(node)
1106
1107     def visit_async_stmt(self, node: Node) -> Iterator[Line]:
1108         """Visit `async def`, `async for`, `async with`."""
1109         yield from self.line()
1110
1111         children = iter(node.children)
1112         for child in children:
1113             yield from self.visit(child)
1114
1115             if child.type == token.ASYNC:
1116                 break
1117
1118         internal_stmt = next(children)
1119         for child in internal_stmt.children:
1120             yield from self.visit(child)
1121
1122     def visit_decorators(self, node: Node) -> Iterator[Line]:
1123         """Visit decorators."""
1124         for child in node.children:
1125             yield from self.line()
1126             yield from self.visit(child)
1127
1128     def visit_SEMI(self, leaf: Leaf) -> Iterator[Line]:
1129         """Remove a semicolon and put the other statement on a separate line."""
1130         yield from self.line()
1131
1132     def visit_ENDMARKER(self, leaf: Leaf) -> Iterator[Line]:
1133         """End of file. Process outstanding comments and end with a newline."""
1134         yield from self.visit_default(leaf)
1135         yield from self.line()
1136
1137     def visit_unformatted(self, node: LN) -> Iterator[Line]:
1138         """Used when file contained a `# fmt: off`."""
1139         if isinstance(node, Node):
1140             for child in node.children:
1141                 yield from self.visit(child)
1142
1143         else:
1144             try:
1145                 self.current_line.append(node)
1146             except FormatOn as f_on:
1147                 f_on.trim_prefix(node)
1148                 yield from self.line()
1149                 yield from self.visit(node)
1150
1151             if node.type == token.ENDMARKER:
1152                 # somebody decided not to put a final `# fmt: on`
1153                 yield from self.line()
1154
1155     def __attrs_post_init__(self) -> None:
1156         """You are in a twisty little maze of passages."""
1157         v = self.visit_stmt
1158         self.visit_if_stmt = partial(v, keywords={"if", "else", "elif"})
1159         self.visit_while_stmt = partial(v, keywords={"while", "else"})
1160         self.visit_for_stmt = partial(v, keywords={"for", "else"})
1161         self.visit_try_stmt = partial(v, keywords={"try", "except", "else", "finally"})
1162         self.visit_except_clause = partial(v, keywords={"except"})
1163         self.visit_funcdef = partial(v, keywords={"def"})
1164         self.visit_with_stmt = partial(v, keywords={"with"})
1165         self.visit_classdef = partial(v, keywords={"class"})
1166         self.visit_async_funcdef = self.visit_async_stmt
1167         self.visit_decorated = self.visit_decorators
1168
1169
1170 BRACKET = {token.LPAR: token.RPAR, token.LSQB: token.RSQB, token.LBRACE: token.RBRACE}
1171 OPENING_BRACKETS = set(BRACKET.keys())
1172 CLOSING_BRACKETS = set(BRACKET.values())
1173 BRACKETS = OPENING_BRACKETS | CLOSING_BRACKETS
1174 ALWAYS_NO_SPACE = CLOSING_BRACKETS | {token.COMMA, STANDALONE_COMMENT}
1175
1176
1177 def whitespace(leaf: Leaf) -> str:  # noqa C901
1178     """Return whitespace prefix if needed for the given `leaf`."""
1179     NO = ""
1180     SPACE = " "
1181     DOUBLESPACE = "  "
1182     t = leaf.type
1183     p = leaf.parent
1184     v = leaf.value
1185     if t in ALWAYS_NO_SPACE:
1186         return NO
1187
1188     if t == token.COMMENT:
1189         return DOUBLESPACE
1190
1191     assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
1192     if t == token.COLON and p.type not in {syms.subscript, syms.subscriptlist}:
1193         return NO
1194
1195     prev = leaf.prev_sibling
1196     if not prev:
1197         prevp = preceding_leaf(p)
1198         if not prevp or prevp.type in OPENING_BRACKETS:
1199             return NO
1200
1201         if t == token.COLON:
1202             return SPACE if prevp.type == token.COMMA else NO
1203
1204         if prevp.type == token.EQUAL:
1205             if prevp.parent:
1206                 if prevp.parent.type in {
1207                     syms.arglist, syms.argument, syms.parameters, syms.varargslist
1208                 }:
1209                     return NO
1210
1211                 elif prevp.parent.type == syms.typedargslist:
1212                     # A bit hacky: if the equal sign has whitespace, it means we
1213                     # previously found it's a typed argument.  So, we're using
1214                     # that, too.
1215                     return prevp.prefix
1216
1217         elif prevp.type == token.DOUBLESTAR:
1218             if prevp.parent and prevp.parent.type in {
1219                 syms.arglist,
1220                 syms.argument,
1221                 syms.dictsetmaker,
1222                 syms.parameters,
1223                 syms.typedargslist,
1224                 syms.varargslist,
1225             }:
1226                 return NO
1227
1228         elif prevp.type == token.COLON:
1229             if prevp.parent and prevp.parent.type in {syms.subscript, syms.sliceop}:
1230                 return NO
1231
1232         elif (
1233             prevp.parent
1234             and prevp.parent.type in {syms.factor, syms.star_expr}
1235             and prevp.type in MATH_OPERATORS
1236         ):
1237             return NO
1238
1239         elif (
1240             prevp.type == token.RIGHTSHIFT
1241             and prevp.parent
1242             and prevp.parent.type == syms.shift_expr
1243             and prevp.prev_sibling
1244             and prevp.prev_sibling.type == token.NAME
1245             and prevp.prev_sibling.value == "print"  # type: ignore
1246         ):
1247             # Python 2 print chevron
1248             return NO
1249
1250     elif prev.type in OPENING_BRACKETS:
1251         return NO
1252
1253     if p.type in {syms.parameters, syms.arglist}:
1254         # untyped function signatures or calls
1255         if t == token.RPAR:
1256             return NO
1257
1258         if not prev or prev.type != token.COMMA:
1259             return NO
1260
1261     elif p.type == syms.varargslist:
1262         # lambdas
1263         if t == token.RPAR:
1264             return NO
1265
1266         if prev and prev.type != token.COMMA:
1267             return NO
1268
1269     elif p.type == syms.typedargslist:
1270         # typed function signatures
1271         if not prev:
1272             return NO
1273
1274         if t == token.EQUAL:
1275             if prev.type != syms.tname:
1276                 return NO
1277
1278         elif prev.type == token.EQUAL:
1279             # A bit hacky: if the equal sign has whitespace, it means we
1280             # previously found it's a typed argument.  So, we're using that, too.
1281             return prev.prefix
1282
1283         elif prev.type != token.COMMA:
1284             return NO
1285
1286     elif p.type == syms.tname:
1287         # type names
1288         if not prev:
1289             prevp = preceding_leaf(p)
1290             if not prevp or prevp.type != token.COMMA:
1291                 return NO
1292
1293     elif p.type == syms.trailer:
1294         # attributes and calls
1295         if t == token.LPAR or t == token.RPAR:
1296             return NO
1297
1298         if not prev:
1299             if t == token.DOT:
1300                 prevp = preceding_leaf(p)
1301                 if not prevp or prevp.type != token.NUMBER:
1302                     return NO
1303
1304             elif t == token.LSQB:
1305                 return NO
1306
1307         elif prev.type != token.COMMA:
1308             return NO
1309
1310     elif p.type == syms.argument:
1311         # single argument
1312         if t == token.EQUAL:
1313             return NO
1314
1315         if not prev:
1316             prevp = preceding_leaf(p)
1317             if not prevp or prevp.type == token.LPAR:
1318                 return NO
1319
1320         elif prev.type == token.EQUAL or prev.type == token.DOUBLESTAR:
1321             return NO
1322
1323     elif p.type == syms.decorator:
1324         # decorators
1325         return NO
1326
1327     elif p.type == syms.dotted_name:
1328         if prev:
1329             return NO
1330
1331         prevp = preceding_leaf(p)
1332         if not prevp or prevp.type == token.AT or prevp.type == token.DOT:
1333             return NO
1334
1335     elif p.type == syms.classdef:
1336         if t == token.LPAR:
1337             return NO
1338
1339         if prev and prev.type == token.LPAR:
1340             return NO
1341
1342     elif p.type == syms.subscript:
1343         # indexing
1344         if not prev:
1345             assert p.parent is not None, "subscripts are always parented"
1346             if p.parent.type == syms.subscriptlist:
1347                 return SPACE
1348
1349             return NO
1350
1351         else:
1352             return NO
1353
1354     elif p.type == syms.atom:
1355         if prev and t == token.DOT:
1356             # dots, but not the first one.
1357             return NO
1358
1359     elif (
1360         p.type == syms.listmaker
1361         or p.type == syms.testlist_gexp
1362         or p.type == syms.subscriptlist
1363     ):
1364         # list interior, including unpacking
1365         if not prev:
1366             return NO
1367
1368     elif p.type == syms.dictsetmaker:
1369         # dict and set interior, including unpacking
1370         if not prev:
1371             return NO
1372
1373         if prev.type == token.DOUBLESTAR:
1374             return NO
1375
1376     elif p.type in {syms.factor, syms.star_expr}:
1377         # unary ops
1378         if not prev:
1379             prevp = preceding_leaf(p)
1380             if not prevp or prevp.type in OPENING_BRACKETS:
1381                 return NO
1382
1383             prevp_parent = prevp.parent
1384             assert prevp_parent is not None
1385             if prevp.type == token.COLON and prevp_parent.type in {
1386                 syms.subscript, syms.sliceop
1387             }:
1388                 return NO
1389
1390             elif prevp.type == token.EQUAL and prevp_parent.type == syms.argument:
1391                 return NO
1392
1393         elif t == token.NAME or t == token.NUMBER:
1394             return NO
1395
1396     elif p.type == syms.import_from:
1397         if t == token.DOT:
1398             if prev and prev.type == token.DOT:
1399                 return NO
1400
1401         elif t == token.NAME:
1402             if v == "import":
1403                 return SPACE
1404
1405             if prev and prev.type == token.DOT:
1406                 return NO
1407
1408     elif p.type == syms.sliceop:
1409         return NO
1410
1411     return SPACE
1412
1413
1414 def preceding_leaf(node: Optional[LN]) -> Optional[Leaf]:
1415     """Return the first leaf that precedes `node`, if any."""
1416     while node:
1417         res = node.prev_sibling
1418         if res:
1419             if isinstance(res, Leaf):
1420                 return res
1421
1422             try:
1423                 return list(res.leaves())[-1]
1424
1425             except IndexError:
1426                 return None
1427
1428         node = node.parent
1429     return None
1430
1431
1432 def is_split_after_delimiter(leaf: Leaf, previous: Leaf = None) -> int:
1433     """Return the priority of the `leaf` delimiter, given a line break after it.
1434
1435     The delimiter priorities returned here are from those delimiters that would
1436     cause a line break after themselves.
1437
1438     Higher numbers are higher priority.
1439     """
1440     if leaf.type == token.COMMA:
1441         return COMMA_PRIORITY
1442
1443     return 0
1444
1445
1446 def is_split_before_delimiter(leaf: Leaf, previous: Leaf = None) -> int:
1447     """Return the priority of the `leaf` delimiter, given a line before after it.
1448
1449     The delimiter priorities returned here are from those delimiters that would
1450     cause a line break before themselves.
1451
1452     Higher numbers are higher priority.
1453     """
1454     if (
1455         leaf.type in VARARGS
1456         and leaf.parent
1457         and leaf.parent.type in {syms.argument, syms.typedargslist}
1458     ):
1459         # * and ** might also be MATH_OPERATORS but in this case they are not.
1460         # Don't treat them as a delimiter.
1461         return 0
1462
1463     if (
1464         leaf.type in MATH_OPERATORS
1465         and leaf.parent
1466         and leaf.parent.type not in {syms.factor, syms.star_expr}
1467     ):
1468         return MATH_PRIORITY
1469
1470     if leaf.type in COMPARATORS:
1471         return COMPARATOR_PRIORITY
1472
1473     if (
1474         leaf.type == token.STRING
1475         and previous is not None
1476         and previous.type == token.STRING
1477     ):
1478         return STRING_PRIORITY
1479
1480     if (
1481         leaf.type == token.NAME
1482         and leaf.value == "for"
1483         and leaf.parent
1484         and leaf.parent.type in {syms.comp_for, syms.old_comp_for}
1485     ):
1486         return COMPREHENSION_PRIORITY
1487
1488     if (
1489         leaf.type == token.NAME
1490         and leaf.value == "if"
1491         and leaf.parent
1492         and leaf.parent.type in {syms.comp_if, syms.old_comp_if}
1493     ):
1494         return COMPREHENSION_PRIORITY
1495
1496     if leaf.type == token.NAME and leaf.value in LOGIC_OPERATORS and leaf.parent:
1497         return LOGIC_PRIORITY
1498
1499     return 0
1500
1501
1502 def is_delimiter(leaf: Leaf, previous: Leaf = None) -> int:
1503     """Return the priority of the `leaf` delimiter. Return 0 if not delimiter.
1504
1505     Higher numbers are higher priority.
1506     """
1507     return max(
1508         is_split_before_delimiter(leaf, previous),
1509         is_split_after_delimiter(leaf, previous),
1510     )
1511
1512
1513 def generate_comments(leaf: Leaf) -> Iterator[Leaf]:
1514     """Clean the prefix of the `leaf` and generate comments from it, if any.
1515
1516     Comments in lib2to3 are shoved into the whitespace prefix.  This happens
1517     in `pgen2/driver.py:Driver.parse_tokens()`.  This was a brilliant implementation
1518     move because it does away with modifying the grammar to include all the
1519     possible places in which comments can be placed.
1520
1521     The sad consequence for us though is that comments don't "belong" anywhere.
1522     This is why this function generates simple parentless Leaf objects for
1523     comments.  We simply don't know what the correct parent should be.
1524
1525     No matter though, we can live without this.  We really only need to
1526     differentiate between inline and standalone comments.  The latter don't
1527     share the line with any code.
1528
1529     Inline comments are emitted as regular token.COMMENT leaves.  Standalone
1530     are emitted with a fake STANDALONE_COMMENT token identifier.
1531     """
1532     p = leaf.prefix
1533     if not p:
1534         return
1535
1536     if "#" not in p:
1537         return
1538
1539     consumed = 0
1540     nlines = 0
1541     for index, line in enumerate(p.split("\n")):
1542         consumed += len(line) + 1  # adding the length of the split '\n'
1543         line = line.lstrip()
1544         if not line:
1545             nlines += 1
1546         if not line.startswith("#"):
1547             continue
1548
1549         if index == 0 and leaf.type != token.ENDMARKER:
1550             comment_type = token.COMMENT  # simple trailing comment
1551         else:
1552             comment_type = STANDALONE_COMMENT
1553         comment = make_comment(line)
1554         yield Leaf(comment_type, comment, prefix="\n" * nlines)
1555
1556         if comment in {"# fmt: on", "# yapf: enable"}:
1557             raise FormatOn(consumed)
1558
1559         if comment in {"# fmt: off", "# yapf: disable"}:
1560             if comment_type == STANDALONE_COMMENT:
1561                 raise FormatOff(consumed)
1562
1563             prev = preceding_leaf(leaf)
1564             if not prev or prev.type in WHITESPACE:  # standalone comment in disguise
1565                 raise FormatOff(consumed)
1566
1567         nlines = 0
1568
1569
1570 def make_comment(content: str) -> str:
1571     """Return a consistently formatted comment from the given `content` string.
1572
1573     All comments (except for "##", "#!", "#:") should have a single space between
1574     the hash sign and the content.
1575
1576     If `content` didn't start with a hash sign, one is provided.
1577     """
1578     content = content.rstrip()
1579     if not content:
1580         return "#"
1581
1582     if content[0] == "#":
1583         content = content[1:]
1584     if content and content[0] not in " !:#":
1585         content = " " + content
1586     return "#" + content
1587
1588
1589 def split_line(
1590     line: Line, line_length: int, inner: bool = False, py36: bool = False
1591 ) -> Iterator[Line]:
1592     """Split a `line` into potentially many lines.
1593
1594     They should fit in the allotted `line_length` but might not be able to.
1595     `inner` signifies that there were a pair of brackets somewhere around the
1596     current `line`, possibly transitively. This means we can fallback to splitting
1597     by delimiters if the LHS/RHS don't yield any results.
1598
1599     If `py36` is True, splitting may generate syntax that is only compatible
1600     with Python 3.6 and later.
1601     """
1602     if isinstance(line, UnformattedLines) or line.is_comment:
1603         yield line
1604         return
1605
1606     line_str = str(line).strip("\n")
1607     if (
1608         len(line_str) <= line_length
1609         and "\n" not in line_str  # multiline strings
1610         and not line.contains_standalone_comments
1611     ):
1612         yield line
1613         return
1614
1615     split_funcs: List[SplitFunc]
1616     if line.is_def:
1617         split_funcs = [left_hand_split]
1618     elif line.inside_brackets:
1619         split_funcs = [delimiter_split, standalone_comment_split, right_hand_split]
1620     else:
1621         split_funcs = [right_hand_split]
1622     for split_func in split_funcs:
1623         # We are accumulating lines in `result` because we might want to abort
1624         # mission and return the original line in the end, or attempt a different
1625         # split altogether.
1626         result: List[Line] = []
1627         try:
1628             for l in split_func(line, py36):
1629                 if str(l).strip("\n") == line_str:
1630                     raise CannotSplit("Split function returned an unchanged result")
1631
1632                 result.extend(
1633                     split_line(l, line_length=line_length, inner=True, py36=py36)
1634                 )
1635         except CannotSplit as cs:
1636             continue
1637
1638         else:
1639             yield from result
1640             break
1641
1642     else:
1643         yield line
1644
1645
1646 def left_hand_split(line: Line, py36: bool = False) -> Iterator[Line]:
1647     """Split line into many lines, starting with the first matching bracket pair.
1648
1649     Note: this usually looks weird, only use this for function definitions.
1650     Prefer RHS otherwise.
1651     """
1652     head = Line(depth=line.depth)
1653     body = Line(depth=line.depth + 1, inside_brackets=True)
1654     tail = Line(depth=line.depth)
1655     tail_leaves: List[Leaf] = []
1656     body_leaves: List[Leaf] = []
1657     head_leaves: List[Leaf] = []
1658     current_leaves = head_leaves
1659     matching_bracket = None
1660     for leaf in line.leaves:
1661         if (
1662             current_leaves is body_leaves
1663             and leaf.type in CLOSING_BRACKETS
1664             and leaf.opening_bracket is matching_bracket
1665         ):
1666             current_leaves = tail_leaves if body_leaves else head_leaves
1667         current_leaves.append(leaf)
1668         if current_leaves is head_leaves:
1669             if leaf.type in OPENING_BRACKETS:
1670                 matching_bracket = leaf
1671                 current_leaves = body_leaves
1672     # Since body is a new indent level, remove spurious leading whitespace.
1673     if body_leaves:
1674         normalize_prefix(body_leaves[0], inside_brackets=True)
1675     # Build the new lines.
1676     for result, leaves in (
1677         (head, head_leaves), (body, body_leaves), (tail, tail_leaves)
1678     ):
1679         for leaf in leaves:
1680             result.append(leaf, preformatted=True)
1681             for comment_after in line.comments_after(leaf):
1682                 result.append(comment_after, preformatted=True)
1683     bracket_split_succeeded_or_raise(head, body, tail)
1684     for result in (head, body, tail):
1685         if result:
1686             yield result
1687
1688
1689 def right_hand_split(line: Line, py36: bool = False) -> Iterator[Line]:
1690     """Split line into many lines, starting with the last matching bracket pair."""
1691     head = Line(depth=line.depth)
1692     body = Line(depth=line.depth + 1, inside_brackets=True)
1693     tail = Line(depth=line.depth)
1694     tail_leaves: List[Leaf] = []
1695     body_leaves: List[Leaf] = []
1696     head_leaves: List[Leaf] = []
1697     current_leaves = tail_leaves
1698     opening_bracket = None
1699     for leaf in reversed(line.leaves):
1700         if current_leaves is body_leaves:
1701             if leaf is opening_bracket:
1702                 current_leaves = head_leaves if body_leaves else tail_leaves
1703         current_leaves.append(leaf)
1704         if current_leaves is tail_leaves:
1705             if leaf.type in CLOSING_BRACKETS:
1706                 opening_bracket = leaf.opening_bracket
1707                 current_leaves = body_leaves
1708     tail_leaves.reverse()
1709     body_leaves.reverse()
1710     head_leaves.reverse()
1711     # Since body is a new indent level, remove spurious leading whitespace.
1712     if body_leaves:
1713         normalize_prefix(body_leaves[0], inside_brackets=True)
1714     # Build the new lines.
1715     for result, leaves in (
1716         (head, head_leaves), (body, body_leaves), (tail, tail_leaves)
1717     ):
1718         for leaf in leaves:
1719             result.append(leaf, preformatted=True)
1720             for comment_after in line.comments_after(leaf):
1721                 result.append(comment_after, preformatted=True)
1722     bracket_split_succeeded_or_raise(head, body, tail)
1723     for result in (head, body, tail):
1724         if result:
1725             yield result
1726
1727
1728 def bracket_split_succeeded_or_raise(head: Line, body: Line, tail: Line) -> None:
1729     """Raise :exc:`CannotSplit` if the last left- or right-hand split failed.
1730
1731     Do nothing otherwise.
1732
1733     A left- or right-hand split is based on a pair of brackets. Content before
1734     (and including) the opening bracket is left on one line, content inside the
1735     brackets is put on a separate line, and finally content starting with and
1736     following the closing bracket is put on a separate line.
1737
1738     Those are called `head`, `body`, and `tail`, respectively. If the split
1739     produced the same line (all content in `head`) or ended up with an empty `body`
1740     and the `tail` is just the closing bracket, then it's considered failed.
1741     """
1742     tail_len = len(str(tail).strip())
1743     if not body:
1744         if tail_len == 0:
1745             raise CannotSplit("Splitting brackets produced the same line")
1746
1747         elif tail_len < 3:
1748             raise CannotSplit(
1749                 f"Splitting brackets on an empty body to save "
1750                 f"{tail_len} characters is not worth it"
1751             )
1752
1753
1754 def dont_increase_indentation(split_func: SplitFunc) -> SplitFunc:
1755     """Normalize prefix of the first leaf in every line returned by `split_func`.
1756
1757     This is a decorator over relevant split functions.
1758     """
1759
1760     @wraps(split_func)
1761     def split_wrapper(line: Line, py36: bool = False) -> Iterator[Line]:
1762         for l in split_func(line, py36):
1763             normalize_prefix(l.leaves[0], inside_brackets=True)
1764             yield l
1765
1766     return split_wrapper
1767
1768
1769 @dont_increase_indentation
1770 def delimiter_split(line: Line, py36: bool = False) -> Iterator[Line]:
1771     """Split according to delimiters of the highest priority.
1772
1773     If `py36` is True, the split will add trailing commas also in function
1774     signatures that contain `*` and `**`.
1775     """
1776     try:
1777         last_leaf = line.leaves[-1]
1778     except IndexError:
1779         raise CannotSplit("Line empty")
1780
1781     delimiters = line.bracket_tracker.delimiters
1782     try:
1783         delimiter_priority = line.bracket_tracker.max_delimiter_priority(
1784             exclude={id(last_leaf)}
1785         )
1786     except ValueError:
1787         raise CannotSplit("No delimiters found")
1788
1789     current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
1790     lowest_depth = sys.maxsize
1791     trailing_comma_safe = True
1792
1793     def append_to_line(leaf: Leaf) -> Iterator[Line]:
1794         """Append `leaf` to current line or to new line if appending impossible."""
1795         nonlocal current_line
1796         try:
1797             current_line.append_safe(leaf, preformatted=True)
1798         except ValueError as ve:
1799             yield current_line
1800
1801             current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
1802             current_line.append(leaf)
1803
1804     for leaf in line.leaves:
1805         yield from append_to_line(leaf)
1806
1807         for comment_after in line.comments_after(leaf):
1808             yield from append_to_line(comment_after)
1809
1810         lowest_depth = min(lowest_depth, leaf.bracket_depth)
1811         if (
1812             leaf.bracket_depth == lowest_depth
1813             and leaf.type == token.STAR
1814             or leaf.type == token.DOUBLESTAR
1815         ):
1816             trailing_comma_safe = trailing_comma_safe and py36
1817         leaf_priority = delimiters.get(id(leaf))
1818         if leaf_priority == delimiter_priority:
1819             yield current_line
1820
1821             current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
1822     if current_line:
1823         if (
1824             trailing_comma_safe
1825             and delimiter_priority == COMMA_PRIORITY
1826             and current_line.leaves[-1].type != token.COMMA
1827             and current_line.leaves[-1].type != STANDALONE_COMMENT
1828         ):
1829             current_line.append(Leaf(token.COMMA, ","))
1830         yield current_line
1831
1832
1833 @dont_increase_indentation
1834 def standalone_comment_split(line: Line, py36: bool = False) -> Iterator[Line]:
1835     """Split standalone comments from the rest of the line."""
1836     for leaf in line.leaves:
1837         if leaf.type == STANDALONE_COMMENT:
1838             if leaf.bracket_depth == 0:
1839                 break
1840
1841     else:
1842         raise CannotSplit("Line does not have any standalone comments")
1843
1844     current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
1845
1846     def append_to_line(leaf: Leaf) -> Iterator[Line]:
1847         """Append `leaf` to current line or to new line if appending impossible."""
1848         nonlocal current_line
1849         try:
1850             current_line.append_safe(leaf, preformatted=True)
1851         except ValueError as ve:
1852             yield current_line
1853
1854             current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
1855             current_line.append(leaf)
1856
1857     for leaf in line.leaves:
1858         yield from append_to_line(leaf)
1859
1860         for comment_after in line.comments_after(leaf):
1861             yield from append_to_line(comment_after)
1862
1863     if current_line:
1864         yield current_line
1865
1866
1867 def is_import(leaf: Leaf) -> bool:
1868     """Return True if the given leaf starts an import statement."""
1869     p = leaf.parent
1870     t = leaf.type
1871     v = leaf.value
1872     return bool(
1873         t == token.NAME
1874         and (
1875             (v == "import" and p and p.type == syms.import_name)
1876             or (v == "from" and p and p.type == syms.import_from)
1877         )
1878     )
1879
1880
1881 def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None:
1882     """Leave existing extra newlines if not `inside_brackets`. Remove everything
1883     else.
1884
1885     Note: don't use backslashes for formatting or you'll lose your voting rights.
1886     """
1887     if not inside_brackets:
1888         spl = leaf.prefix.split("#")
1889         if "\\" not in spl[0]:
1890             nl_count = spl[-1].count("\n")
1891             if len(spl) > 1:
1892                 nl_count -= 1
1893             leaf.prefix = "\n" * nl_count
1894             return
1895
1896     leaf.prefix = ""
1897
1898
1899 def normalize_string_quotes(leaf: Leaf) -> None:
1900     """Prefer double quotes but only if it doesn't cause more escaping.
1901
1902     Adds or removes backslashes as appropriate. Doesn't parse and fix
1903     strings nested in f-strings (yet).
1904
1905     Note: Mutates its argument.
1906     """
1907     value = leaf.value.lstrip("furbFURB")
1908     if value[:3] == '"""':
1909         return
1910
1911     elif value[:3] == "'''":
1912         orig_quote = "'''"
1913         new_quote = '"""'
1914     elif value[0] == '"':
1915         orig_quote = '"'
1916         new_quote = "'"
1917     else:
1918         orig_quote = "'"
1919         new_quote = '"'
1920     first_quote_pos = leaf.value.find(orig_quote)
1921     if first_quote_pos == -1:
1922         return  # There's an internal error
1923
1924     prefix = leaf.value[:first_quote_pos]
1925     body = leaf.value[first_quote_pos + len(orig_quote):-len(orig_quote)]
1926     unescaped_new_quote = re.compile(r"(([^\\]|^)(\\\\)*)" + new_quote)
1927     escaped_orig_quote = re.compile(r"\\(\\\\)*" + orig_quote)
1928     if "r" in prefix.casefold():
1929         if unescaped_new_quote.search(body):
1930             # There's at least one unescaped new_quote in this raw string
1931             # so converting is impossible
1932             return
1933
1934         # Do not introduce or remove backslashes in raw strings
1935         new_body = body
1936     else:
1937         new_body = escaped_orig_quote.sub(f"\\1{orig_quote}", body)
1938         new_body = unescaped_new_quote.sub(f"\\1\\\\{new_quote}", new_body)
1939     if new_quote == '"""' and new_body[-1] == '"':
1940         # edge case:
1941         new_body = new_body[:-1] + '\\"'
1942     orig_escape_count = body.count("\\")
1943     new_escape_count = new_body.count("\\")
1944     if new_escape_count > orig_escape_count:
1945         return  # Do not introduce more escaping
1946
1947     if new_escape_count == orig_escape_count and orig_quote == '"':
1948         return  # Prefer double quotes
1949
1950     leaf.value = f"{prefix}{new_quote}{new_body}{new_quote}"
1951
1952
1953 def is_python36(node: Node) -> bool:
1954     """Return True if the current file is using Python 3.6+ features.
1955
1956     Currently looking for:
1957     - f-strings; and
1958     - trailing commas after * or ** in function signatures.
1959     """
1960     for n in node.pre_order():
1961         if n.type == token.STRING:
1962             value_head = n.value[:2]  # type: ignore
1963             if value_head in {'f"', 'F"', "f'", "F'", "rf", "fr", "RF", "FR"}:
1964                 return True
1965
1966         elif (
1967             n.type == syms.typedargslist
1968             and n.children
1969             and n.children[-1].type == token.COMMA
1970         ):
1971             for ch in n.children:
1972                 if ch.type == token.STAR or ch.type == token.DOUBLESTAR:
1973                     return True
1974
1975     return False
1976
1977
1978 PYTHON_EXTENSIONS = {".py"}
1979 BLACKLISTED_DIRECTORIES = {
1980     "build", "buck-out", "dist", "_build", ".git", ".hg", ".mypy_cache", ".tox", ".venv"
1981 }
1982
1983
1984 def gen_python_files_in_dir(path: Path) -> Iterator[Path]:
1985     """Generate all files under `path` which aren't under BLACKLISTED_DIRECTORIES
1986     and have one of the PYTHON_EXTENSIONS.
1987     """
1988     for child in path.iterdir():
1989         if child.is_dir():
1990             if child.name in BLACKLISTED_DIRECTORIES:
1991                 continue
1992
1993             yield from gen_python_files_in_dir(child)
1994
1995         elif child.suffix in PYTHON_EXTENSIONS:
1996             yield child
1997
1998
1999 @dataclass
2000 class Report:
2001     """Provides a reformatting counter. Can be rendered with `str(report)`."""
2002     check: bool = False
2003     quiet: bool = False
2004     change_count: int = 0
2005     same_count: int = 0
2006     failure_count: int = 0
2007
2008     def done(self, src: Path, changed: bool) -> None:
2009         """Increment the counter for successful reformatting. Write out a message."""
2010         if changed:
2011             reformatted = "would reformat" if self.check else "reformatted"
2012             if not self.quiet:
2013                 out(f"{reformatted} {src}")
2014             self.change_count += 1
2015         else:
2016             if not self.quiet:
2017                 out(f"{src} already well formatted, good job.", bold=False)
2018             self.same_count += 1
2019
2020     def failed(self, src: Path, message: str) -> None:
2021         """Increment the counter for failed reformatting. Write out a message."""
2022         err(f"error: cannot format {src}: {message}")
2023         self.failure_count += 1
2024
2025     @property
2026     def return_code(self) -> int:
2027         """Return the exit code that the app should use.
2028
2029         This considers the current state of changed files and failures:
2030         - if there were any failures, return 123;
2031         - if any files were changed and --check is being used, return 1;
2032         - otherwise return 0.
2033         """
2034         # According to http://tldp.org/LDP/abs/html/exitcodes.html starting with
2035         # 126 we have special returncodes reserved by the shell.
2036         if self.failure_count:
2037             return 123
2038
2039         elif self.change_count and self.check:
2040             return 1
2041
2042         return 0
2043
2044     def __str__(self) -> str:
2045         """Render a color report of the current state.
2046
2047         Use `click.unstyle` to remove colors.
2048         """
2049         if self.check:
2050             reformatted = "would be reformatted"
2051             unchanged = "would be left unchanged"
2052             failed = "would fail to reformat"
2053         else:
2054             reformatted = "reformatted"
2055             unchanged = "left unchanged"
2056             failed = "failed to reformat"
2057         report = []
2058         if self.change_count:
2059             s = "s" if self.change_count > 1 else ""
2060             report.append(
2061                 click.style(f"{self.change_count} file{s} {reformatted}", bold=True)
2062             )
2063         if self.same_count:
2064             s = "s" if self.same_count > 1 else ""
2065             report.append(f"{self.same_count} file{s} {unchanged}")
2066         if self.failure_count:
2067             s = "s" if self.failure_count > 1 else ""
2068             report.append(
2069                 click.style(f"{self.failure_count} file{s} {failed}", fg="red")
2070             )
2071         return ", ".join(report) + "."
2072
2073
2074 def assert_equivalent(src: str, dst: str) -> None:
2075     """Raise AssertionError if `src` and `dst` aren't equivalent."""
2076
2077     import ast
2078     import traceback
2079
2080     def _v(node: ast.AST, depth: int = 0) -> Iterator[str]:
2081         """Simple visitor generating strings to compare ASTs by content."""
2082         yield f"{'  ' * depth}{node.__class__.__name__}("
2083
2084         for field in sorted(node._fields):
2085             try:
2086                 value = getattr(node, field)
2087             except AttributeError:
2088                 continue
2089
2090             yield f"{'  ' * (depth+1)}{field}="
2091
2092             if isinstance(value, list):
2093                 for item in value:
2094                     if isinstance(item, ast.AST):
2095                         yield from _v(item, depth + 2)
2096
2097             elif isinstance(value, ast.AST):
2098                 yield from _v(value, depth + 2)
2099
2100             else:
2101                 yield f"{'  ' * (depth+2)}{value!r},  # {value.__class__.__name__}"
2102
2103         yield f"{'  ' * depth})  # /{node.__class__.__name__}"
2104
2105     try:
2106         src_ast = ast.parse(src)
2107     except Exception as exc:
2108         major, minor = sys.version_info[:2]
2109         raise AssertionError(
2110             f"cannot use --safe with this file; failed to parse source file "
2111             f"with Python {major}.{minor}'s builtin AST. Re-run with --fast "
2112             f"or stop using deprecated Python 2 syntax. AST error message: {exc}"
2113         )
2114
2115     try:
2116         dst_ast = ast.parse(dst)
2117     except Exception as exc:
2118         log = dump_to_file("".join(traceback.format_tb(exc.__traceback__)), dst)
2119         raise AssertionError(
2120             f"INTERNAL ERROR: Black produced invalid code: {exc}. "
2121             f"Please report a bug on https://github.com/ambv/black/issues.  "
2122             f"This invalid output might be helpful: {log}"
2123         ) from None
2124
2125     src_ast_str = "\n".join(_v(src_ast))
2126     dst_ast_str = "\n".join(_v(dst_ast))
2127     if src_ast_str != dst_ast_str:
2128         log = dump_to_file(diff(src_ast_str, dst_ast_str, "src", "dst"))
2129         raise AssertionError(
2130             f"INTERNAL ERROR: Black produced code that is not equivalent to "
2131             f"the source.  "
2132             f"Please report a bug on https://github.com/ambv/black/issues.  "
2133             f"This diff might be helpful: {log}"
2134         ) from None
2135
2136
2137 def assert_stable(src: str, dst: str, line_length: int) -> None:
2138     """Raise AssertionError if `dst` reformats differently the second time."""
2139     newdst = format_str(dst, line_length=line_length)
2140     if dst != newdst:
2141         log = dump_to_file(
2142             diff(src, dst, "source", "first pass"),
2143             diff(dst, newdst, "first pass", "second pass"),
2144         )
2145         raise AssertionError(
2146             f"INTERNAL ERROR: Black produced different code on the second pass "
2147             f"of the formatter.  "
2148             f"Please report a bug on https://github.com/ambv/black/issues.  "
2149             f"This diff might be helpful: {log}"
2150         ) from None
2151
2152
2153 def dump_to_file(*output: str) -> str:
2154     """Dump `output` to a temporary file. Return path to the file."""
2155     import tempfile
2156
2157     with tempfile.NamedTemporaryFile(
2158         mode="w", prefix="blk_", suffix=".log", delete=False
2159     ) as f:
2160         for lines in output:
2161             f.write(lines)
2162             if lines and lines[-1] != "\n":
2163                 f.write("\n")
2164     return f.name
2165
2166
2167 def diff(a: str, b: str, a_name: str, b_name: str) -> str:
2168     """Return a unified diff string between strings `a` and `b`."""
2169     import difflib
2170
2171     a_lines = [line + "\n" for line in a.split("\n")]
2172     b_lines = [line + "\n" for line in b.split("\n")]
2173     return "".join(
2174         difflib.unified_diff(a_lines, b_lines, fromfile=a_name, tofile=b_name, n=5)
2175     )
2176
2177
2178 def cancel(tasks: List[asyncio.Task]) -> None:
2179     """asyncio signal handler that cancels all `tasks` and reports to stderr."""
2180     err("Aborted!")
2181     for task in tasks:
2182         task.cancel()
2183
2184
2185 def shutdown(loop: BaseEventLoop) -> None:
2186     """Cancel all pending tasks on `loop`, wait for them, and close the loop."""
2187     try:
2188         # This part is borrowed from asyncio/runners.py in Python 3.7b2.
2189         to_cancel = [task for task in asyncio.Task.all_tasks(loop) if not task.done()]
2190         if not to_cancel:
2191             return
2192
2193         for task in to_cancel:
2194             task.cancel()
2195         loop.run_until_complete(
2196             asyncio.gather(*to_cancel, loop=loop, return_exceptions=True)
2197         )
2198     finally:
2199         # `concurrent.futures.Future` objects cannot be cancelled once they
2200         # are already running. There might be some when the `shutdown()` happened.
2201         # Silence their logger's spew about the event loop being closed.
2202         cf_logger = logging.getLogger("concurrent.futures")
2203         cf_logger.setLevel(logging.CRITICAL)
2204         loop.close()
2205
2206
2207 if __name__ == "__main__":
2208     main()