]> 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:

Introduce a section of docs about exceptions
[etc/vim.git] / black.py
1 import ast
2 import asyncio
3 from concurrent.futures import Executor, ProcessPoolExecutor
4 from contextlib import contextmanager
5 from datetime import datetime
6 from enum import Enum
7 from functools import lru_cache, partial, wraps
8 import io
9 import itertools
10 import logging
11 from multiprocessing import Manager, freeze_support
12 import os
13 from pathlib import Path
14 import pickle
15 import regex as re
16 import signal
17 import sys
18 import tempfile
19 import tokenize
20 import traceback
21 from typing import (
22     Any,
23     Callable,
24     Collection,
25     Dict,
26     Generator,
27     Generic,
28     Iterable,
29     Iterator,
30     List,
31     Optional,
32     Pattern,
33     Sequence,
34     Set,
35     Tuple,
36     TypeVar,
37     Union,
38     cast,
39 )
40 from typing_extensions import Final
41 from mypy_extensions import mypyc_attr
42
43 from appdirs import user_cache_dir
44 from dataclasses import dataclass, field, replace
45 import click
46 import toml
47 from typed_ast import ast3, ast27
48 from pathspec import PathSpec
49
50 # lib2to3 fork
51 from blib2to3.pytree import Node, Leaf, type_repr
52 from blib2to3 import pygram, pytree
53 from blib2to3.pgen2 import driver, token
54 from blib2to3.pgen2.grammar import Grammar
55 from blib2to3.pgen2.parse import ParseError
56
57 from _black_version import version as __version__
58
59 DEFAULT_LINE_LENGTH = 88
60 DEFAULT_EXCLUDES = r"/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|\.svn|_build|buck-out|build|dist)/"  # noqa: B950
61 DEFAULT_INCLUDES = r"\.pyi?$"
62 CACHE_DIR = Path(user_cache_dir("black", version=__version__))
63
64
65 # types
66 FileContent = str
67 Encoding = str
68 NewLine = str
69 Depth = int
70 NodeType = int
71 LeafID = int
72 Priority = int
73 Index = int
74 LN = Union[Leaf, Node]
75 SplitFunc = Callable[["Line", Collection["Feature"]], Iterator["Line"]]
76 Timestamp = float
77 FileSize = int
78 CacheInfo = Tuple[Timestamp, FileSize]
79 Cache = Dict[Path, CacheInfo]
80 out = partial(click.secho, bold=True, err=True)
81 err = partial(click.secho, fg="red", err=True)
82
83 pygram.initialize(CACHE_DIR)
84 syms = pygram.python_symbols
85
86
87 class NothingChanged(UserWarning):
88     """Raised when reformatted code is the same as source."""
89
90
91 class CannotSplit(Exception):
92     """A readable split that fits the allotted line length is impossible."""
93
94
95 class InvalidInput(ValueError):
96     """Raised when input source code fails all parse attempts."""
97
98
99 class WriteBack(Enum):
100     NO = 0
101     YES = 1
102     DIFF = 2
103     CHECK = 3
104
105     @classmethod
106     def from_configuration(cls, *, check: bool, diff: bool) -> "WriteBack":
107         if check and not diff:
108             return cls.CHECK
109
110         return cls.DIFF if diff else cls.YES
111
112
113 class Changed(Enum):
114     NO = 0
115     CACHED = 1
116     YES = 2
117
118
119 class TargetVersion(Enum):
120     PY27 = 2
121     PY33 = 3
122     PY34 = 4
123     PY35 = 5
124     PY36 = 6
125     PY37 = 7
126     PY38 = 8
127
128     def is_python2(self) -> bool:
129         return self is TargetVersion.PY27
130
131
132 PY36_VERSIONS = {TargetVersion.PY36, TargetVersion.PY37, TargetVersion.PY38}
133
134
135 class Feature(Enum):
136     # All string literals are unicode
137     UNICODE_LITERALS = 1
138     F_STRINGS = 2
139     NUMERIC_UNDERSCORES = 3
140     TRAILING_COMMA_IN_CALL = 4
141     TRAILING_COMMA_IN_DEF = 5
142     # The following two feature-flags are mutually exclusive, and exactly one should be
143     # set for every version of python.
144     ASYNC_IDENTIFIERS = 6
145     ASYNC_KEYWORDS = 7
146     ASSIGNMENT_EXPRESSIONS = 8
147     POS_ONLY_ARGUMENTS = 9
148
149
150 VERSION_TO_FEATURES: Dict[TargetVersion, Set[Feature]] = {
151     TargetVersion.PY27: {Feature.ASYNC_IDENTIFIERS},
152     TargetVersion.PY33: {Feature.UNICODE_LITERALS, Feature.ASYNC_IDENTIFIERS},
153     TargetVersion.PY34: {Feature.UNICODE_LITERALS, Feature.ASYNC_IDENTIFIERS},
154     TargetVersion.PY35: {
155         Feature.UNICODE_LITERALS,
156         Feature.TRAILING_COMMA_IN_CALL,
157         Feature.ASYNC_IDENTIFIERS,
158     },
159     TargetVersion.PY36: {
160         Feature.UNICODE_LITERALS,
161         Feature.F_STRINGS,
162         Feature.NUMERIC_UNDERSCORES,
163         Feature.TRAILING_COMMA_IN_CALL,
164         Feature.TRAILING_COMMA_IN_DEF,
165         Feature.ASYNC_IDENTIFIERS,
166     },
167     TargetVersion.PY37: {
168         Feature.UNICODE_LITERALS,
169         Feature.F_STRINGS,
170         Feature.NUMERIC_UNDERSCORES,
171         Feature.TRAILING_COMMA_IN_CALL,
172         Feature.TRAILING_COMMA_IN_DEF,
173         Feature.ASYNC_KEYWORDS,
174     },
175     TargetVersion.PY38: {
176         Feature.UNICODE_LITERALS,
177         Feature.F_STRINGS,
178         Feature.NUMERIC_UNDERSCORES,
179         Feature.TRAILING_COMMA_IN_CALL,
180         Feature.TRAILING_COMMA_IN_DEF,
181         Feature.ASYNC_KEYWORDS,
182         Feature.ASSIGNMENT_EXPRESSIONS,
183         Feature.POS_ONLY_ARGUMENTS,
184     },
185 }
186
187
188 @dataclass
189 class FileMode:
190     target_versions: Set[TargetVersion] = field(default_factory=set)
191     line_length: int = DEFAULT_LINE_LENGTH
192     string_normalization: bool = True
193     is_pyi: bool = False
194
195     def get_cache_key(self) -> str:
196         if self.target_versions:
197             version_str = ",".join(
198                 str(version.value)
199                 for version in sorted(self.target_versions, key=lambda v: v.value)
200             )
201         else:
202             version_str = "-"
203         parts = [
204             version_str,
205             str(self.line_length),
206             str(int(self.string_normalization)),
207             str(int(self.is_pyi)),
208         ]
209         return ".".join(parts)
210
211
212 def supports_feature(target_versions: Set[TargetVersion], feature: Feature) -> bool:
213     return all(feature in VERSION_TO_FEATURES[version] for version in target_versions)
214
215
216 def find_pyproject_toml(path_search_start: str) -> Optional[str]:
217     """Find the absolute filepath to a pyproject.toml if it exists"""
218     path_project_root = find_project_root(path_search_start)
219     path_pyproject_toml = path_project_root / "pyproject.toml"
220     return str(path_pyproject_toml) if path_pyproject_toml.is_file() else None
221
222
223 def parse_pyproject_toml(path_config: str) -> Dict[str, Any]:
224     """Parse a pyproject toml file, pulling out relevant parts for Black
225
226     If parsing fails, will raise a toml.TomlDecodeError
227     """
228     pyproject_toml = toml.load(path_config)
229     config = pyproject_toml.get("tool", {}).get("black", {})
230     return {k.replace("--", "").replace("-", "_"): v for k, v in config.items()}
231
232
233 def read_pyproject_toml(
234     ctx: click.Context, param: click.Parameter, value: Union[str, int, bool, None]
235 ) -> Optional[str]:
236     """Inject Black configuration from "pyproject.toml" into defaults in `ctx`.
237
238     Returns the path to a successfully found and read configuration file, None
239     otherwise.
240     """
241     assert not isinstance(value, (int, bool)), "Invalid parameter type passed"
242     if not value:
243         value = find_pyproject_toml(ctx.params.get("src", ()))
244         if value is None:
245             return None
246
247     try:
248         config = parse_pyproject_toml(value)
249     except (toml.TomlDecodeError, OSError) as e:
250         raise click.FileError(
251             filename=value, hint=f"Error reading configuration file: {e}"
252         )
253
254     if not config:
255         return None
256
257     if ctx.default_map is None:
258         ctx.default_map = {}
259     ctx.default_map.update(config)  # type: ignore  # bad types in .pyi
260     return value
261
262
263 def target_version_option_callback(
264     c: click.Context, p: Union[click.Option, click.Parameter], v: Tuple[str, ...]
265 ) -> List[TargetVersion]:
266     """Compute the target versions from a --target-version flag.
267
268     This is its own function because mypy couldn't infer the type correctly
269     when it was a lambda, causing mypyc trouble.
270     """
271     return [TargetVersion[val.upper()] for val in v]
272
273
274 @click.command(context_settings=dict(help_option_names=["-h", "--help"]))
275 @click.option("-c", "--code", type=str, help="Format the code passed in as a string.")
276 @click.option(
277     "-l",
278     "--line-length",
279     type=int,
280     default=DEFAULT_LINE_LENGTH,
281     help="How many characters per line to allow.",
282     show_default=True,
283 )
284 @click.option(
285     "-t",
286     "--target-version",
287     type=click.Choice([v.name.lower() for v in TargetVersion]),
288     callback=target_version_option_callback,
289     multiple=True,
290     help=(
291         "Python versions that should be supported by Black's output. [default: "
292         "per-file auto-detection]"
293     ),
294 )
295 @click.option(
296     "--py36",
297     is_flag=True,
298     help=(
299         "Allow using Python 3.6-only syntax on all input files.  This will put "
300         "trailing commas in function signatures and calls also after *args and "
301         "**kwargs. Deprecated; use --target-version instead. "
302         "[default: per-file auto-detection]"
303     ),
304 )
305 @click.option(
306     "--pyi",
307     is_flag=True,
308     help=(
309         "Format all input files like typing stubs regardless of file extension "
310         "(useful when piping source on standard input)."
311     ),
312 )
313 @click.option(
314     "-S",
315     "--skip-string-normalization",
316     is_flag=True,
317     help="Don't normalize string quotes or prefixes.",
318 )
319 @click.option(
320     "--check",
321     is_flag=True,
322     help=(
323         "Don't write the files back, just return the status.  Return code 0 "
324         "means nothing would change.  Return code 1 means some files would be "
325         "reformatted.  Return code 123 means there was an internal error."
326     ),
327 )
328 @click.option(
329     "--diff",
330     is_flag=True,
331     help="Don't write the files back, just output a diff for each file on stdout.",
332 )
333 @click.option(
334     "--fast/--safe",
335     is_flag=True,
336     help="If --fast given, skip temporary sanity checks. [default: --safe]",
337 )
338 @click.option(
339     "--include",
340     type=str,
341     default=DEFAULT_INCLUDES,
342     help=(
343         "A regular expression that matches files and directories that should be "
344         "included on recursive searches.  An empty value means all files are "
345         "included regardless of the name.  Use forward slashes for directories on "
346         "all platforms (Windows, too).  Exclusions are calculated first, inclusions "
347         "later."
348     ),
349     show_default=True,
350 )
351 @click.option(
352     "--exclude",
353     type=str,
354     default=DEFAULT_EXCLUDES,
355     help=(
356         "A regular expression that matches files and directories that should be "
357         "excluded on recursive searches.  An empty value means no paths are excluded. "
358         "Use forward slashes for directories on all platforms (Windows, too).  "
359         "Exclusions are calculated first, inclusions later."
360     ),
361     show_default=True,
362 )
363 @click.option(
364     "-q",
365     "--quiet",
366     is_flag=True,
367     help=(
368         "Don't emit non-error messages to stderr. Errors are still emitted; "
369         "silence those with 2>/dev/null."
370     ),
371 )
372 @click.option(
373     "-v",
374     "--verbose",
375     is_flag=True,
376     help=(
377         "Also emit messages to stderr about files that were not changed or were "
378         "ignored due to --exclude=."
379     ),
380 )
381 @click.version_option(version=__version__)
382 @click.argument(
383     "src",
384     nargs=-1,
385     type=click.Path(
386         exists=True, file_okay=True, dir_okay=True, readable=True, allow_dash=True
387     ),
388     is_eager=True,
389 )
390 @click.option(
391     "--config",
392     type=click.Path(
393         exists=False, file_okay=True, dir_okay=False, readable=True, allow_dash=False
394     ),
395     is_eager=True,
396     callback=read_pyproject_toml,
397     help="Read configuration from PATH.",
398 )
399 @click.pass_context
400 def main(
401     ctx: click.Context,
402     code: Optional[str],
403     line_length: int,
404     target_version: List[TargetVersion],
405     check: bool,
406     diff: bool,
407     fast: bool,
408     pyi: bool,
409     py36: bool,
410     skip_string_normalization: bool,
411     quiet: bool,
412     verbose: bool,
413     include: str,
414     exclude: str,
415     src: Tuple[str, ...],
416     config: Optional[str],
417 ) -> None:
418     """The uncompromising code formatter."""
419     write_back = WriteBack.from_configuration(check=check, diff=diff)
420     if target_version:
421         if py36:
422             err(f"Cannot use both --target-version and --py36")
423             ctx.exit(2)
424         else:
425             versions = set(target_version)
426     elif py36:
427         err(
428             "--py36 is deprecated and will be removed in a future version. "
429             "Use --target-version py36 instead."
430         )
431         versions = PY36_VERSIONS
432     else:
433         # We'll autodetect later.
434         versions = set()
435     mode = FileMode(
436         target_versions=versions,
437         line_length=line_length,
438         is_pyi=pyi,
439         string_normalization=not skip_string_normalization,
440     )
441     if config and verbose:
442         out(f"Using configuration from {config}.", bold=False, fg="blue")
443     if code is not None:
444         print(format_str(code, mode=mode))
445         ctx.exit(0)
446     try:
447         include_regex = re_compile_maybe_verbose(include)
448     except re.error:
449         err(f"Invalid regular expression for include given: {include!r}")
450         ctx.exit(2)
451     try:
452         exclude_regex = re_compile_maybe_verbose(exclude)
453     except re.error:
454         err(f"Invalid regular expression for exclude given: {exclude!r}")
455         ctx.exit(2)
456     report = Report(check=check, diff=diff, quiet=quiet, verbose=verbose)
457     root = find_project_root(src)
458     sources: Set[Path] = set()
459     path_empty(src, quiet, verbose, ctx)
460     for s in src:
461         p = Path(s)
462         if p.is_dir():
463             sources.update(
464                 gen_python_files_in_dir(
465                     p, root, include_regex, exclude_regex, report, get_gitignore(root)
466                 )
467             )
468         elif p.is_file() or s == "-":
469             # if a file was explicitly given, we don't care about its extension
470             sources.add(p)
471         else:
472             err(f"invalid path: {s}")
473     if len(sources) == 0:
474         if verbose or not quiet:
475             out("No Python files are present to be formatted. Nothing to do 😴")
476         ctx.exit(0)
477
478     if len(sources) == 1:
479         reformat_one(
480             src=sources.pop(),
481             fast=fast,
482             write_back=write_back,
483             mode=mode,
484             report=report,
485         )
486     else:
487         reformat_many(
488             sources=sources, fast=fast, write_back=write_back, mode=mode, report=report
489         )
490
491     if verbose or not quiet:
492         out("Oh no! 💥 💔 💥" if report.return_code else "All done! ✨ 🍰 ✨")
493         click.secho(str(report), err=True)
494     ctx.exit(report.return_code)
495
496
497 def path_empty(
498     src: Tuple[str, ...], quiet: bool, verbose: bool, ctx: click.Context
499 ) -> None:
500     """
501     Exit if there is no `src` provided for formatting
502     """
503     if not src:
504         if verbose or not quiet:
505             out("No Path provided. Nothing to do 😴")
506             ctx.exit(0)
507
508
509 def reformat_one(
510     src: Path, fast: bool, write_back: WriteBack, mode: FileMode, report: "Report"
511 ) -> None:
512     """Reformat a single file under `src` without spawning child processes.
513
514     `fast`, `write_back`, and `mode` options are passed to
515     :func:`format_file_in_place` or :func:`format_stdin_to_stdout`.
516     """
517     try:
518         changed = Changed.NO
519         if not src.is_file() and str(src) == "-":
520             if format_stdin_to_stdout(fast=fast, write_back=write_back, mode=mode):
521                 changed = Changed.YES
522         else:
523             cache: Cache = {}
524             if write_back != WriteBack.DIFF:
525                 cache = read_cache(mode)
526                 res_src = src.resolve()
527                 if res_src in cache and cache[res_src] == get_cache_info(res_src):
528                     changed = Changed.CACHED
529             if changed is not Changed.CACHED and format_file_in_place(
530                 src, fast=fast, write_back=write_back, mode=mode
531             ):
532                 changed = Changed.YES
533             if (write_back is WriteBack.YES and changed is not Changed.CACHED) or (
534                 write_back is WriteBack.CHECK and changed is Changed.NO
535             ):
536                 write_cache(cache, [src], mode)
537         report.done(src, changed)
538     except Exception as exc:
539         report.failed(src, str(exc))
540
541
542 def reformat_many(
543     sources: Set[Path],
544     fast: bool,
545     write_back: WriteBack,
546     mode: FileMode,
547     report: "Report",
548 ) -> None:
549     """Reformat multiple files using a ProcessPoolExecutor."""
550     loop = asyncio.get_event_loop()
551     worker_count = os.cpu_count()
552     if sys.platform == "win32":
553         # Work around https://bugs.python.org/issue26903
554         worker_count = min(worker_count, 61)
555     executor = ProcessPoolExecutor(max_workers=worker_count)
556     try:
557         loop.run_until_complete(
558             schedule_formatting(
559                 sources=sources,
560                 fast=fast,
561                 write_back=write_back,
562                 mode=mode,
563                 report=report,
564                 loop=loop,
565                 executor=executor,
566             )
567         )
568     finally:
569         shutdown(loop)
570         executor.shutdown()
571
572
573 async def schedule_formatting(
574     sources: Set[Path],
575     fast: bool,
576     write_back: WriteBack,
577     mode: FileMode,
578     report: "Report",
579     loop: asyncio.AbstractEventLoop,
580     executor: Executor,
581 ) -> None:
582     """Run formatting of `sources` in parallel using the provided `executor`.
583
584     (Use ProcessPoolExecutors for actual parallelism.)
585
586     `write_back`, `fast`, and `mode` options are passed to
587     :func:`format_file_in_place`.
588     """
589     cache: Cache = {}
590     if write_back != WriteBack.DIFF:
591         cache = read_cache(mode)
592         sources, cached = filter_cached(cache, sources)
593         for src in sorted(cached):
594             report.done(src, Changed.CACHED)
595     if not sources:
596         return
597
598     cancelled = []
599     sources_to_cache = []
600     lock = None
601     if write_back == WriteBack.DIFF:
602         # For diff output, we need locks to ensure we don't interleave output
603         # from different processes.
604         manager = Manager()
605         lock = manager.Lock()
606     tasks = {
607         asyncio.ensure_future(
608             loop.run_in_executor(
609                 executor, format_file_in_place, src, fast, mode, write_back, lock
610             )
611         ): src
612         for src in sorted(sources)
613     }
614     pending: Iterable["asyncio.Future[bool]"] = tasks.keys()
615     try:
616         loop.add_signal_handler(signal.SIGINT, cancel, pending)
617         loop.add_signal_handler(signal.SIGTERM, cancel, pending)
618     except NotImplementedError:
619         # There are no good alternatives for these on Windows.
620         pass
621     while pending:
622         done, _ = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
623         for task in done:
624             src = tasks.pop(task)
625             if task.cancelled():
626                 cancelled.append(task)
627             elif task.exception():
628                 report.failed(src, str(task.exception()))
629             else:
630                 changed = Changed.YES if task.result() else Changed.NO
631                 # If the file was written back or was successfully checked as
632                 # well-formatted, store this information in the cache.
633                 if write_back is WriteBack.YES or (
634                     write_back is WriteBack.CHECK and changed is Changed.NO
635                 ):
636                     sources_to_cache.append(src)
637                 report.done(src, changed)
638     if cancelled:
639         await asyncio.gather(*cancelled, loop=loop, return_exceptions=True)
640     if sources_to_cache:
641         write_cache(cache, sources_to_cache, mode)
642
643
644 def format_file_in_place(
645     src: Path,
646     fast: bool,
647     mode: FileMode,
648     write_back: WriteBack = WriteBack.NO,
649     lock: Any = None,  # multiprocessing.Manager().Lock() is some crazy proxy
650 ) -> bool:
651     """Format file under `src` path. Return True if changed.
652
653     If `write_back` is DIFF, write a diff to stdout. If it is YES, write reformatted
654     code to the file.
655     `mode` and `fast` options are passed to :func:`format_file_contents`.
656     """
657     if src.suffix == ".pyi":
658         mode = replace(mode, is_pyi=True)
659
660     then = datetime.utcfromtimestamp(src.stat().st_mtime)
661     with open(src, "rb") as buf:
662         src_contents, encoding, newline = decode_bytes(buf.read())
663     try:
664         dst_contents = format_file_contents(src_contents, fast=fast, mode=mode)
665     except NothingChanged:
666         return False
667
668     if write_back == WriteBack.YES:
669         with open(src, "w", encoding=encoding, newline=newline) as f:
670             f.write(dst_contents)
671     elif write_back == WriteBack.DIFF:
672         now = datetime.utcnow()
673         src_name = f"{src}\t{then} +0000"
674         dst_name = f"{src}\t{now} +0000"
675         diff_contents = diff(src_contents, dst_contents, src_name, dst_name)
676
677         with lock or nullcontext():
678             f = io.TextIOWrapper(
679                 sys.stdout.buffer,
680                 encoding=encoding,
681                 newline=newline,
682                 write_through=True,
683             )
684             f.write(diff_contents)
685             f.detach()
686
687     return True
688
689
690 def format_stdin_to_stdout(
691     fast: bool, *, write_back: WriteBack = WriteBack.NO, mode: FileMode
692 ) -> bool:
693     """Format file on stdin. Return True if changed.
694
695     If `write_back` is YES, write reformatted code back to stdout. If it is DIFF,
696     write a diff to stdout. The `mode` argument is passed to
697     :func:`format_file_contents`.
698     """
699     then = datetime.utcnow()
700     src, encoding, newline = decode_bytes(sys.stdin.buffer.read())
701     dst = src
702     try:
703         dst = format_file_contents(src, fast=fast, mode=mode)
704         return True
705
706     except NothingChanged:
707         return False
708
709     finally:
710         f = io.TextIOWrapper(
711             sys.stdout.buffer, encoding=encoding, newline=newline, write_through=True
712         )
713         if write_back == WriteBack.YES:
714             f.write(dst)
715         elif write_back == WriteBack.DIFF:
716             now = datetime.utcnow()
717             src_name = f"STDIN\t{then} +0000"
718             dst_name = f"STDOUT\t{now} +0000"
719             f.write(diff(src, dst, src_name, dst_name))
720         f.detach()
721
722
723 def format_file_contents(
724     src_contents: str, *, fast: bool, mode: FileMode
725 ) -> FileContent:
726     """Reformat contents a file and return new contents.
727
728     If `fast` is False, additionally confirm that the reformatted code is
729     valid by calling :func:`assert_equivalent` and :func:`assert_stable` on it.
730     `mode` is passed to :func:`format_str`.
731     """
732     if src_contents.strip() == "":
733         raise NothingChanged
734
735     dst_contents = format_str(src_contents, mode=mode)
736     if src_contents == dst_contents:
737         raise NothingChanged
738
739     if not fast:
740         assert_equivalent(src_contents, dst_contents)
741         assert_stable(src_contents, dst_contents, mode=mode)
742     return dst_contents
743
744
745 def format_str(src_contents: str, *, mode: FileMode) -> FileContent:
746     """Reformat a string and return new contents.
747
748     `mode` determines formatting options, such as how many characters per line are
749     allowed.
750     """
751     src_node = lib2to3_parse(src_contents.lstrip(), mode.target_versions)
752     dst_contents = []
753     future_imports = get_future_imports(src_node)
754     if mode.target_versions:
755         versions = mode.target_versions
756     else:
757         versions = detect_target_versions(src_node)
758     normalize_fmt_off(src_node)
759     lines = LineGenerator(
760         remove_u_prefix="unicode_literals" in future_imports
761         or supports_feature(versions, Feature.UNICODE_LITERALS),
762         is_pyi=mode.is_pyi,
763         normalize_strings=mode.string_normalization,
764     )
765     elt = EmptyLineTracker(is_pyi=mode.is_pyi)
766     empty_line = Line()
767     after = 0
768     split_line_features = {
769         feature
770         for feature in {Feature.TRAILING_COMMA_IN_CALL, Feature.TRAILING_COMMA_IN_DEF}
771         if supports_feature(versions, feature)
772     }
773     for current_line in lines.visit(src_node):
774         dst_contents.append(str(empty_line) * after)
775         before, after = elt.maybe_empty_lines(current_line)
776         dst_contents.append(str(empty_line) * before)
777         for line in split_line(
778             current_line, line_length=mode.line_length, features=split_line_features
779         ):
780             dst_contents.append(str(line))
781     return "".join(dst_contents)
782
783
784 def decode_bytes(src: bytes) -> Tuple[FileContent, Encoding, NewLine]:
785     """Return a tuple of (decoded_contents, encoding, newline).
786
787     `newline` is either CRLF or LF but `decoded_contents` is decoded with
788     universal newlines (i.e. only contains LF).
789     """
790     srcbuf = io.BytesIO(src)
791     encoding, lines = tokenize.detect_encoding(srcbuf.readline)
792     if not lines:
793         return "", encoding, "\n"
794
795     newline = "\r\n" if b"\r\n" == lines[0][-2:] else "\n"
796     srcbuf.seek(0)
797     with io.TextIOWrapper(srcbuf, encoding) as tiow:
798         return tiow.read(), encoding, newline
799
800
801 def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]:
802     if not target_versions:
803         # No target_version specified, so try all grammars.
804         return [
805             # Python 3.7+
806             pygram.python_grammar_no_print_statement_no_exec_statement_async_keywords,
807             # Python 3.0-3.6
808             pygram.python_grammar_no_print_statement_no_exec_statement,
809             # Python 2.7 with future print_function import
810             pygram.python_grammar_no_print_statement,
811             # Python 2.7
812             pygram.python_grammar,
813         ]
814
815     if all(version.is_python2() for version in target_versions):
816         # Python 2-only code, so try Python 2 grammars.
817         return [
818             # Python 2.7 with future print_function import
819             pygram.python_grammar_no_print_statement,
820             # Python 2.7
821             pygram.python_grammar,
822         ]
823
824     # Python 3-compatible code, so only try Python 3 grammar.
825     grammars = []
826     # If we have to parse both, try to parse async as a keyword first
827     if not supports_feature(target_versions, Feature.ASYNC_IDENTIFIERS):
828         # Python 3.7+
829         grammars.append(
830             pygram.python_grammar_no_print_statement_no_exec_statement_async_keywords
831         )
832     if not supports_feature(target_versions, Feature.ASYNC_KEYWORDS):
833         # Python 3.0-3.6
834         grammars.append(pygram.python_grammar_no_print_statement_no_exec_statement)
835     # At least one of the above branches must have been taken, because every Python
836     # version has exactly one of the two 'ASYNC_*' flags
837     return grammars
838
839
840 def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) -> Node:
841     """Given a string with source, return the lib2to3 Node."""
842     if src_txt[-1:] != "\n":
843         src_txt += "\n"
844
845     for grammar in get_grammars(set(target_versions)):
846         drv = driver.Driver(grammar, pytree.convert)
847         try:
848             result = drv.parse_string(src_txt, True)
849             break
850
851         except ParseError as pe:
852             lineno, column = pe.context[1]
853             lines = src_txt.splitlines()
854             try:
855                 faulty_line = lines[lineno - 1]
856             except IndexError:
857                 faulty_line = "<line number missing in source>"
858             exc = InvalidInput(f"Cannot parse: {lineno}:{column}: {faulty_line}")
859     else:
860         raise exc from None
861
862     if isinstance(result, Leaf):
863         result = Node(syms.file_input, [result])
864     return result
865
866
867 def lib2to3_unparse(node: Node) -> str:
868     """Given a lib2to3 node, return its string representation."""
869     code = str(node)
870     return code
871
872
873 T = TypeVar("T")
874
875
876 class Visitor(Generic[T]):
877     """Basic lib2to3 visitor that yields things of type `T` on `visit()`."""
878
879     def visit(self, node: LN) -> Iterator[T]:
880         """Main method to visit `node` and its children.
881
882         It tries to find a `visit_*()` method for the given `node.type`, like
883         `visit_simple_stmt` for Node objects or `visit_INDENT` for Leaf objects.
884         If no dedicated `visit_*()` method is found, chooses `visit_default()`
885         instead.
886
887         Then yields objects of type `T` from the selected visitor.
888         """
889         if node.type < 256:
890             name = token.tok_name[node.type]
891         else:
892             name = str(type_repr(node.type))
893         # We explicitly branch on whether a visitor exists (instead of
894         # using self.visit_default as the default arg to getattr) in order
895         # to save needing to create a bound method object and so mypyc can
896         # generate a native call to visit_default.
897         visitf = getattr(self, f"visit_{name}", None)
898         if visitf:
899             yield from visitf(node)
900         else:
901             yield from self.visit_default(node)
902
903     def visit_default(self, node: LN) -> Iterator[T]:
904         """Default `visit_*()` implementation. Recurses to children of `node`."""
905         if isinstance(node, Node):
906             for child in node.children:
907                 yield from self.visit(child)
908
909
910 @dataclass
911 class DebugVisitor(Visitor[T]):
912     tree_depth: int = 0
913
914     def visit_default(self, node: LN) -> Iterator[T]:
915         indent = " " * (2 * self.tree_depth)
916         if isinstance(node, Node):
917             _type = type_repr(node.type)
918             out(f"{indent}{_type}", fg="yellow")
919             self.tree_depth += 1
920             for child in node.children:
921                 yield from self.visit(child)
922
923             self.tree_depth -= 1
924             out(f"{indent}/{_type}", fg="yellow", bold=False)
925         else:
926             _type = token.tok_name.get(node.type, str(node.type))
927             out(f"{indent}{_type}", fg="blue", nl=False)
928             if node.prefix:
929                 # We don't have to handle prefixes for `Node` objects since
930                 # that delegates to the first child anyway.
931                 out(f" {node.prefix!r}", fg="green", bold=False, nl=False)
932             out(f" {node.value!r}", fg="blue", bold=False)
933
934     @classmethod
935     def show(cls, code: Union[str, Leaf, Node]) -> None:
936         """Pretty-print the lib2to3 AST of a given string of `code`.
937
938         Convenience method for debugging.
939         """
940         v: DebugVisitor[None] = DebugVisitor()
941         if isinstance(code, str):
942             code = lib2to3_parse(code)
943         list(v.visit(code))
944
945
946 WHITESPACE: Final = {token.DEDENT, token.INDENT, token.NEWLINE}
947 STATEMENT: Final = {
948     syms.if_stmt,
949     syms.while_stmt,
950     syms.for_stmt,
951     syms.try_stmt,
952     syms.except_clause,
953     syms.with_stmt,
954     syms.funcdef,
955     syms.classdef,
956 }
957 STANDALONE_COMMENT: Final = 153
958 token.tok_name[STANDALONE_COMMENT] = "STANDALONE_COMMENT"
959 LOGIC_OPERATORS: Final = {"and", "or"}
960 COMPARATORS: Final = {
961     token.LESS,
962     token.GREATER,
963     token.EQEQUAL,
964     token.NOTEQUAL,
965     token.LESSEQUAL,
966     token.GREATEREQUAL,
967 }
968 MATH_OPERATORS: Final = {
969     token.VBAR,
970     token.CIRCUMFLEX,
971     token.AMPER,
972     token.LEFTSHIFT,
973     token.RIGHTSHIFT,
974     token.PLUS,
975     token.MINUS,
976     token.STAR,
977     token.SLASH,
978     token.DOUBLESLASH,
979     token.PERCENT,
980     token.AT,
981     token.TILDE,
982     token.DOUBLESTAR,
983 }
984 STARS: Final = {token.STAR, token.DOUBLESTAR}
985 VARARGS_SPECIALS: Final = STARS | {token.SLASH}
986 VARARGS_PARENTS: Final = {
987     syms.arglist,
988     syms.argument,  # double star in arglist
989     syms.trailer,  # single argument to call
990     syms.typedargslist,
991     syms.varargslist,  # lambdas
992 }
993 UNPACKING_PARENTS: Final = {
994     syms.atom,  # single element of a list or set literal
995     syms.dictsetmaker,
996     syms.listmaker,
997     syms.testlist_gexp,
998     syms.testlist_star_expr,
999 }
1000 TEST_DESCENDANTS: Final = {
1001     syms.test,
1002     syms.lambdef,
1003     syms.or_test,
1004     syms.and_test,
1005     syms.not_test,
1006     syms.comparison,
1007     syms.star_expr,
1008     syms.expr,
1009     syms.xor_expr,
1010     syms.and_expr,
1011     syms.shift_expr,
1012     syms.arith_expr,
1013     syms.trailer,
1014     syms.term,
1015     syms.power,
1016 }
1017 ASSIGNMENTS: Final = {
1018     "=",
1019     "+=",
1020     "-=",
1021     "*=",
1022     "@=",
1023     "/=",
1024     "%=",
1025     "&=",
1026     "|=",
1027     "^=",
1028     "<<=",
1029     ">>=",
1030     "**=",
1031     "//=",
1032 }
1033 COMPREHENSION_PRIORITY: Final = 20
1034 COMMA_PRIORITY: Final = 18
1035 TERNARY_PRIORITY: Final = 16
1036 LOGIC_PRIORITY: Final = 14
1037 STRING_PRIORITY: Final = 12
1038 COMPARATOR_PRIORITY: Final = 10
1039 MATH_PRIORITIES: Final = {
1040     token.VBAR: 9,
1041     token.CIRCUMFLEX: 8,
1042     token.AMPER: 7,
1043     token.LEFTSHIFT: 6,
1044     token.RIGHTSHIFT: 6,
1045     token.PLUS: 5,
1046     token.MINUS: 5,
1047     token.STAR: 4,
1048     token.SLASH: 4,
1049     token.DOUBLESLASH: 4,
1050     token.PERCENT: 4,
1051     token.AT: 4,
1052     token.TILDE: 3,
1053     token.DOUBLESTAR: 2,
1054 }
1055 DOT_PRIORITY: Final = 1
1056
1057
1058 @dataclass
1059 class BracketTracker:
1060     """Keeps track of brackets on a line."""
1061
1062     depth: int = 0
1063     bracket_match: Dict[Tuple[Depth, NodeType], Leaf] = field(default_factory=dict)
1064     delimiters: Dict[LeafID, Priority] = field(default_factory=dict)
1065     previous: Optional[Leaf] = None
1066     _for_loop_depths: List[int] = field(default_factory=list)
1067     _lambda_argument_depths: List[int] = field(default_factory=list)
1068
1069     def mark(self, leaf: Leaf) -> None:
1070         """Mark `leaf` with bracket-related metadata. Keep track of delimiters.
1071
1072         All leaves receive an int `bracket_depth` field that stores how deep
1073         within brackets a given leaf is. 0 means there are no enclosing brackets
1074         that started on this line.
1075
1076         If a leaf is itself a closing bracket, it receives an `opening_bracket`
1077         field that it forms a pair with. This is a one-directional link to
1078         avoid reference cycles.
1079
1080         If a leaf is a delimiter (a token on which Black can split the line if
1081         needed) and it's on depth 0, its `id()` is stored in the tracker's
1082         `delimiters` field.
1083         """
1084         if leaf.type == token.COMMENT:
1085             return
1086
1087         self.maybe_decrement_after_for_loop_variable(leaf)
1088         self.maybe_decrement_after_lambda_arguments(leaf)
1089         if leaf.type in CLOSING_BRACKETS:
1090             self.depth -= 1
1091             opening_bracket = self.bracket_match.pop((self.depth, leaf.type))
1092             leaf.opening_bracket = opening_bracket
1093         leaf.bracket_depth = self.depth
1094         if self.depth == 0:
1095             delim = is_split_before_delimiter(leaf, self.previous)
1096             if delim and self.previous is not None:
1097                 self.delimiters[id(self.previous)] = delim
1098             else:
1099                 delim = is_split_after_delimiter(leaf, self.previous)
1100                 if delim:
1101                     self.delimiters[id(leaf)] = delim
1102         if leaf.type in OPENING_BRACKETS:
1103             self.bracket_match[self.depth, BRACKET[leaf.type]] = leaf
1104             self.depth += 1
1105         self.previous = leaf
1106         self.maybe_increment_lambda_arguments(leaf)
1107         self.maybe_increment_for_loop_variable(leaf)
1108
1109     def any_open_brackets(self) -> bool:
1110         """Return True if there is an yet unmatched open bracket on the line."""
1111         return bool(self.bracket_match)
1112
1113     def max_delimiter_priority(self, exclude: Iterable[LeafID] = ()) -> Priority:
1114         """Return the highest priority of a delimiter found on the line.
1115
1116         Values are consistent with what `is_split_*_delimiter()` return.
1117         Raises ValueError on no delimiters.
1118         """
1119         return max(v for k, v in self.delimiters.items() if k not in exclude)
1120
1121     def delimiter_count_with_priority(self, priority: Priority = 0) -> int:
1122         """Return the number of delimiters with the given `priority`.
1123
1124         If no `priority` is passed, defaults to max priority on the line.
1125         """
1126         if not self.delimiters:
1127             return 0
1128
1129         priority = priority or self.max_delimiter_priority()
1130         return sum(1 for p in self.delimiters.values() if p == priority)
1131
1132     def maybe_increment_for_loop_variable(self, leaf: Leaf) -> bool:
1133         """In a for loop, or comprehension, the variables are often unpacks.
1134
1135         To avoid splitting on the comma in this situation, increase the depth of
1136         tokens between `for` and `in`.
1137         """
1138         if leaf.type == token.NAME and leaf.value == "for":
1139             self.depth += 1
1140             self._for_loop_depths.append(self.depth)
1141             return True
1142
1143         return False
1144
1145     def maybe_decrement_after_for_loop_variable(self, leaf: Leaf) -> bool:
1146         """See `maybe_increment_for_loop_variable` above for explanation."""
1147         if (
1148             self._for_loop_depths
1149             and self._for_loop_depths[-1] == self.depth
1150             and leaf.type == token.NAME
1151             and leaf.value == "in"
1152         ):
1153             self.depth -= 1
1154             self._for_loop_depths.pop()
1155             return True
1156
1157         return False
1158
1159     def maybe_increment_lambda_arguments(self, leaf: Leaf) -> bool:
1160         """In a lambda expression, there might be more than one argument.
1161
1162         To avoid splitting on the comma in this situation, increase the depth of
1163         tokens between `lambda` and `:`.
1164         """
1165         if leaf.type == token.NAME and leaf.value == "lambda":
1166             self.depth += 1
1167             self._lambda_argument_depths.append(self.depth)
1168             return True
1169
1170         return False
1171
1172     def maybe_decrement_after_lambda_arguments(self, leaf: Leaf) -> bool:
1173         """See `maybe_increment_lambda_arguments` above for explanation."""
1174         if (
1175             self._lambda_argument_depths
1176             and self._lambda_argument_depths[-1] == self.depth
1177             and leaf.type == token.COLON
1178         ):
1179             self.depth -= 1
1180             self._lambda_argument_depths.pop()
1181             return True
1182
1183         return False
1184
1185     def get_open_lsqb(self) -> Optional[Leaf]:
1186         """Return the most recent opening square bracket (if any)."""
1187         return self.bracket_match.get((self.depth - 1, token.RSQB))
1188
1189
1190 @dataclass
1191 class Line:
1192     """Holds leaves and comments. Can be printed with `str(line)`."""
1193
1194     depth: int = 0
1195     leaves: List[Leaf] = field(default_factory=list)
1196     # keys ordered like `leaves`
1197     comments: Dict[LeafID, List[Leaf]] = field(default_factory=dict)
1198     bracket_tracker: BracketTracker = field(default_factory=BracketTracker)
1199     inside_brackets: bool = False
1200     should_explode: bool = False
1201
1202     def append(self, leaf: Leaf, preformatted: bool = False) -> None:
1203         """Add a new `leaf` to the end of the line.
1204
1205         Unless `preformatted` is True, the `leaf` will receive a new consistent
1206         whitespace prefix and metadata applied by :class:`BracketTracker`.
1207         Trailing commas are maybe removed, unpacked for loop variables are
1208         demoted from being delimiters.
1209
1210         Inline comments are put aside.
1211         """
1212         has_value = leaf.type in BRACKETS or bool(leaf.value.strip())
1213         if not has_value:
1214             return
1215
1216         if token.COLON == leaf.type and self.is_class_paren_empty:
1217             del self.leaves[-2:]
1218         if self.leaves and not preformatted:
1219             # Note: at this point leaf.prefix should be empty except for
1220             # imports, for which we only preserve newlines.
1221             leaf.prefix += whitespace(
1222                 leaf, complex_subscript=self.is_complex_subscript(leaf)
1223             )
1224         if self.inside_brackets or not preformatted:
1225             self.bracket_tracker.mark(leaf)
1226             self.maybe_remove_trailing_comma(leaf)
1227         if not self.append_comment(leaf):
1228             self.leaves.append(leaf)
1229
1230     def append_safe(self, leaf: Leaf, preformatted: bool = False) -> None:
1231         """Like :func:`append()` but disallow invalid standalone comment structure.
1232
1233         Raises ValueError when any `leaf` is appended after a standalone comment
1234         or when a standalone comment is not the first leaf on the line.
1235         """
1236         if self.bracket_tracker.depth == 0:
1237             if self.is_comment:
1238                 raise ValueError("cannot append to standalone comments")
1239
1240             if self.leaves and leaf.type == STANDALONE_COMMENT:
1241                 raise ValueError(
1242                     "cannot append standalone comments to a populated line"
1243                 )
1244
1245         self.append(leaf, preformatted=preformatted)
1246
1247     @property
1248     def is_comment(self) -> bool:
1249         """Is this line a standalone comment?"""
1250         return len(self.leaves) == 1 and self.leaves[0].type == STANDALONE_COMMENT
1251
1252     @property
1253     def is_decorator(self) -> bool:
1254         """Is this line a decorator?"""
1255         return bool(self) and self.leaves[0].type == token.AT
1256
1257     @property
1258     def is_import(self) -> bool:
1259         """Is this an import line?"""
1260         return bool(self) and is_import(self.leaves[0])
1261
1262     @property
1263     def is_class(self) -> bool:
1264         """Is this line a class definition?"""
1265         return (
1266             bool(self)
1267             and self.leaves[0].type == token.NAME
1268             and self.leaves[0].value == "class"
1269         )
1270
1271     @property
1272     def is_stub_class(self) -> bool:
1273         """Is this line a class definition with a body consisting only of "..."?"""
1274         return self.is_class and self.leaves[-3:] == [
1275             Leaf(token.DOT, ".") for _ in range(3)
1276         ]
1277
1278     @property
1279     def is_collection_with_optional_trailing_comma(self) -> bool:
1280         """Is this line a collection literal with a trailing comma that's optional?
1281
1282         Note that the trailing comma in a 1-tuple is not optional.
1283         """
1284         if not self.leaves or len(self.leaves) < 4:
1285             return False
1286
1287         # Look for and address a trailing colon.
1288         if self.leaves[-1].type == token.COLON:
1289             closer = self.leaves[-2]
1290             close_index = -2
1291         else:
1292             closer = self.leaves[-1]
1293             close_index = -1
1294         if closer.type not in CLOSING_BRACKETS or self.inside_brackets:
1295             return False
1296
1297         if closer.type == token.RPAR:
1298             # Tuples require an extra check, because if there's only
1299             # one element in the tuple removing the comma unmakes the
1300             # tuple.
1301             #
1302             # We also check for parens before looking for the trailing
1303             # comma because in some cases (eg assigning a dict
1304             # literal) the literal gets wrapped in temporary parens
1305             # during parsing. This case is covered by the
1306             # collections.py test data.
1307             opener = closer.opening_bracket
1308             for _open_index, leaf in enumerate(self.leaves):
1309                 if leaf is opener:
1310                     break
1311
1312             else:
1313                 # Couldn't find the matching opening paren, play it safe.
1314                 return False
1315
1316             commas = 0
1317             comma_depth = self.leaves[close_index - 1].bracket_depth
1318             for leaf in self.leaves[_open_index + 1 : close_index]:
1319                 if leaf.bracket_depth == comma_depth and leaf.type == token.COMMA:
1320                     commas += 1
1321             if commas > 1:
1322                 # We haven't looked yet for the trailing comma because
1323                 # we might also have caught noop parens.
1324                 return self.leaves[close_index - 1].type == token.COMMA
1325
1326             elif commas == 1:
1327                 return False  # it's either a one-tuple or didn't have a trailing comma
1328
1329             if self.leaves[close_index - 1].type in CLOSING_BRACKETS:
1330                 close_index -= 1
1331                 closer = self.leaves[close_index]
1332                 if closer.type == token.RPAR:
1333                     # TODO: this is a gut feeling. Will we ever see this?
1334                     return False
1335
1336         if self.leaves[close_index - 1].type != token.COMMA:
1337             return False
1338
1339         return True
1340
1341     @property
1342     def is_def(self) -> bool:
1343         """Is this a function definition? (Also returns True for async defs.)"""
1344         try:
1345             first_leaf = self.leaves[0]
1346         except IndexError:
1347             return False
1348
1349         try:
1350             second_leaf: Optional[Leaf] = self.leaves[1]
1351         except IndexError:
1352             second_leaf = None
1353         return (first_leaf.type == token.NAME and first_leaf.value == "def") or (
1354             first_leaf.type == token.ASYNC
1355             and second_leaf is not None
1356             and second_leaf.type == token.NAME
1357             and second_leaf.value == "def"
1358         )
1359
1360     @property
1361     def is_class_paren_empty(self) -> bool:
1362         """Is this a class with no base classes but using parentheses?
1363
1364         Those are unnecessary and should be removed.
1365         """
1366         return (
1367             bool(self)
1368             and len(self.leaves) == 4
1369             and self.is_class
1370             and self.leaves[2].type == token.LPAR
1371             and self.leaves[2].value == "("
1372             and self.leaves[3].type == token.RPAR
1373             and self.leaves[3].value == ")"
1374         )
1375
1376     @property
1377     def is_triple_quoted_string(self) -> bool:
1378         """Is the line a triple quoted string?"""
1379         return (
1380             bool(self)
1381             and self.leaves[0].type == token.STRING
1382             and self.leaves[0].value.startswith(('"""', "'''"))
1383         )
1384
1385     def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool:
1386         """If so, needs to be split before emitting."""
1387         for leaf in self.leaves:
1388             if leaf.type == STANDALONE_COMMENT and leaf.bracket_depth <= depth_limit:
1389                 return True
1390
1391         return False
1392
1393     def contains_uncollapsable_type_comments(self) -> bool:
1394         ignored_ids = set()
1395         try:
1396             last_leaf = self.leaves[-1]
1397             ignored_ids.add(id(last_leaf))
1398             if last_leaf.type == token.COMMA or (
1399                 last_leaf.type == token.RPAR and not last_leaf.value
1400             ):
1401                 # When trailing commas or optional parens are inserted by Black for
1402                 # consistency, comments after the previous last element are not moved
1403                 # (they don't have to, rendering will still be correct).  So we ignore
1404                 # trailing commas and invisible.
1405                 last_leaf = self.leaves[-2]
1406                 ignored_ids.add(id(last_leaf))
1407         except IndexError:
1408             return False
1409
1410         # A type comment is uncollapsable if it is attached to a leaf
1411         # that isn't at the end of the line (since that could cause it
1412         # to get associated to a different argument) or if there are
1413         # comments before it (since that could cause it to get hidden
1414         # behind a comment.
1415         comment_seen = False
1416         for leaf_id, comments in self.comments.items():
1417             for comment in comments:
1418                 if is_type_comment(comment):
1419                     if comment_seen or (
1420                         not is_type_comment(comment, " ignore")
1421                         and leaf_id not in ignored_ids
1422                     ):
1423                         return True
1424
1425                 comment_seen = True
1426
1427         return False
1428
1429     def contains_unsplittable_type_ignore(self) -> bool:
1430         if not self.leaves:
1431             return False
1432
1433         # If a 'type: ignore' is attached to the end of a line, we
1434         # can't split the line, because we can't know which of the
1435         # subexpressions the ignore was meant to apply to.
1436         #
1437         # We only want this to apply to actual physical lines from the
1438         # original source, though: we don't want the presence of a
1439         # 'type: ignore' at the end of a multiline expression to
1440         # justify pushing it all onto one line. Thus we
1441         # (unfortunately) need to check the actual source lines and
1442         # only report an unsplittable 'type: ignore' if this line was
1443         # one line in the original code.
1444
1445         # Grab the first and last line numbers, skipping generated leaves
1446         first_line = next((l.lineno for l in self.leaves if l.lineno != 0), 0)
1447         last_line = next((l.lineno for l in reversed(self.leaves) if l.lineno != 0), 0)
1448
1449         if first_line == last_line:
1450             # We look at the last two leaves since a comma or an
1451             # invisible paren could have been added at the end of the
1452             # line.
1453             for node in self.leaves[-2:]:
1454                 for comment in self.comments.get(id(node), []):
1455                     if is_type_comment(comment, " ignore"):
1456                         return True
1457
1458         return False
1459
1460     def contains_multiline_strings(self) -> bool:
1461         return any(is_multiline_string(leaf) for leaf in self.leaves)
1462
1463     def maybe_remove_trailing_comma(self, closing: Leaf) -> bool:
1464         """Remove trailing comma if there is one and it's safe."""
1465         if not (self.leaves and self.leaves[-1].type == token.COMMA):
1466             return False
1467
1468         # We remove trailing commas only in the case of importing a
1469         # single name from a module.
1470         if not (
1471             self.leaves
1472             and self.is_import
1473             and len(self.leaves) > 4
1474             and self.leaves[-1].type == token.COMMA
1475             and closing.type in CLOSING_BRACKETS
1476             and self.leaves[-4].type == token.NAME
1477             and (
1478                 # regular `from foo import bar,`
1479                 self.leaves[-4].value == "import"
1480                 # `from foo import (bar as baz,)
1481                 or (
1482                     len(self.leaves) > 6
1483                     and self.leaves[-6].value == "import"
1484                     and self.leaves[-3].value == "as"
1485                 )
1486                 # `from foo import bar as baz,`
1487                 or (
1488                     len(self.leaves) > 5
1489                     and self.leaves[-5].value == "import"
1490                     and self.leaves[-3].value == "as"
1491                 )
1492             )
1493             and closing.type == token.RPAR
1494         ):
1495             return False
1496
1497         self.remove_trailing_comma()
1498         return True
1499
1500     def append_comment(self, comment: Leaf) -> bool:
1501         """Add an inline or standalone comment to the line."""
1502         if (
1503             comment.type == STANDALONE_COMMENT
1504             and self.bracket_tracker.any_open_brackets()
1505         ):
1506             comment.prefix = ""
1507             return False
1508
1509         if comment.type != token.COMMENT:
1510             return False
1511
1512         if not self.leaves:
1513             comment.type = STANDALONE_COMMENT
1514             comment.prefix = ""
1515             return False
1516
1517         last_leaf = self.leaves[-1]
1518         if (
1519             last_leaf.type == token.RPAR
1520             and not last_leaf.value
1521             and last_leaf.parent
1522             and len(list(last_leaf.parent.leaves())) <= 3
1523             and not is_type_comment(comment)
1524         ):
1525             # Comments on an optional parens wrapping a single leaf should belong to
1526             # the wrapped node except if it's a type comment. Pinning the comment like
1527             # this avoids unstable formatting caused by comment migration.
1528             if len(self.leaves) < 2:
1529                 comment.type = STANDALONE_COMMENT
1530                 comment.prefix = ""
1531                 return False
1532
1533             last_leaf = self.leaves[-2]
1534         self.comments.setdefault(id(last_leaf), []).append(comment)
1535         return True
1536
1537     def comments_after(self, leaf: Leaf) -> List[Leaf]:
1538         """Generate comments that should appear directly after `leaf`."""
1539         return self.comments.get(id(leaf), [])
1540
1541     def remove_trailing_comma(self) -> None:
1542         """Remove the trailing comma and moves the comments attached to it."""
1543         trailing_comma = self.leaves.pop()
1544         trailing_comma_comments = self.comments.pop(id(trailing_comma), [])
1545         self.comments.setdefault(id(self.leaves[-1]), []).extend(
1546             trailing_comma_comments
1547         )
1548
1549     def is_complex_subscript(self, leaf: Leaf) -> bool:
1550         """Return True iff `leaf` is part of a slice with non-trivial exprs."""
1551         open_lsqb = self.bracket_tracker.get_open_lsqb()
1552         if open_lsqb is None:
1553             return False
1554
1555         subscript_start = open_lsqb.next_sibling
1556
1557         if isinstance(subscript_start, Node):
1558             if subscript_start.type == syms.listmaker:
1559                 return False
1560
1561             if subscript_start.type == syms.subscriptlist:
1562                 subscript_start = child_towards(subscript_start, leaf)
1563         return subscript_start is not None and any(
1564             n.type in TEST_DESCENDANTS for n in subscript_start.pre_order()
1565         )
1566
1567     def __str__(self) -> str:
1568         """Render the line."""
1569         if not self:
1570             return "\n"
1571
1572         indent = "    " * self.depth
1573         leaves = iter(self.leaves)
1574         first = next(leaves)
1575         res = f"{first.prefix}{indent}{first.value}"
1576         for leaf in leaves:
1577             res += str(leaf)
1578         for comment in itertools.chain.from_iterable(self.comments.values()):
1579             res += str(comment)
1580         return res + "\n"
1581
1582     def __bool__(self) -> bool:
1583         """Return True if the line has leaves or comments."""
1584         return bool(self.leaves or self.comments)
1585
1586
1587 @dataclass
1588 class EmptyLineTracker:
1589     """Provides a stateful method that returns the number of potential extra
1590     empty lines needed before and after the currently processed line.
1591
1592     Note: this tracker works on lines that haven't been split yet.  It assumes
1593     the prefix of the first leaf consists of optional newlines.  Those newlines
1594     are consumed by `maybe_empty_lines()` and included in the computation.
1595     """
1596
1597     is_pyi: bool = False
1598     previous_line: Optional[Line] = None
1599     previous_after: int = 0
1600     previous_defs: List[int] = field(default_factory=list)
1601
1602     def maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
1603         """Return the number of extra empty lines before and after the `current_line`.
1604
1605         This is for separating `def`, `async def` and `class` with extra empty
1606         lines (two on module-level).
1607         """
1608         before, after = self._maybe_empty_lines(current_line)
1609         before = (
1610             # Black should not insert empty lines at the beginning
1611             # of the file
1612             0
1613             if self.previous_line is None
1614             else before - self.previous_after
1615         )
1616         self.previous_after = after
1617         self.previous_line = current_line
1618         return before, after
1619
1620     def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
1621         max_allowed = 1
1622         if current_line.depth == 0:
1623             max_allowed = 1 if self.is_pyi else 2
1624         if current_line.leaves:
1625             # Consume the first leaf's extra newlines.
1626             first_leaf = current_line.leaves[0]
1627             before = first_leaf.prefix.count("\n")
1628             before = min(before, max_allowed)
1629             first_leaf.prefix = ""
1630         else:
1631             before = 0
1632         depth = current_line.depth
1633         while self.previous_defs and self.previous_defs[-1] >= depth:
1634             self.previous_defs.pop()
1635             if self.is_pyi:
1636                 before = 0 if depth else 1
1637             else:
1638                 before = 1 if depth else 2
1639         if current_line.is_decorator or current_line.is_def or current_line.is_class:
1640             return self._maybe_empty_lines_for_class_or_def(current_line, before)
1641
1642         if (
1643             self.previous_line
1644             and self.previous_line.is_import
1645             and not current_line.is_import
1646             and depth == self.previous_line.depth
1647         ):
1648             return (before or 1), 0
1649
1650         if (
1651             self.previous_line
1652             and self.previous_line.is_class
1653             and current_line.is_triple_quoted_string
1654         ):
1655             return before, 1
1656
1657         return before, 0
1658
1659     def _maybe_empty_lines_for_class_or_def(
1660         self, current_line: Line, before: int
1661     ) -> Tuple[int, int]:
1662         if not current_line.is_decorator:
1663             self.previous_defs.append(current_line.depth)
1664         if self.previous_line is None:
1665             # Don't insert empty lines before the first line in the file.
1666             return 0, 0
1667
1668         if self.previous_line.is_decorator:
1669             return 0, 0
1670
1671         if self.previous_line.depth < current_line.depth and (
1672             self.previous_line.is_class or self.previous_line.is_def
1673         ):
1674             return 0, 0
1675
1676         if (
1677             self.previous_line.is_comment
1678             and self.previous_line.depth == current_line.depth
1679             and before == 0
1680         ):
1681             return 0, 0
1682
1683         if self.is_pyi:
1684             if self.previous_line.depth > current_line.depth:
1685                 newlines = 1
1686             elif current_line.is_class or self.previous_line.is_class:
1687                 if current_line.is_stub_class and self.previous_line.is_stub_class:
1688                     # No blank line between classes with an empty body
1689                     newlines = 0
1690                 else:
1691                     newlines = 1
1692             elif current_line.is_def and not self.previous_line.is_def:
1693                 # Blank line between a block of functions and a block of non-functions
1694                 newlines = 1
1695             else:
1696                 newlines = 0
1697         else:
1698             newlines = 2
1699         if current_line.depth and newlines:
1700             newlines -= 1
1701         return newlines, 0
1702
1703
1704 @dataclass
1705 class LineGenerator(Visitor[Line]):
1706     """Generates reformatted Line objects.  Empty lines are not emitted.
1707
1708     Note: destroys the tree it's visiting by mutating prefixes of its leaves
1709     in ways that will no longer stringify to valid Python code on the tree.
1710     """
1711
1712     is_pyi: bool = False
1713     normalize_strings: bool = True
1714     current_line: Line = field(default_factory=Line)
1715     remove_u_prefix: bool = False
1716
1717     def line(self, indent: int = 0) -> Iterator[Line]:
1718         """Generate a line.
1719
1720         If the line is empty, only emit if it makes sense.
1721         If the line is too long, split it first and then generate.
1722
1723         If any lines were generated, set up a new current_line.
1724         """
1725         if not self.current_line:
1726             self.current_line.depth += indent
1727             return  # Line is empty, don't emit. Creating a new one unnecessary.
1728
1729         complete_line = self.current_line
1730         self.current_line = Line(depth=complete_line.depth + indent)
1731         yield complete_line
1732
1733     def visit_default(self, node: LN) -> Iterator[Line]:
1734         """Default `visit_*()` implementation. Recurses to children of `node`."""
1735         if isinstance(node, Leaf):
1736             any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
1737             for comment in generate_comments(node):
1738                 if any_open_brackets:
1739                     # any comment within brackets is subject to splitting
1740                     self.current_line.append(comment)
1741                 elif comment.type == token.COMMENT:
1742                     # regular trailing comment
1743                     self.current_line.append(comment)
1744                     yield from self.line()
1745
1746                 else:
1747                     # regular standalone comment
1748                     yield from self.line()
1749
1750                     self.current_line.append(comment)
1751                     yield from self.line()
1752
1753             normalize_prefix(node, inside_brackets=any_open_brackets)
1754             if self.normalize_strings and node.type == token.STRING:
1755                 normalize_string_prefix(node, remove_u_prefix=self.remove_u_prefix)
1756                 normalize_string_quotes(node)
1757             if node.type == token.NUMBER:
1758                 normalize_numeric_literal(node)
1759             if node.type not in WHITESPACE:
1760                 self.current_line.append(node)
1761         yield from super().visit_default(node)
1762
1763     def visit_INDENT(self, node: Leaf) -> Iterator[Line]:
1764         """Increase indentation level, maybe yield a line."""
1765         # In blib2to3 INDENT never holds comments.
1766         yield from self.line(+1)
1767         yield from self.visit_default(node)
1768
1769     def visit_DEDENT(self, node: Leaf) -> Iterator[Line]:
1770         """Decrease indentation level, maybe yield a line."""
1771         # The current line might still wait for trailing comments.  At DEDENT time
1772         # there won't be any (they would be prefixes on the preceding NEWLINE).
1773         # Emit the line then.
1774         yield from self.line()
1775
1776         # While DEDENT has no value, its prefix may contain standalone comments
1777         # that belong to the current indentation level.  Get 'em.
1778         yield from self.visit_default(node)
1779
1780         # Finally, emit the dedent.
1781         yield from self.line(-1)
1782
1783     def visit_stmt(
1784         self, node: Node, keywords: Set[str], parens: Set[str]
1785     ) -> Iterator[Line]:
1786         """Visit a statement.
1787
1788         This implementation is shared for `if`, `while`, `for`, `try`, `except`,
1789         `def`, `with`, `class`, `assert` and assignments.
1790
1791         The relevant Python language `keywords` for a given statement will be
1792         NAME leaves within it. This methods puts those on a separate line.
1793
1794         `parens` holds a set of string leaf values immediately after which
1795         invisible parens should be put.
1796         """
1797         normalize_invisible_parens(node, parens_after=parens)
1798         for child in node.children:
1799             if child.type == token.NAME and child.value in keywords:  # type: ignore
1800                 yield from self.line()
1801
1802             yield from self.visit(child)
1803
1804     def visit_suite(self, node: Node) -> Iterator[Line]:
1805         """Visit a suite."""
1806         if self.is_pyi and is_stub_suite(node):
1807             yield from self.visit(node.children[2])
1808         else:
1809             yield from self.visit_default(node)
1810
1811     def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
1812         """Visit a statement without nested statements."""
1813         is_suite_like = node.parent and node.parent.type in STATEMENT
1814         if is_suite_like:
1815             if self.is_pyi and is_stub_body(node):
1816                 yield from self.visit_default(node)
1817             else:
1818                 yield from self.line(+1)
1819                 yield from self.visit_default(node)
1820                 yield from self.line(-1)
1821
1822         else:
1823             if not self.is_pyi or not node.parent or not is_stub_suite(node.parent):
1824                 yield from self.line()
1825             yield from self.visit_default(node)
1826
1827     def visit_async_stmt(self, node: Node) -> Iterator[Line]:
1828         """Visit `async def`, `async for`, `async with`."""
1829         yield from self.line()
1830
1831         children = iter(node.children)
1832         for child in children:
1833             yield from self.visit(child)
1834
1835             if child.type == token.ASYNC:
1836                 break
1837
1838         internal_stmt = next(children)
1839         for child in internal_stmt.children:
1840             yield from self.visit(child)
1841
1842     def visit_decorators(self, node: Node) -> Iterator[Line]:
1843         """Visit decorators."""
1844         for child in node.children:
1845             yield from self.line()
1846             yield from self.visit(child)
1847
1848     def visit_SEMI(self, leaf: Leaf) -> Iterator[Line]:
1849         """Remove a semicolon and put the other statement on a separate line."""
1850         yield from self.line()
1851
1852     def visit_ENDMARKER(self, leaf: Leaf) -> Iterator[Line]:
1853         """End of file. Process outstanding comments and end with a newline."""
1854         yield from self.visit_default(leaf)
1855         yield from self.line()
1856
1857     def visit_STANDALONE_COMMENT(self, leaf: Leaf) -> Iterator[Line]:
1858         if not self.current_line.bracket_tracker.any_open_brackets():
1859             yield from self.line()
1860         yield from self.visit_default(leaf)
1861
1862     def visit_factor(self, node: Node) -> Iterator[Line]:
1863         """Force parentheses between a unary op and a binary power:
1864
1865         -2 ** 8 -> -(2 ** 8)
1866         """
1867         _operator, operand = node.children
1868         if (
1869             operand.type == syms.power
1870             and len(operand.children) == 3
1871             and operand.children[1].type == token.DOUBLESTAR
1872         ):
1873             lpar = Leaf(token.LPAR, "(")
1874             rpar = Leaf(token.RPAR, ")")
1875             index = operand.remove() or 0
1876             node.insert_child(index, Node(syms.atom, [lpar, operand, rpar]))
1877         yield from self.visit_default(node)
1878
1879     def __post_init__(self) -> None:
1880         """You are in a twisty little maze of passages."""
1881         v = self.visit_stmt
1882         Ø: Set[str] = set()
1883         self.visit_assert_stmt = partial(v, keywords={"assert"}, parens={"assert", ","})
1884         self.visit_if_stmt = partial(
1885             v, keywords={"if", "else", "elif"}, parens={"if", "elif"}
1886         )
1887         self.visit_while_stmt = partial(v, keywords={"while", "else"}, parens={"while"})
1888         self.visit_for_stmt = partial(v, keywords={"for", "else"}, parens={"for", "in"})
1889         self.visit_try_stmt = partial(
1890             v, keywords={"try", "except", "else", "finally"}, parens=Ø
1891         )
1892         self.visit_except_clause = partial(v, keywords={"except"}, parens=Ø)
1893         self.visit_with_stmt = partial(v, keywords={"with"}, parens=Ø)
1894         self.visit_funcdef = partial(v, keywords={"def"}, parens=Ø)
1895         self.visit_classdef = partial(v, keywords={"class"}, parens=Ø)
1896         self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS)
1897         self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"})
1898         self.visit_import_from = partial(v, keywords=Ø, parens={"import"})
1899         self.visit_del_stmt = partial(v, keywords=Ø, parens={"del"})
1900         self.visit_async_funcdef = self.visit_async_stmt
1901         self.visit_decorated = self.visit_decorators
1902
1903
1904 IMPLICIT_TUPLE = {syms.testlist, syms.testlist_star_expr, syms.exprlist}
1905 BRACKET = {token.LPAR: token.RPAR, token.LSQB: token.RSQB, token.LBRACE: token.RBRACE}
1906 OPENING_BRACKETS = set(BRACKET.keys())
1907 CLOSING_BRACKETS = set(BRACKET.values())
1908 BRACKETS = OPENING_BRACKETS | CLOSING_BRACKETS
1909 ALWAYS_NO_SPACE = CLOSING_BRACKETS | {token.COMMA, STANDALONE_COMMENT}
1910
1911
1912 def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str:  # noqa: C901
1913     """Return whitespace prefix if needed for the given `leaf`.
1914
1915     `complex_subscript` signals whether the given leaf is part of a subscription
1916     which has non-trivial arguments, like arithmetic expressions or function calls.
1917     """
1918     NO = ""
1919     SPACE = " "
1920     DOUBLESPACE = "  "
1921     t = leaf.type
1922     p = leaf.parent
1923     v = leaf.value
1924     if t in ALWAYS_NO_SPACE:
1925         return NO
1926
1927     if t == token.COMMENT:
1928         return DOUBLESPACE
1929
1930     assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
1931     if t == token.COLON and p.type not in {
1932         syms.subscript,
1933         syms.subscriptlist,
1934         syms.sliceop,
1935     }:
1936         return NO
1937
1938     prev = leaf.prev_sibling
1939     if not prev:
1940         prevp = preceding_leaf(p)
1941         if not prevp or prevp.type in OPENING_BRACKETS:
1942             return NO
1943
1944         if t == token.COLON:
1945             if prevp.type == token.COLON:
1946                 return NO
1947
1948             elif prevp.type != token.COMMA and not complex_subscript:
1949                 return NO
1950
1951             return SPACE
1952
1953         if prevp.type == token.EQUAL:
1954             if prevp.parent:
1955                 if prevp.parent.type in {
1956                     syms.arglist,
1957                     syms.argument,
1958                     syms.parameters,
1959                     syms.varargslist,
1960                 }:
1961                     return NO
1962
1963                 elif prevp.parent.type == syms.typedargslist:
1964                     # A bit hacky: if the equal sign has whitespace, it means we
1965                     # previously found it's a typed argument.  So, we're using
1966                     # that, too.
1967                     return prevp.prefix
1968
1969         elif prevp.type in VARARGS_SPECIALS:
1970             if is_vararg(prevp, within=VARARGS_PARENTS | UNPACKING_PARENTS):
1971                 return NO
1972
1973         elif prevp.type == token.COLON:
1974             if prevp.parent and prevp.parent.type in {syms.subscript, syms.sliceop}:
1975                 return SPACE if complex_subscript else NO
1976
1977         elif (
1978             prevp.parent
1979             and prevp.parent.type == syms.factor
1980             and prevp.type in MATH_OPERATORS
1981         ):
1982             return NO
1983
1984         elif (
1985             prevp.type == token.RIGHTSHIFT
1986             and prevp.parent
1987             and prevp.parent.type == syms.shift_expr
1988             and prevp.prev_sibling
1989             and prevp.prev_sibling.type == token.NAME
1990             and prevp.prev_sibling.value == "print"  # type: ignore
1991         ):
1992             # Python 2 print chevron
1993             return NO
1994
1995     elif prev.type in OPENING_BRACKETS:
1996         return NO
1997
1998     if p.type in {syms.parameters, syms.arglist}:
1999         # untyped function signatures or calls
2000         if not prev or prev.type != token.COMMA:
2001             return NO
2002
2003     elif p.type == syms.varargslist:
2004         # lambdas
2005         if prev and prev.type != token.COMMA:
2006             return NO
2007
2008     elif p.type == syms.typedargslist:
2009         # typed function signatures
2010         if not prev:
2011             return NO
2012
2013         if t == token.EQUAL:
2014             if prev.type != syms.tname:
2015                 return NO
2016
2017         elif prev.type == token.EQUAL:
2018             # A bit hacky: if the equal sign has whitespace, it means we
2019             # previously found it's a typed argument.  So, we're using that, too.
2020             return prev.prefix
2021
2022         elif prev.type != token.COMMA:
2023             return NO
2024
2025     elif p.type == syms.tname:
2026         # type names
2027         if not prev:
2028             prevp = preceding_leaf(p)
2029             if not prevp or prevp.type != token.COMMA:
2030                 return NO
2031
2032     elif p.type == syms.trailer:
2033         # attributes and calls
2034         if t == token.LPAR or t == token.RPAR:
2035             return NO
2036
2037         if not prev:
2038             if t == token.DOT:
2039                 prevp = preceding_leaf(p)
2040                 if not prevp or prevp.type != token.NUMBER:
2041                     return NO
2042
2043             elif t == token.LSQB:
2044                 return NO
2045
2046         elif prev.type != token.COMMA:
2047             return NO
2048
2049     elif p.type == syms.argument:
2050         # single argument
2051         if t == token.EQUAL:
2052             return NO
2053
2054         if not prev:
2055             prevp = preceding_leaf(p)
2056             if not prevp or prevp.type == token.LPAR:
2057                 return NO
2058
2059         elif prev.type in {token.EQUAL} | VARARGS_SPECIALS:
2060             return NO
2061
2062     elif p.type == syms.decorator:
2063         # decorators
2064         return NO
2065
2066     elif p.type == syms.dotted_name:
2067         if prev:
2068             return NO
2069
2070         prevp = preceding_leaf(p)
2071         if not prevp or prevp.type == token.AT or prevp.type == token.DOT:
2072             return NO
2073
2074     elif p.type == syms.classdef:
2075         if t == token.LPAR:
2076             return NO
2077
2078         if prev and prev.type == token.LPAR:
2079             return NO
2080
2081     elif p.type in {syms.subscript, syms.sliceop}:
2082         # indexing
2083         if not prev:
2084             assert p.parent is not None, "subscripts are always parented"
2085             if p.parent.type == syms.subscriptlist:
2086                 return SPACE
2087
2088             return NO
2089
2090         elif not complex_subscript:
2091             return NO
2092
2093     elif p.type == syms.atom:
2094         if prev and t == token.DOT:
2095             # dots, but not the first one.
2096             return NO
2097
2098     elif p.type == syms.dictsetmaker:
2099         # dict unpacking
2100         if prev and prev.type == token.DOUBLESTAR:
2101             return NO
2102
2103     elif p.type in {syms.factor, syms.star_expr}:
2104         # unary ops
2105         if not prev:
2106             prevp = preceding_leaf(p)
2107             if not prevp or prevp.type in OPENING_BRACKETS:
2108                 return NO
2109
2110             prevp_parent = prevp.parent
2111             assert prevp_parent is not None
2112             if prevp.type == token.COLON and prevp_parent.type in {
2113                 syms.subscript,
2114                 syms.sliceop,
2115             }:
2116                 return NO
2117
2118             elif prevp.type == token.EQUAL and prevp_parent.type == syms.argument:
2119                 return NO
2120
2121         elif t in {token.NAME, token.NUMBER, token.STRING}:
2122             return NO
2123
2124     elif p.type == syms.import_from:
2125         if t == token.DOT:
2126             if prev and prev.type == token.DOT:
2127                 return NO
2128
2129         elif t == token.NAME:
2130             if v == "import":
2131                 return SPACE
2132
2133             if prev and prev.type == token.DOT:
2134                 return NO
2135
2136     elif p.type == syms.sliceop:
2137         return NO
2138
2139     return SPACE
2140
2141
2142 def preceding_leaf(node: Optional[LN]) -> Optional[Leaf]:
2143     """Return the first leaf that precedes `node`, if any."""
2144     while node:
2145         res = node.prev_sibling
2146         if res:
2147             if isinstance(res, Leaf):
2148                 return res
2149
2150             try:
2151                 return list(res.leaves())[-1]
2152
2153             except IndexError:
2154                 return None
2155
2156         node = node.parent
2157     return None
2158
2159
2160 def child_towards(ancestor: Node, descendant: LN) -> Optional[LN]:
2161     """Return the child of `ancestor` that contains `descendant`."""
2162     node: Optional[LN] = descendant
2163     while node and node.parent != ancestor:
2164         node = node.parent
2165     return node
2166
2167
2168 def container_of(leaf: Leaf) -> LN:
2169     """Return `leaf` or one of its ancestors that is the topmost container of it.
2170
2171     By "container" we mean a node where `leaf` is the very first child.
2172     """
2173     same_prefix = leaf.prefix
2174     container: LN = leaf
2175     while container:
2176         parent = container.parent
2177         if parent is None:
2178             break
2179
2180         if parent.children[0].prefix != same_prefix:
2181             break
2182
2183         if parent.type == syms.file_input:
2184             break
2185
2186         if parent.prev_sibling is not None and parent.prev_sibling.type in BRACKETS:
2187             break
2188
2189         container = parent
2190     return container
2191
2192
2193 def is_split_after_delimiter(leaf: Leaf, previous: Optional[Leaf] = None) -> Priority:
2194     """Return the priority of the `leaf` delimiter, given a line break after it.
2195
2196     The delimiter priorities returned here are from those delimiters that would
2197     cause a line break after themselves.
2198
2199     Higher numbers are higher priority.
2200     """
2201     if leaf.type == token.COMMA:
2202         return COMMA_PRIORITY
2203
2204     return 0
2205
2206
2207 def is_split_before_delimiter(leaf: Leaf, previous: Optional[Leaf] = None) -> Priority:
2208     """Return the priority of the `leaf` delimiter, given a line break before it.
2209
2210     The delimiter priorities returned here are from those delimiters that would
2211     cause a line break before themselves.
2212
2213     Higher numbers are higher priority.
2214     """
2215     if is_vararg(leaf, within=VARARGS_PARENTS | UNPACKING_PARENTS):
2216         # * and ** might also be MATH_OPERATORS but in this case they are not.
2217         # Don't treat them as a delimiter.
2218         return 0
2219
2220     if (
2221         leaf.type == token.DOT
2222         and leaf.parent
2223         and leaf.parent.type not in {syms.import_from, syms.dotted_name}
2224         and (previous is None or previous.type in CLOSING_BRACKETS)
2225     ):
2226         return DOT_PRIORITY
2227
2228     if (
2229         leaf.type in MATH_OPERATORS
2230         and leaf.parent
2231         and leaf.parent.type not in {syms.factor, syms.star_expr}
2232     ):
2233         return MATH_PRIORITIES[leaf.type]
2234
2235     if leaf.type in COMPARATORS:
2236         return COMPARATOR_PRIORITY
2237
2238     if (
2239         leaf.type == token.STRING
2240         and previous is not None
2241         and previous.type == token.STRING
2242     ):
2243         return STRING_PRIORITY
2244
2245     if leaf.type not in {token.NAME, token.ASYNC}:
2246         return 0
2247
2248     if (
2249         leaf.value == "for"
2250         and leaf.parent
2251         and leaf.parent.type in {syms.comp_for, syms.old_comp_for}
2252         or leaf.type == token.ASYNC
2253     ):
2254         if (
2255             not isinstance(leaf.prev_sibling, Leaf)
2256             or leaf.prev_sibling.value != "async"
2257         ):
2258             return COMPREHENSION_PRIORITY
2259
2260     if (
2261         leaf.value == "if"
2262         and leaf.parent
2263         and leaf.parent.type in {syms.comp_if, syms.old_comp_if}
2264     ):
2265         return COMPREHENSION_PRIORITY
2266
2267     if leaf.value in {"if", "else"} and leaf.parent and leaf.parent.type == syms.test:
2268         return TERNARY_PRIORITY
2269
2270     if leaf.value == "is":
2271         return COMPARATOR_PRIORITY
2272
2273     if (
2274         leaf.value == "in"
2275         and leaf.parent
2276         and leaf.parent.type in {syms.comp_op, syms.comparison}
2277         and not (
2278             previous is not None
2279             and previous.type == token.NAME
2280             and previous.value == "not"
2281         )
2282     ):
2283         return COMPARATOR_PRIORITY
2284
2285     if (
2286         leaf.value == "not"
2287         and leaf.parent
2288         and leaf.parent.type == syms.comp_op
2289         and not (
2290             previous is not None
2291             and previous.type == token.NAME
2292             and previous.value == "is"
2293         )
2294     ):
2295         return COMPARATOR_PRIORITY
2296
2297     if leaf.value in LOGIC_OPERATORS and leaf.parent:
2298         return LOGIC_PRIORITY
2299
2300     return 0
2301
2302
2303 FMT_OFF = {"# fmt: off", "# fmt:off", "# yapf: disable"}
2304 FMT_ON = {"# fmt: on", "# fmt:on", "# yapf: enable"}
2305
2306
2307 def generate_comments(leaf: LN) -> Iterator[Leaf]:
2308     """Clean the prefix of the `leaf` and generate comments from it, if any.
2309
2310     Comments in lib2to3 are shoved into the whitespace prefix.  This happens
2311     in `pgen2/driver.py:Driver.parse_tokens()`.  This was a brilliant implementation
2312     move because it does away with modifying the grammar to include all the
2313     possible places in which comments can be placed.
2314
2315     The sad consequence for us though is that comments don't "belong" anywhere.
2316     This is why this function generates simple parentless Leaf objects for
2317     comments.  We simply don't know what the correct parent should be.
2318
2319     No matter though, we can live without this.  We really only need to
2320     differentiate between inline and standalone comments.  The latter don't
2321     share the line with any code.
2322
2323     Inline comments are emitted as regular token.COMMENT leaves.  Standalone
2324     are emitted with a fake STANDALONE_COMMENT token identifier.
2325     """
2326     for pc in list_comments(leaf.prefix, is_endmarker=leaf.type == token.ENDMARKER):
2327         yield Leaf(pc.type, pc.value, prefix="\n" * pc.newlines)
2328
2329
2330 @dataclass
2331 class ProtoComment:
2332     """Describes a piece of syntax that is a comment.
2333
2334     It's not a :class:`blib2to3.pytree.Leaf` so that:
2335
2336     * it can be cached (`Leaf` objects should not be reused more than once as
2337       they store their lineno, column, prefix, and parent information);
2338     * `newlines` and `consumed` fields are kept separate from the `value`. This
2339       simplifies handling of special marker comments like ``# fmt: off/on``.
2340     """
2341
2342     type: int  # token.COMMENT or STANDALONE_COMMENT
2343     value: str  # content of the comment
2344     newlines: int  # how many newlines before the comment
2345     consumed: int  # how many characters of the original leaf's prefix did we consume
2346
2347
2348 @lru_cache(maxsize=4096)
2349 def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]:
2350     """Return a list of :class:`ProtoComment` objects parsed from the given `prefix`."""
2351     result: List[ProtoComment] = []
2352     if not prefix or "#" not in prefix:
2353         return result
2354
2355     consumed = 0
2356     nlines = 0
2357     ignored_lines = 0
2358     for index, line in enumerate(prefix.split("\n")):
2359         consumed += len(line) + 1  # adding the length of the split '\n'
2360         line = line.lstrip()
2361         if not line:
2362             nlines += 1
2363         if not line.startswith("#"):
2364             # Escaped newlines outside of a comment are not really newlines at
2365             # all. We treat a single-line comment following an escaped newline
2366             # as a simple trailing comment.
2367             if line.endswith("\\"):
2368                 ignored_lines += 1
2369             continue
2370
2371         if index == ignored_lines and not is_endmarker:
2372             comment_type = token.COMMENT  # simple trailing comment
2373         else:
2374             comment_type = STANDALONE_COMMENT
2375         comment = make_comment(line)
2376         result.append(
2377             ProtoComment(
2378                 type=comment_type, value=comment, newlines=nlines, consumed=consumed
2379             )
2380         )
2381         nlines = 0
2382     return result
2383
2384
2385 def make_comment(content: str) -> str:
2386     """Return a consistently formatted comment from the given `content` string.
2387
2388     All comments (except for "##", "#!", "#:", '#'", "#%%") should have a single
2389     space between the hash sign and the content.
2390
2391     If `content` didn't start with a hash sign, one is provided.
2392     """
2393     content = content.rstrip()
2394     if not content:
2395         return "#"
2396
2397     if content[0] == "#":
2398         content = content[1:]
2399     if content and content[0] not in " !:#'%":
2400         content = " " + content
2401     return "#" + content
2402
2403
2404 def split_line(
2405     line: Line,
2406     line_length: int,
2407     inner: bool = False,
2408     features: Collection[Feature] = (),
2409 ) -> Iterator[Line]:
2410     """Split a `line` into potentially many lines.
2411
2412     They should fit in the allotted `line_length` but might not be able to.
2413     `inner` signifies that there were a pair of brackets somewhere around the
2414     current `line`, possibly transitively. This means we can fallback to splitting
2415     by delimiters if the LHS/RHS don't yield any results.
2416
2417     `features` are syntactical features that may be used in the output.
2418     """
2419     if line.is_comment:
2420         yield line
2421         return
2422
2423     line_str = str(line).strip("\n")
2424
2425     if (
2426         not line.contains_uncollapsable_type_comments()
2427         and not line.should_explode
2428         and not line.is_collection_with_optional_trailing_comma
2429         and (
2430             is_line_short_enough(line, line_length=line_length, line_str=line_str)
2431             or line.contains_unsplittable_type_ignore()
2432         )
2433     ):
2434         yield line
2435         return
2436
2437     split_funcs: List[SplitFunc]
2438     if line.is_def:
2439         split_funcs = [left_hand_split]
2440     else:
2441
2442         def rhs(line: Line, features: Collection[Feature]) -> Iterator[Line]:
2443             for omit in generate_trailers_to_omit(line, line_length):
2444                 lines = list(right_hand_split(line, line_length, features, omit=omit))
2445                 if is_line_short_enough(lines[0], line_length=line_length):
2446                     yield from lines
2447                     return
2448
2449             # All splits failed, best effort split with no omits.
2450             # This mostly happens to multiline strings that are by definition
2451             # reported as not fitting a single line.
2452             # line_length=1 here was historically a bug that somehow became a feature.
2453             # See #762 and #781 for the full story.
2454             yield from right_hand_split(line, line_length=1, features=features)
2455
2456         if line.inside_brackets:
2457             split_funcs = [delimiter_split, standalone_comment_split, rhs]
2458         else:
2459             split_funcs = [rhs]
2460     for split_func in split_funcs:
2461         # We are accumulating lines in `result` because we might want to abort
2462         # mission and return the original line in the end, or attempt a different
2463         # split altogether.
2464         result: List[Line] = []
2465         try:
2466             for l in split_func(line, features):
2467                 if str(l).strip("\n") == line_str:
2468                     raise CannotSplit("Split function returned an unchanged result")
2469
2470                 result.extend(
2471                     split_line(
2472                         l, line_length=line_length, inner=True, features=features
2473                     )
2474                 )
2475         except CannotSplit:
2476             continue
2477
2478         else:
2479             yield from result
2480             break
2481
2482     else:
2483         yield line
2484
2485
2486 def left_hand_split(line: Line, features: Collection[Feature] = ()) -> Iterator[Line]:
2487     """Split line into many lines, starting with the first matching bracket pair.
2488
2489     Note: this usually looks weird, only use this for function definitions.
2490     Prefer RHS otherwise.  This is why this function is not symmetrical with
2491     :func:`right_hand_split` which also handles optional parentheses.
2492     """
2493     tail_leaves: List[Leaf] = []
2494     body_leaves: List[Leaf] = []
2495     head_leaves: List[Leaf] = []
2496     current_leaves = head_leaves
2497     matching_bracket: Optional[Leaf] = None
2498     for leaf in line.leaves:
2499         if (
2500             current_leaves is body_leaves
2501             and leaf.type in CLOSING_BRACKETS
2502             and leaf.opening_bracket is matching_bracket
2503         ):
2504             current_leaves = tail_leaves if body_leaves else head_leaves
2505         current_leaves.append(leaf)
2506         if current_leaves is head_leaves:
2507             if leaf.type in OPENING_BRACKETS:
2508                 matching_bracket = leaf
2509                 current_leaves = body_leaves
2510     if not matching_bracket:
2511         raise CannotSplit("No brackets found")
2512
2513     head = bracket_split_build_line(head_leaves, line, matching_bracket)
2514     body = bracket_split_build_line(body_leaves, line, matching_bracket, is_body=True)
2515     tail = bracket_split_build_line(tail_leaves, line, matching_bracket)
2516     bracket_split_succeeded_or_raise(head, body, tail)
2517     for result in (head, body, tail):
2518         if result:
2519             yield result
2520
2521
2522 def right_hand_split(
2523     line: Line,
2524     line_length: int,
2525     features: Collection[Feature] = (),
2526     omit: Collection[LeafID] = (),
2527 ) -> Iterator[Line]:
2528     """Split line into many lines, starting with the last matching bracket pair.
2529
2530     If the split was by optional parentheses, attempt splitting without them, too.
2531     `omit` is a collection of closing bracket IDs that shouldn't be considered for
2532     this split.
2533
2534     Note: running this function modifies `bracket_depth` on the leaves of `line`.
2535     """
2536     tail_leaves: List[Leaf] = []
2537     body_leaves: List[Leaf] = []
2538     head_leaves: List[Leaf] = []
2539     current_leaves = tail_leaves
2540     opening_bracket: Optional[Leaf] = None
2541     closing_bracket: Optional[Leaf] = None
2542     for leaf in reversed(line.leaves):
2543         if current_leaves is body_leaves:
2544             if leaf is opening_bracket:
2545                 current_leaves = head_leaves if body_leaves else tail_leaves
2546         current_leaves.append(leaf)
2547         if current_leaves is tail_leaves:
2548             if leaf.type in CLOSING_BRACKETS and id(leaf) not in omit:
2549                 opening_bracket = leaf.opening_bracket
2550                 closing_bracket = leaf
2551                 current_leaves = body_leaves
2552     if not (opening_bracket and closing_bracket and head_leaves):
2553         # If there is no opening or closing_bracket that means the split failed and
2554         # all content is in the tail.  Otherwise, if `head_leaves` are empty, it means
2555         # the matching `opening_bracket` wasn't available on `line` anymore.
2556         raise CannotSplit("No brackets found")
2557
2558     tail_leaves.reverse()
2559     body_leaves.reverse()
2560     head_leaves.reverse()
2561     head = bracket_split_build_line(head_leaves, line, opening_bracket)
2562     body = bracket_split_build_line(body_leaves, line, opening_bracket, is_body=True)
2563     tail = bracket_split_build_line(tail_leaves, line, opening_bracket)
2564     bracket_split_succeeded_or_raise(head, body, tail)
2565     if (
2566         # the body shouldn't be exploded
2567         not body.should_explode
2568         # the opening bracket is an optional paren
2569         and opening_bracket.type == token.LPAR
2570         and not opening_bracket.value
2571         # the closing bracket is an optional paren
2572         and closing_bracket.type == token.RPAR
2573         and not closing_bracket.value
2574         # it's not an import (optional parens are the only thing we can split on
2575         # in this case; attempting a split without them is a waste of time)
2576         and not line.is_import
2577         # there are no standalone comments in the body
2578         and not body.contains_standalone_comments(0)
2579         # and we can actually remove the parens
2580         and can_omit_invisible_parens(body, line_length)
2581     ):
2582         omit = {id(closing_bracket), *omit}
2583         try:
2584             yield from right_hand_split(line, line_length, features=features, omit=omit)
2585             return
2586
2587         except CannotSplit:
2588             if not (
2589                 can_be_split(body)
2590                 or is_line_short_enough(body, line_length=line_length)
2591             ):
2592                 raise CannotSplit(
2593                     "Splitting failed, body is still too long and can't be split."
2594                 )
2595
2596             elif head.contains_multiline_strings() or tail.contains_multiline_strings():
2597                 raise CannotSplit(
2598                     "The current optional pair of parentheses is bound to fail to "
2599                     "satisfy the splitting algorithm because the head or the tail "
2600                     "contains multiline strings which by definition never fit one "
2601                     "line."
2602                 )
2603
2604     ensure_visible(opening_bracket)
2605     ensure_visible(closing_bracket)
2606     for result in (head, body, tail):
2607         if result:
2608             yield result
2609
2610
2611 def bracket_split_succeeded_or_raise(head: Line, body: Line, tail: Line) -> None:
2612     """Raise :exc:`CannotSplit` if the last left- or right-hand split failed.
2613
2614     Do nothing otherwise.
2615
2616     A left- or right-hand split is based on a pair of brackets. Content before
2617     (and including) the opening bracket is left on one line, content inside the
2618     brackets is put on a separate line, and finally content starting with and
2619     following the closing bracket is put on a separate line.
2620
2621     Those are called `head`, `body`, and `tail`, respectively. If the split
2622     produced the same line (all content in `head`) or ended up with an empty `body`
2623     and the `tail` is just the closing bracket, then it's considered failed.
2624     """
2625     tail_len = len(str(tail).strip())
2626     if not body:
2627         if tail_len == 0:
2628             raise CannotSplit("Splitting brackets produced the same line")
2629
2630         elif tail_len < 3:
2631             raise CannotSplit(
2632                 f"Splitting brackets on an empty body to save "
2633                 f"{tail_len} characters is not worth it"
2634             )
2635
2636
2637 def bracket_split_build_line(
2638     leaves: List[Leaf], original: Line, opening_bracket: Leaf, *, is_body: bool = False
2639 ) -> Line:
2640     """Return a new line with given `leaves` and respective comments from `original`.
2641
2642     If `is_body` is True, the result line is one-indented inside brackets and as such
2643     has its first leaf's prefix normalized and a trailing comma added when expected.
2644     """
2645     result = Line(depth=original.depth)
2646     if is_body:
2647         result.inside_brackets = True
2648         result.depth += 1
2649         if leaves:
2650             # Since body is a new indent level, remove spurious leading whitespace.
2651             normalize_prefix(leaves[0], inside_brackets=True)
2652             # Ensure a trailing comma for imports and standalone function arguments, but
2653             # be careful not to add one after any comments or within type annotations.
2654             no_commas = (
2655                 original.is_def
2656                 and opening_bracket.value == "("
2657                 and not any(l.type == token.COMMA for l in leaves)
2658             )
2659
2660             if original.is_import or no_commas:
2661                 for i in range(len(leaves) - 1, -1, -1):
2662                     if leaves[i].type == STANDALONE_COMMENT:
2663                         continue
2664
2665                     if leaves[i].type != token.COMMA:
2666                         leaves.insert(i + 1, Leaf(token.COMMA, ","))
2667                     break
2668
2669     # Populate the line
2670     for leaf in leaves:
2671         result.append(leaf, preformatted=True)
2672         for comment_after in original.comments_after(leaf):
2673             result.append(comment_after, preformatted=True)
2674     if is_body:
2675         result.should_explode = should_explode(result, opening_bracket)
2676     return result
2677
2678
2679 def dont_increase_indentation(split_func: SplitFunc) -> SplitFunc:
2680     """Normalize prefix of the first leaf in every line returned by `split_func`.
2681
2682     This is a decorator over relevant split functions.
2683     """
2684
2685     @wraps(split_func)
2686     def split_wrapper(line: Line, features: Collection[Feature] = ()) -> Iterator[Line]:
2687         for l in split_func(line, features):
2688             normalize_prefix(l.leaves[0], inside_brackets=True)
2689             yield l
2690
2691     return split_wrapper
2692
2693
2694 @dont_increase_indentation
2695 def delimiter_split(line: Line, features: Collection[Feature] = ()) -> Iterator[Line]:
2696     """Split according to delimiters of the highest priority.
2697
2698     If the appropriate Features are given, the split will add trailing commas
2699     also in function signatures and calls that contain `*` and `**`.
2700     """
2701     try:
2702         last_leaf = line.leaves[-1]
2703     except IndexError:
2704         raise CannotSplit("Line empty")
2705
2706     bt = line.bracket_tracker
2707     try:
2708         delimiter_priority = bt.max_delimiter_priority(exclude={id(last_leaf)})
2709     except ValueError:
2710         raise CannotSplit("No delimiters found")
2711
2712     if delimiter_priority == DOT_PRIORITY:
2713         if bt.delimiter_count_with_priority(delimiter_priority) == 1:
2714             raise CannotSplit("Splitting a single attribute from its owner looks wrong")
2715
2716     current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
2717     lowest_depth = sys.maxsize
2718     trailing_comma_safe = True
2719
2720     def append_to_line(leaf: Leaf) -> Iterator[Line]:
2721         """Append `leaf` to current line or to new line if appending impossible."""
2722         nonlocal current_line
2723         try:
2724             current_line.append_safe(leaf, preformatted=True)
2725         except ValueError:
2726             yield current_line
2727
2728             current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
2729             current_line.append(leaf)
2730
2731     for leaf in line.leaves:
2732         yield from append_to_line(leaf)
2733
2734         for comment_after in line.comments_after(leaf):
2735             yield from append_to_line(comment_after)
2736
2737         lowest_depth = min(lowest_depth, leaf.bracket_depth)
2738         if leaf.bracket_depth == lowest_depth:
2739             if is_vararg(leaf, within={syms.typedargslist}):
2740                 trailing_comma_safe = (
2741                     trailing_comma_safe and Feature.TRAILING_COMMA_IN_DEF in features
2742                 )
2743             elif is_vararg(leaf, within={syms.arglist, syms.argument}):
2744                 trailing_comma_safe = (
2745                     trailing_comma_safe and Feature.TRAILING_COMMA_IN_CALL in features
2746                 )
2747
2748         leaf_priority = bt.delimiters.get(id(leaf))
2749         if leaf_priority == delimiter_priority:
2750             yield current_line
2751
2752             current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
2753     if current_line:
2754         if (
2755             trailing_comma_safe
2756             and delimiter_priority == COMMA_PRIORITY
2757             and current_line.leaves[-1].type != token.COMMA
2758             and current_line.leaves[-1].type != STANDALONE_COMMENT
2759         ):
2760             current_line.append(Leaf(token.COMMA, ","))
2761         yield current_line
2762
2763
2764 @dont_increase_indentation
2765 def standalone_comment_split(
2766     line: Line, features: Collection[Feature] = ()
2767 ) -> Iterator[Line]:
2768     """Split standalone comments from the rest of the line."""
2769     if not line.contains_standalone_comments(0):
2770         raise CannotSplit("Line does not have any standalone comments")
2771
2772     current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
2773
2774     def append_to_line(leaf: Leaf) -> Iterator[Line]:
2775         """Append `leaf` to current line or to new line if appending impossible."""
2776         nonlocal current_line
2777         try:
2778             current_line.append_safe(leaf, preformatted=True)
2779         except ValueError:
2780             yield current_line
2781
2782             current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
2783             current_line.append(leaf)
2784
2785     for leaf in line.leaves:
2786         yield from append_to_line(leaf)
2787
2788         for comment_after in line.comments_after(leaf):
2789             yield from append_to_line(comment_after)
2790
2791     if current_line:
2792         yield current_line
2793
2794
2795 def is_import(leaf: Leaf) -> bool:
2796     """Return True if the given leaf starts an import statement."""
2797     p = leaf.parent
2798     t = leaf.type
2799     v = leaf.value
2800     return bool(
2801         t == token.NAME
2802         and (
2803             (v == "import" and p and p.type == syms.import_name)
2804             or (v == "from" and p and p.type == syms.import_from)
2805         )
2806     )
2807
2808
2809 def is_type_comment(leaf: Leaf, suffix: str = "") -> bool:
2810     """Return True if the given leaf is a special comment.
2811     Only returns true for type comments for now."""
2812     t = leaf.type
2813     v = leaf.value
2814     return t in {token.COMMENT, STANDALONE_COMMENT} and v.startswith("# type:" + suffix)
2815
2816
2817 def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None:
2818     """Leave existing extra newlines if not `inside_brackets`. Remove everything
2819     else.
2820
2821     Note: don't use backslashes for formatting or you'll lose your voting rights.
2822     """
2823     if not inside_brackets:
2824         spl = leaf.prefix.split("#")
2825         if "\\" not in spl[0]:
2826             nl_count = spl[-1].count("\n")
2827             if len(spl) > 1:
2828                 nl_count -= 1
2829             leaf.prefix = "\n" * nl_count
2830             return
2831
2832     leaf.prefix = ""
2833
2834
2835 def normalize_string_prefix(leaf: Leaf, remove_u_prefix: bool = False) -> None:
2836     """Make all string prefixes lowercase.
2837
2838     If remove_u_prefix is given, also removes any u prefix from the string.
2839
2840     Note: Mutates its argument.
2841     """
2842     match = re.match(r"^([furbFURB]*)(.*)$", leaf.value, re.DOTALL)
2843     assert match is not None, f"failed to match string {leaf.value!r}"
2844     orig_prefix = match.group(1)
2845     new_prefix = orig_prefix.replace("F", "f").replace("B", "b").replace("U", "u")
2846     if remove_u_prefix:
2847         new_prefix = new_prefix.replace("u", "")
2848     leaf.value = f"{new_prefix}{match.group(2)}"
2849
2850
2851 def normalize_string_quotes(leaf: Leaf) -> None:
2852     """Prefer double quotes but only if it doesn't cause more escaping.
2853
2854     Adds or removes backslashes as appropriate. Doesn't parse and fix
2855     strings nested in f-strings (yet).
2856
2857     Note: Mutates its argument.
2858     """
2859     value = leaf.value.lstrip("furbFURB")
2860     if value[:3] == '"""':
2861         return
2862
2863     elif value[:3] == "'''":
2864         orig_quote = "'''"
2865         new_quote = '"""'
2866     elif value[0] == '"':
2867         orig_quote = '"'
2868         new_quote = "'"
2869     else:
2870         orig_quote = "'"
2871         new_quote = '"'
2872     first_quote_pos = leaf.value.find(orig_quote)
2873     if first_quote_pos == -1:
2874         return  # There's an internal error
2875
2876     prefix = leaf.value[:first_quote_pos]
2877     unescaped_new_quote = re.compile(rf"(([^\\]|^)(\\\\)*){new_quote}")
2878     escaped_new_quote = re.compile(rf"([^\\]|^)\\((?:\\\\)*){new_quote}")
2879     escaped_orig_quote = re.compile(rf"([^\\]|^)\\((?:\\\\)*){orig_quote}")
2880     body = leaf.value[first_quote_pos + len(orig_quote) : -len(orig_quote)]
2881     if "r" in prefix.casefold():
2882         if unescaped_new_quote.search(body):
2883             # There's at least one unescaped new_quote in this raw string
2884             # so converting is impossible
2885             return
2886
2887         # Do not introduce or remove backslashes in raw strings
2888         new_body = body
2889     else:
2890         # remove unnecessary escapes
2891         new_body = sub_twice(escaped_new_quote, rf"\1\2{new_quote}", body)
2892         if body != new_body:
2893             # Consider the string without unnecessary escapes as the original
2894             body = new_body
2895             leaf.value = f"{prefix}{orig_quote}{body}{orig_quote}"
2896         new_body = sub_twice(escaped_orig_quote, rf"\1\2{orig_quote}", new_body)
2897         new_body = sub_twice(unescaped_new_quote, rf"\1\\{new_quote}", new_body)
2898     if "f" in prefix.casefold():
2899         matches = re.findall(
2900             r"""
2901             (?:[^{]|^)\{  # start of the string or a non-{ followed by a single {
2902                 ([^{].*?)  # contents of the brackets except if begins with {{
2903             \}(?:[^}]|$)  # A } followed by end of the string or a non-}
2904             """,
2905             new_body,
2906             re.VERBOSE,
2907         )
2908         for m in matches:
2909             if "\\" in str(m):
2910                 # Do not introduce backslashes in interpolated expressions
2911                 return
2912
2913     if new_quote == '"""' and new_body[-1:] == '"':
2914         # edge case:
2915         new_body = new_body[:-1] + '\\"'
2916     orig_escape_count = body.count("\\")
2917     new_escape_count = new_body.count("\\")
2918     if new_escape_count > orig_escape_count:
2919         return  # Do not introduce more escaping
2920
2921     if new_escape_count == orig_escape_count and orig_quote == '"':
2922         return  # Prefer double quotes
2923
2924     leaf.value = f"{prefix}{new_quote}{new_body}{new_quote}"
2925
2926
2927 def normalize_numeric_literal(leaf: Leaf) -> None:
2928     """Normalizes numeric (float, int, and complex) literals.
2929
2930     All letters used in the representation are normalized to lowercase (except
2931     in Python 2 long literals).
2932     """
2933     text = leaf.value.lower()
2934     if text.startswith(("0o", "0b")):
2935         # Leave octal and binary literals alone.
2936         pass
2937     elif text.startswith("0x"):
2938         # Change hex literals to upper case.
2939         before, after = text[:2], text[2:]
2940         text = f"{before}{after.upper()}"
2941     elif "e" in text:
2942         before, after = text.split("e")
2943         sign = ""
2944         if after.startswith("-"):
2945             after = after[1:]
2946             sign = "-"
2947         elif after.startswith("+"):
2948             after = after[1:]
2949         before = format_float_or_int_string(before)
2950         text = f"{before}e{sign}{after}"
2951     elif text.endswith(("j", "l")):
2952         number = text[:-1]
2953         suffix = text[-1]
2954         # Capitalize in "2L" because "l" looks too similar to "1".
2955         if suffix == "l":
2956             suffix = "L"
2957         text = f"{format_float_or_int_string(number)}{suffix}"
2958     else:
2959         text = format_float_or_int_string(text)
2960     leaf.value = text
2961
2962
2963 def format_float_or_int_string(text: str) -> str:
2964     """Formats a float string like "1.0"."""
2965     if "." not in text:
2966         return text
2967
2968     before, after = text.split(".")
2969     return f"{before or 0}.{after or 0}"
2970
2971
2972 def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
2973     """Make existing optional parentheses invisible or create new ones.
2974
2975     `parens_after` is a set of string leaf values immediately after which parens
2976     should be put.
2977
2978     Standardizes on visible parentheses for single-element tuples, and keeps
2979     existing visible parentheses for other tuples and generator expressions.
2980     """
2981     for pc in list_comments(node.prefix, is_endmarker=False):
2982         if pc.value in FMT_OFF:
2983             # This `node` has a prefix with `# fmt: off`, don't mess with parens.
2984             return
2985     check_lpar = False
2986     for index, child in enumerate(list(node.children)):
2987         # Add parentheses around long tuple unpacking in assignments.
2988         if (
2989             index == 0
2990             and isinstance(child, Node)
2991             and child.type == syms.testlist_star_expr
2992         ):
2993             check_lpar = True
2994
2995         if check_lpar:
2996             if is_walrus_assignment(child):
2997                 continue
2998
2999             if child.type == syms.atom:
3000                 if maybe_make_parens_invisible_in_atom(child, parent=node):
3001                     wrap_in_parentheses(node, child, visible=False)
3002             elif is_one_tuple(child):
3003                 wrap_in_parentheses(node, child, visible=True)
3004             elif node.type == syms.import_from:
3005                 # "import from" nodes store parentheses directly as part of
3006                 # the statement
3007                 if child.type == token.LPAR:
3008                     # make parentheses invisible
3009                     child.value = ""  # type: ignore
3010                     node.children[-1].value = ""  # type: ignore
3011                 elif child.type != token.STAR:
3012                     # insert invisible parentheses
3013                     node.insert_child(index, Leaf(token.LPAR, ""))
3014                     node.append_child(Leaf(token.RPAR, ""))
3015                 break
3016
3017             elif not (isinstance(child, Leaf) and is_multiline_string(child)):
3018                 wrap_in_parentheses(node, child, visible=False)
3019
3020         check_lpar = isinstance(child, Leaf) and child.value in parens_after
3021
3022
3023 def normalize_fmt_off(node: Node) -> None:
3024     """Convert content between `# fmt: off`/`# fmt: on` into standalone comments."""
3025     try_again = True
3026     while try_again:
3027         try_again = convert_one_fmt_off_pair(node)
3028
3029
3030 def convert_one_fmt_off_pair(node: Node) -> bool:
3031     """Convert content of a single `# fmt: off`/`# fmt: on` into a standalone comment.
3032
3033     Returns True if a pair was converted.
3034     """
3035     for leaf in node.leaves():
3036         previous_consumed = 0
3037         for comment in list_comments(leaf.prefix, is_endmarker=False):
3038             if comment.value in FMT_OFF:
3039                 # We only want standalone comments. If there's no previous leaf or
3040                 # the previous leaf is indentation, it's a standalone comment in
3041                 # disguise.
3042                 if comment.type != STANDALONE_COMMENT:
3043                     prev = preceding_leaf(leaf)
3044                     if prev and prev.type not in WHITESPACE:
3045                         continue
3046
3047                 ignored_nodes = list(generate_ignored_nodes(leaf))
3048                 if not ignored_nodes:
3049                     continue
3050
3051                 first = ignored_nodes[0]  # Can be a container node with the `leaf`.
3052                 parent = first.parent
3053                 prefix = first.prefix
3054                 first.prefix = prefix[comment.consumed :]
3055                 hidden_value = (
3056                     comment.value + "\n" + "".join(str(n) for n in ignored_nodes)
3057                 )
3058                 if hidden_value.endswith("\n"):
3059                     # That happens when one of the `ignored_nodes` ended with a NEWLINE
3060                     # leaf (possibly followed by a DEDENT).
3061                     hidden_value = hidden_value[:-1]
3062                 first_idx: Optional[int] = None
3063                 for ignored in ignored_nodes:
3064                     index = ignored.remove()
3065                     if first_idx is None:
3066                         first_idx = index
3067                 assert parent is not None, "INTERNAL ERROR: fmt: on/off handling (1)"
3068                 assert first_idx is not None, "INTERNAL ERROR: fmt: on/off handling (2)"
3069                 parent.insert_child(
3070                     first_idx,
3071                     Leaf(
3072                         STANDALONE_COMMENT,
3073                         hidden_value,
3074                         prefix=prefix[:previous_consumed] + "\n" * comment.newlines,
3075                     ),
3076                 )
3077                 return True
3078
3079             previous_consumed = comment.consumed
3080
3081     return False
3082
3083
3084 def generate_ignored_nodes(leaf: Leaf) -> Iterator[LN]:
3085     """Starting from the container of `leaf`, generate all leaves until `# fmt: on`.
3086
3087     Stops at the end of the block.
3088     """
3089     container: Optional[LN] = container_of(leaf)
3090     while container is not None and container.type != token.ENDMARKER:
3091         is_fmt_on = False
3092         for comment in list_comments(container.prefix, is_endmarker=False):
3093             if comment.value in FMT_ON:
3094                 is_fmt_on = True
3095             elif comment.value in FMT_OFF:
3096                 is_fmt_on = False
3097         if is_fmt_on:
3098             return
3099
3100         yield container
3101
3102         container = container.next_sibling
3103
3104
3105 def maybe_make_parens_invisible_in_atom(node: LN, parent: LN) -> bool:
3106     """If it's safe, make the parens in the atom `node` invisible, recursively.
3107     Additionally, remove repeated, adjacent invisible parens from the atom `node`
3108     as they are redundant.
3109
3110     Returns whether the node should itself be wrapped in invisible parentheses.
3111
3112     """
3113     if (
3114         node.type != syms.atom
3115         or is_empty_tuple(node)
3116         or is_one_tuple(node)
3117         or (is_yield(node) and parent.type != syms.expr_stmt)
3118         or max_delimiter_priority_in_atom(node) >= COMMA_PRIORITY
3119     ):
3120         return False
3121
3122     first = node.children[0]
3123     last = node.children[-1]
3124     if first.type == token.LPAR and last.type == token.RPAR:
3125         middle = node.children[1]
3126         # make parentheses invisible
3127         first.value = ""  # type: ignore
3128         last.value = ""  # type: ignore
3129         maybe_make_parens_invisible_in_atom(middle, parent=parent)
3130
3131         if is_atom_with_invisible_parens(middle):
3132             # Strip the invisible parens from `middle` by replacing
3133             # it with the child in-between the invisible parens
3134             middle.replace(middle.children[1])
3135
3136         return False
3137
3138     return True
3139
3140
3141 def is_atom_with_invisible_parens(node: LN) -> bool:
3142     """Given a `LN`, determines whether it's an atom `node` with invisible
3143     parens. Useful in dedupe-ing and normalizing parens.
3144     """
3145     if isinstance(node, Leaf) or node.type != syms.atom:
3146         return False
3147
3148     first, last = node.children[0], node.children[-1]
3149     return (
3150         isinstance(first, Leaf)
3151         and first.type == token.LPAR
3152         and first.value == ""
3153         and isinstance(last, Leaf)
3154         and last.type == token.RPAR
3155         and last.value == ""
3156     )
3157
3158
3159 def is_empty_tuple(node: LN) -> bool:
3160     """Return True if `node` holds an empty tuple."""
3161     return (
3162         node.type == syms.atom
3163         and len(node.children) == 2
3164         and node.children[0].type == token.LPAR
3165         and node.children[1].type == token.RPAR
3166     )
3167
3168
3169 def unwrap_singleton_parenthesis(node: LN) -> Optional[LN]:
3170     """Returns `wrapped` if `node` is of the shape ( wrapped ).
3171
3172     Parenthesis can be optional. Returns None otherwise"""
3173     if len(node.children) != 3:
3174         return None
3175
3176     lpar, wrapped, rpar = node.children
3177     if not (lpar.type == token.LPAR and rpar.type == token.RPAR):
3178         return None
3179
3180     return wrapped
3181
3182
3183 def wrap_in_parentheses(parent: Node, child: LN, *, visible: bool = True) -> None:
3184     """Wrap `child` in parentheses.
3185
3186     This replaces `child` with an atom holding the parentheses and the old
3187     child.  That requires moving the prefix.
3188
3189     If `visible` is False, the leaves will be valueless (and thus invisible).
3190     """
3191     lpar = Leaf(token.LPAR, "(" if visible else "")
3192     rpar = Leaf(token.RPAR, ")" if visible else "")
3193     prefix = child.prefix
3194     child.prefix = ""
3195     index = child.remove() or 0
3196     new_child = Node(syms.atom, [lpar, child, rpar])
3197     new_child.prefix = prefix
3198     parent.insert_child(index, new_child)
3199
3200
3201 def is_one_tuple(node: LN) -> bool:
3202     """Return True if `node` holds a tuple with one element, with or without parens."""
3203     if node.type == syms.atom:
3204         gexp = unwrap_singleton_parenthesis(node)
3205         if gexp is None or gexp.type != syms.testlist_gexp:
3206             return False
3207
3208         return len(gexp.children) == 2 and gexp.children[1].type == token.COMMA
3209
3210     return (
3211         node.type in IMPLICIT_TUPLE
3212         and len(node.children) == 2
3213         and node.children[1].type == token.COMMA
3214     )
3215
3216
3217 def is_walrus_assignment(node: LN) -> bool:
3218     """Return True iff `node` is of the shape ( test := test )"""
3219     inner = unwrap_singleton_parenthesis(node)
3220     return inner is not None and inner.type == syms.namedexpr_test
3221
3222
3223 def is_yield(node: LN) -> bool:
3224     """Return True if `node` holds a `yield` or `yield from` expression."""
3225     if node.type == syms.yield_expr:
3226         return True
3227
3228     if node.type == token.NAME and node.value == "yield":  # type: ignore
3229         return True
3230
3231     if node.type != syms.atom:
3232         return False
3233
3234     if len(node.children) != 3:
3235         return False
3236
3237     lpar, expr, rpar = node.children
3238     if lpar.type == token.LPAR and rpar.type == token.RPAR:
3239         return is_yield(expr)
3240
3241     return False
3242
3243
3244 def is_vararg(leaf: Leaf, within: Set[NodeType]) -> bool:
3245     """Return True if `leaf` is a star or double star in a vararg or kwarg.
3246
3247     If `within` includes VARARGS_PARENTS, this applies to function signatures.
3248     If `within` includes UNPACKING_PARENTS, it applies to right hand-side
3249     extended iterable unpacking (PEP 3132) and additional unpacking
3250     generalizations (PEP 448).
3251     """
3252     if leaf.type not in VARARGS_SPECIALS or not leaf.parent:
3253         return False
3254
3255     p = leaf.parent
3256     if p.type == syms.star_expr:
3257         # Star expressions are also used as assignment targets in extended
3258         # iterable unpacking (PEP 3132).  See what its parent is instead.
3259         if not p.parent:
3260             return False
3261
3262         p = p.parent
3263
3264     return p.type in within
3265
3266
3267 def is_multiline_string(leaf: Leaf) -> bool:
3268     """Return True if `leaf` is a multiline string that actually spans many lines."""
3269     value = leaf.value.lstrip("furbFURB")
3270     return value[:3] in {'"""', "'''"} and "\n" in value
3271
3272
3273 def is_stub_suite(node: Node) -> bool:
3274     """Return True if `node` is a suite with a stub body."""
3275     if (
3276         len(node.children) != 4
3277         or node.children[0].type != token.NEWLINE
3278         or node.children[1].type != token.INDENT
3279         or node.children[3].type != token.DEDENT
3280     ):
3281         return False
3282
3283     return is_stub_body(node.children[2])
3284
3285
3286 def is_stub_body(node: LN) -> bool:
3287     """Return True if `node` is a simple statement containing an ellipsis."""
3288     if not isinstance(node, Node) or node.type != syms.simple_stmt:
3289         return False
3290
3291     if len(node.children) != 2:
3292         return False
3293
3294     child = node.children[0]
3295     return (
3296         child.type == syms.atom
3297         and len(child.children) == 3
3298         and all(leaf == Leaf(token.DOT, ".") for leaf in child.children)
3299     )
3300
3301
3302 def max_delimiter_priority_in_atom(node: LN) -> Priority:
3303     """Return maximum delimiter priority inside `node`.
3304
3305     This is specific to atoms with contents contained in a pair of parentheses.
3306     If `node` isn't an atom or there are no enclosing parentheses, returns 0.
3307     """
3308     if node.type != syms.atom:
3309         return 0
3310
3311     first = node.children[0]
3312     last = node.children[-1]
3313     if not (first.type == token.LPAR and last.type == token.RPAR):
3314         return 0
3315
3316     bt = BracketTracker()
3317     for c in node.children[1:-1]:
3318         if isinstance(c, Leaf):
3319             bt.mark(c)
3320         else:
3321             for leaf in c.leaves():
3322                 bt.mark(leaf)
3323     try:
3324         return bt.max_delimiter_priority()
3325
3326     except ValueError:
3327         return 0
3328
3329
3330 def ensure_visible(leaf: Leaf) -> None:
3331     """Make sure parentheses are visible.
3332
3333     They could be invisible as part of some statements (see
3334     :func:`normalize_invisible_parens` and :func:`visit_import_from`).
3335     """
3336     if leaf.type == token.LPAR:
3337         leaf.value = "("
3338     elif leaf.type == token.RPAR:
3339         leaf.value = ")"
3340
3341
3342 def should_explode(line: Line, opening_bracket: Leaf) -> bool:
3343     """Should `line` immediately be split with `delimiter_split()` after RHS?"""
3344
3345     if not (
3346         opening_bracket.parent
3347         and opening_bracket.parent.type in {syms.atom, syms.import_from}
3348         and opening_bracket.value in "[{("
3349     ):
3350         return False
3351
3352     try:
3353         last_leaf = line.leaves[-1]
3354         exclude = {id(last_leaf)} if last_leaf.type == token.COMMA else set()
3355         max_priority = line.bracket_tracker.max_delimiter_priority(exclude=exclude)
3356     except (IndexError, ValueError):
3357         return False
3358
3359     return max_priority == COMMA_PRIORITY
3360
3361
3362 def get_features_used(node: Node) -> Set[Feature]:
3363     """Return a set of (relatively) new Python features used in this file.
3364
3365     Currently looking for:
3366     - f-strings;
3367     - underscores in numeric literals;
3368     - trailing commas after * or ** in function signatures and calls;
3369     - positional only arguments in function signatures and lambdas;
3370     """
3371     features: Set[Feature] = set()
3372     for n in node.pre_order():
3373         if n.type == token.STRING:
3374             value_head = n.value[:2]  # type: ignore
3375             if value_head in {'f"', 'F"', "f'", "F'", "rf", "fr", "RF", "FR"}:
3376                 features.add(Feature.F_STRINGS)
3377
3378         elif n.type == token.NUMBER:
3379             if "_" in n.value:  # type: ignore
3380                 features.add(Feature.NUMERIC_UNDERSCORES)
3381
3382         elif n.type == token.SLASH:
3383             if n.parent and n.parent.type in {syms.typedargslist, syms.arglist}:
3384                 features.add(Feature.POS_ONLY_ARGUMENTS)
3385
3386         elif n.type == token.COLONEQUAL:
3387             features.add(Feature.ASSIGNMENT_EXPRESSIONS)
3388
3389         elif (
3390             n.type in {syms.typedargslist, syms.arglist}
3391             and n.children
3392             and n.children[-1].type == token.COMMA
3393         ):
3394             if n.type == syms.typedargslist:
3395                 feature = Feature.TRAILING_COMMA_IN_DEF
3396             else:
3397                 feature = Feature.TRAILING_COMMA_IN_CALL
3398
3399             for ch in n.children:
3400                 if ch.type in STARS:
3401                     features.add(feature)
3402
3403                 if ch.type == syms.argument:
3404                     for argch in ch.children:
3405                         if argch.type in STARS:
3406                             features.add(feature)
3407
3408     return features
3409
3410
3411 def detect_target_versions(node: Node) -> Set[TargetVersion]:
3412     """Detect the version to target based on the nodes used."""
3413     features = get_features_used(node)
3414     return {
3415         version for version in TargetVersion if features <= VERSION_TO_FEATURES[version]
3416     }
3417
3418
3419 def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[LeafID]]:
3420     """Generate sets of closing bracket IDs that should be omitted in a RHS.
3421
3422     Brackets can be omitted if the entire trailer up to and including
3423     a preceding closing bracket fits in one line.
3424
3425     Yielded sets are cumulative (contain results of previous yields, too).  First
3426     set is empty.
3427     """
3428
3429     omit: Set[LeafID] = set()
3430     yield omit
3431
3432     length = 4 * line.depth
3433     opening_bracket: Optional[Leaf] = None
3434     closing_bracket: Optional[Leaf] = None
3435     inner_brackets: Set[LeafID] = set()
3436     for index, leaf, leaf_length in enumerate_with_length(line, reversed=True):
3437         length += leaf_length
3438         if length > line_length:
3439             break
3440
3441         has_inline_comment = leaf_length > len(leaf.value) + len(leaf.prefix)
3442         if leaf.type == STANDALONE_COMMENT or has_inline_comment:
3443             break
3444
3445         if opening_bracket:
3446             if leaf is opening_bracket:
3447                 opening_bracket = None
3448             elif leaf.type in CLOSING_BRACKETS:
3449                 inner_brackets.add(id(leaf))
3450         elif leaf.type in CLOSING_BRACKETS:
3451             if index > 0 and line.leaves[index - 1].type in OPENING_BRACKETS:
3452                 # Empty brackets would fail a split so treat them as "inner"
3453                 # brackets (e.g. only add them to the `omit` set if another
3454                 # pair of brackets was good enough.
3455                 inner_brackets.add(id(leaf))
3456                 continue
3457
3458             if closing_bracket:
3459                 omit.add(id(closing_bracket))
3460                 omit.update(inner_brackets)
3461                 inner_brackets.clear()
3462                 yield omit
3463
3464             if leaf.value:
3465                 opening_bracket = leaf.opening_bracket
3466                 closing_bracket = leaf
3467
3468
3469 def get_future_imports(node: Node) -> Set[str]:
3470     """Return a set of __future__ imports in the file."""
3471     imports: Set[str] = set()
3472
3473     def get_imports_from_children(children: List[LN]) -> Generator[str, None, None]:
3474         for child in children:
3475             if isinstance(child, Leaf):
3476                 if child.type == token.NAME:
3477                     yield child.value
3478
3479             elif child.type == syms.import_as_name:
3480                 orig_name = child.children[0]
3481                 assert isinstance(orig_name, Leaf), "Invalid syntax parsing imports"
3482                 assert orig_name.type == token.NAME, "Invalid syntax parsing imports"
3483                 yield orig_name.value
3484
3485             elif child.type == syms.import_as_names:
3486                 yield from get_imports_from_children(child.children)
3487
3488             else:
3489                 raise AssertionError("Invalid syntax parsing imports")
3490
3491     for child in node.children:
3492         if child.type != syms.simple_stmt:
3493             break
3494
3495         first_child = child.children[0]
3496         if isinstance(first_child, Leaf):
3497             # Continue looking if we see a docstring; otherwise stop.
3498             if (
3499                 len(child.children) == 2
3500                 and first_child.type == token.STRING
3501                 and child.children[1].type == token.NEWLINE
3502             ):
3503                 continue
3504
3505             break
3506
3507         elif first_child.type == syms.import_from:
3508             module_name = first_child.children[1]
3509             if not isinstance(module_name, Leaf) or module_name.value != "__future__":
3510                 break
3511
3512             imports |= set(get_imports_from_children(first_child.children[3:]))
3513         else:
3514             break
3515
3516     return imports
3517
3518
3519 @lru_cache()
3520 def get_gitignore(root: Path) -> PathSpec:
3521     """ Return a PathSpec matching gitignore content if present."""
3522     gitignore = root / ".gitignore"
3523     lines: List[str] = []
3524     if gitignore.is_file():
3525         with gitignore.open() as gf:
3526             lines = gf.readlines()
3527     return PathSpec.from_lines("gitwildmatch", lines)
3528
3529
3530 def gen_python_files_in_dir(
3531     path: Path,
3532     root: Path,
3533     include: Pattern[str],
3534     exclude: Pattern[str],
3535     report: "Report",
3536     gitignore: PathSpec,
3537 ) -> Iterator[Path]:
3538     """Generate all files under `path` whose paths are not excluded by the
3539     `exclude` regex, but are included by the `include` regex.
3540
3541     Symbolic links pointing outside of the `root` directory are ignored.
3542
3543     `report` is where output about exclusions goes.
3544     """
3545     assert root.is_absolute(), f"INTERNAL ERROR: `root` must be absolute but is {root}"
3546     for child in path.iterdir():
3547         # First ignore files matching .gitignore
3548         if gitignore.match_file(child.as_posix()):
3549             report.path_ignored(child, f"matches the .gitignore file content")
3550             continue
3551
3552         # Then ignore with `exclude` option.
3553         try:
3554             normalized_path = "/" + child.resolve().relative_to(root).as_posix()
3555         except OSError as e:
3556             report.path_ignored(child, f"cannot be read because {e}")
3557             continue
3558
3559         except ValueError:
3560             if child.is_symlink():
3561                 report.path_ignored(
3562                     child, f"is a symbolic link that points outside {root}"
3563                 )
3564                 continue
3565
3566             raise
3567
3568         if child.is_dir():
3569             normalized_path += "/"
3570
3571         exclude_match = exclude.search(normalized_path)
3572         if exclude_match and exclude_match.group(0):
3573             report.path_ignored(child, f"matches the --exclude regular expression")
3574             continue
3575
3576         if child.is_dir():
3577             yield from gen_python_files_in_dir(
3578                 child, root, include, exclude, report, gitignore
3579             )
3580
3581         elif child.is_file():
3582             include_match = include.search(normalized_path)
3583             if include_match:
3584                 yield child
3585
3586
3587 @lru_cache()
3588 def find_project_root(srcs: Iterable[str]) -> Path:
3589     """Return a directory containing .git, .hg, or pyproject.toml.
3590
3591     That directory can be one of the directories passed in `srcs` or their
3592     common parent.
3593
3594     If no directory in the tree contains a marker that would specify it's the
3595     project root, the root of the file system is returned.
3596     """
3597     if not srcs:
3598         return Path("/").resolve()
3599
3600     common_base = min(Path(src).resolve() for src in srcs)
3601     if common_base.is_dir():
3602         # Append a fake file so `parents` below returns `common_base_dir`, too.
3603         common_base /= "fake-file"
3604     for directory in common_base.parents:
3605         if (directory / ".git").exists():
3606             return directory
3607
3608         if (directory / ".hg").is_dir():
3609             return directory
3610
3611         if (directory / "pyproject.toml").is_file():
3612             return directory
3613
3614     return directory
3615
3616
3617 @dataclass
3618 class Report:
3619     """Provides a reformatting counter. Can be rendered with `str(report)`."""
3620
3621     check: bool = False
3622     diff: bool = False
3623     quiet: bool = False
3624     verbose: bool = False
3625     change_count: int = 0
3626     same_count: int = 0
3627     failure_count: int = 0
3628
3629     def done(self, src: Path, changed: Changed) -> None:
3630         """Increment the counter for successful reformatting. Write out a message."""
3631         if changed is Changed.YES:
3632             reformatted = "would reformat" if self.check or self.diff else "reformatted"
3633             if self.verbose or not self.quiet:
3634                 out(f"{reformatted} {src}")
3635             self.change_count += 1
3636         else:
3637             if self.verbose:
3638                 if changed is Changed.NO:
3639                     msg = f"{src} already well formatted, good job."
3640                 else:
3641                     msg = f"{src} wasn't modified on disk since last run."
3642                 out(msg, bold=False)
3643             self.same_count += 1
3644
3645     def failed(self, src: Path, message: str) -> None:
3646         """Increment the counter for failed reformatting. Write out a message."""
3647         err(f"error: cannot format {src}: {message}")
3648         self.failure_count += 1
3649
3650     def path_ignored(self, path: Path, message: str) -> None:
3651         if self.verbose:
3652             out(f"{path} ignored: {message}", bold=False)
3653
3654     @property
3655     def return_code(self) -> int:
3656         """Return the exit code that the app should use.
3657
3658         This considers the current state of changed files and failures:
3659         - if there were any failures, return 123;
3660         - if any files were changed and --check is being used, return 1;
3661         - otherwise return 0.
3662         """
3663         # According to http://tldp.org/LDP/abs/html/exitcodes.html starting with
3664         # 126 we have special return codes reserved by the shell.
3665         if self.failure_count:
3666             return 123
3667
3668         elif self.change_count and self.check:
3669             return 1
3670
3671         return 0
3672
3673     def __str__(self) -> str:
3674         """Render a color report of the current state.
3675
3676         Use `click.unstyle` to remove colors.
3677         """
3678         if self.check or self.diff:
3679             reformatted = "would be reformatted"
3680             unchanged = "would be left unchanged"
3681             failed = "would fail to reformat"
3682         else:
3683             reformatted = "reformatted"
3684             unchanged = "left unchanged"
3685             failed = "failed to reformat"
3686         report = []
3687         if self.change_count:
3688             s = "s" if self.change_count > 1 else ""
3689             report.append(
3690                 click.style(f"{self.change_count} file{s} {reformatted}", bold=True)
3691             )
3692         if self.same_count:
3693             s = "s" if self.same_count > 1 else ""
3694             report.append(f"{self.same_count} file{s} {unchanged}")
3695         if self.failure_count:
3696             s = "s" if self.failure_count > 1 else ""
3697             report.append(
3698                 click.style(f"{self.failure_count} file{s} {failed}", fg="red")
3699             )
3700         return ", ".join(report) + "."
3701
3702
3703 def parse_ast(src: str) -> Union[ast.AST, ast3.AST, ast27.AST]:
3704     filename = "<unknown>"
3705     if sys.version_info >= (3, 8):
3706         # TODO: support Python 4+ ;)
3707         for minor_version in range(sys.version_info[1], 4, -1):
3708             try:
3709                 return ast.parse(src, filename, feature_version=(3, minor_version))
3710             except SyntaxError:
3711                 continue
3712     else:
3713         for feature_version in (7, 6):
3714             try:
3715                 return ast3.parse(src, filename, feature_version=feature_version)
3716             except SyntaxError:
3717                 continue
3718
3719     return ast27.parse(src)
3720
3721
3722 def _fixup_ast_constants(
3723     node: Union[ast.AST, ast3.AST, ast27.AST]
3724 ) -> Union[ast.AST, ast3.AST, ast27.AST]:
3725     """Map ast nodes deprecated in 3.8 to Constant."""
3726     if isinstance(node, (ast.Str, ast3.Str, ast27.Str, ast.Bytes, ast3.Bytes)):
3727         return ast.Constant(value=node.s)
3728
3729     if isinstance(node, (ast.Num, ast3.Num, ast27.Num)):
3730         return ast.Constant(value=node.n)
3731
3732     if isinstance(node, (ast.NameConstant, ast3.NameConstant)):
3733         return ast.Constant(value=node.value)
3734
3735     return node
3736
3737
3738 def assert_equivalent(src: str, dst: str) -> None:
3739     """Raise AssertionError if `src` and `dst` aren't equivalent."""
3740
3741     def _v(node: Union[ast.AST, ast3.AST, ast27.AST], depth: int = 0) -> Iterator[str]:
3742         """Simple visitor generating strings to compare ASTs by content."""
3743
3744         node = _fixup_ast_constants(node)
3745
3746         yield f"{'  ' * depth}{node.__class__.__name__}("
3747
3748         for field in sorted(node._fields):  # noqa: F402
3749             # TypeIgnore has only one field 'lineno' which breaks this comparison
3750             type_ignore_classes = (ast3.TypeIgnore, ast27.TypeIgnore)
3751             if sys.version_info >= (3, 8):
3752                 type_ignore_classes += (ast.TypeIgnore,)
3753             if isinstance(node, type_ignore_classes):
3754                 break
3755
3756             try:
3757                 value = getattr(node, field)
3758             except AttributeError:
3759                 continue
3760
3761             yield f"{'  ' * (depth+1)}{field}="
3762
3763             if isinstance(value, list):
3764                 for item in value:
3765                     # Ignore nested tuples within del statements, because we may insert
3766                     # parentheses and they change the AST.
3767                     if (
3768                         field == "targets"
3769                         and isinstance(node, (ast.Delete, ast3.Delete, ast27.Delete))
3770                         and isinstance(item, (ast.Tuple, ast3.Tuple, ast27.Tuple))
3771                     ):
3772                         for item in item.elts:
3773                             yield from _v(item, depth + 2)
3774
3775                     elif isinstance(item, (ast.AST, ast3.AST, ast27.AST)):
3776                         yield from _v(item, depth + 2)
3777
3778             elif isinstance(value, (ast.AST, ast3.AST, ast27.AST)):
3779                 yield from _v(value, depth + 2)
3780
3781             else:
3782                 yield f"{'  ' * (depth+2)}{value!r},  # {value.__class__.__name__}"
3783
3784         yield f"{'  ' * depth})  # /{node.__class__.__name__}"
3785
3786     try:
3787         src_ast = parse_ast(src)
3788     except Exception as exc:
3789         raise AssertionError(
3790             f"cannot use --safe with this file; failed to parse source file.  "
3791             f"AST error message: {exc}"
3792         )
3793
3794     try:
3795         dst_ast = parse_ast(dst)
3796     except Exception as exc:
3797         log = dump_to_file("".join(traceback.format_tb(exc.__traceback__)), dst)
3798         raise AssertionError(
3799             f"INTERNAL ERROR: Black produced invalid code: {exc}. "
3800             f"Please report a bug on https://github.com/psf/black/issues.  "
3801             f"This invalid output might be helpful: {log}"
3802         ) from None
3803
3804     src_ast_str = "\n".join(_v(src_ast))
3805     dst_ast_str = "\n".join(_v(dst_ast))
3806     if src_ast_str != dst_ast_str:
3807         log = dump_to_file(diff(src_ast_str, dst_ast_str, "src", "dst"))
3808         raise AssertionError(
3809             f"INTERNAL ERROR: Black produced code that is not equivalent to "
3810             f"the source.  "
3811             f"Please report a bug on https://github.com/psf/black/issues.  "
3812             f"This diff might be helpful: {log}"
3813         ) from None
3814
3815
3816 def assert_stable(src: str, dst: str, mode: FileMode) -> None:
3817     """Raise AssertionError if `dst` reformats differently the second time."""
3818     newdst = format_str(dst, mode=mode)
3819     if dst != newdst:
3820         log = dump_to_file(
3821             diff(src, dst, "source", "first pass"),
3822             diff(dst, newdst, "first pass", "second pass"),
3823         )
3824         raise AssertionError(
3825             f"INTERNAL ERROR: Black produced different code on the second pass "
3826             f"of the formatter.  "
3827             f"Please report a bug on https://github.com/psf/black/issues.  "
3828             f"This diff might be helpful: {log}"
3829         ) from None
3830
3831
3832 @mypyc_attr(patchable=True)
3833 def dump_to_file(*output: str) -> str:
3834     """Dump `output` to a temporary file. Return path to the file."""
3835     with tempfile.NamedTemporaryFile(
3836         mode="w", prefix="blk_", suffix=".log", delete=False, encoding="utf8"
3837     ) as f:
3838         for lines in output:
3839             f.write(lines)
3840             if lines and lines[-1] != "\n":
3841                 f.write("\n")
3842     return f.name
3843
3844
3845 @contextmanager
3846 def nullcontext() -> Iterator[None]:
3847     """Return an empty context manager.
3848
3849     To be used like `nullcontext` in Python 3.7.
3850     """
3851     yield
3852
3853
3854 def diff(a: str, b: str, a_name: str, b_name: str) -> str:
3855     """Return a unified diff string between strings `a` and `b`."""
3856     import difflib
3857
3858     a_lines = [line + "\n" for line in a.split("\n")]
3859     b_lines = [line + "\n" for line in b.split("\n")]
3860     return "".join(
3861         difflib.unified_diff(a_lines, b_lines, fromfile=a_name, tofile=b_name, n=5)
3862     )
3863
3864
3865 def cancel(tasks: Iterable["asyncio.Task[Any]"]) -> None:
3866     """asyncio signal handler that cancels all `tasks` and reports to stderr."""
3867     err("Aborted!")
3868     for task in tasks:
3869         task.cancel()
3870
3871
3872 def shutdown(loop: asyncio.AbstractEventLoop) -> None:
3873     """Cancel all pending tasks on `loop`, wait for them, and close the loop."""
3874     try:
3875         if sys.version_info[:2] >= (3, 7):
3876             all_tasks = asyncio.all_tasks
3877         else:
3878             all_tasks = asyncio.Task.all_tasks
3879         # This part is borrowed from asyncio/runners.py in Python 3.7b2.
3880         to_cancel = [task for task in all_tasks(loop) if not task.done()]
3881         if not to_cancel:
3882             return
3883
3884         for task in to_cancel:
3885             task.cancel()
3886         loop.run_until_complete(
3887             asyncio.gather(*to_cancel, loop=loop, return_exceptions=True)
3888         )
3889     finally:
3890         # `concurrent.futures.Future` objects cannot be cancelled once they
3891         # are already running. There might be some when the `shutdown()` happened.
3892         # Silence their logger's spew about the event loop being closed.
3893         cf_logger = logging.getLogger("concurrent.futures")
3894         cf_logger.setLevel(logging.CRITICAL)
3895         loop.close()
3896
3897
3898 def sub_twice(regex: Pattern[str], replacement: str, original: str) -> str:
3899     """Replace `regex` with `replacement` twice on `original`.
3900
3901     This is used by string normalization to perform replaces on
3902     overlapping matches.
3903     """
3904     return regex.sub(replacement, regex.sub(replacement, original))
3905
3906
3907 def re_compile_maybe_verbose(regex: str) -> Pattern[str]:
3908     """Compile a regular expression string in `regex`.
3909
3910     If it contains newlines, use verbose mode.
3911     """
3912     if "\n" in regex:
3913         regex = "(?x)" + regex
3914     compiled: Pattern[str] = re.compile(regex)
3915     return compiled
3916
3917
3918 def enumerate_reversed(sequence: Sequence[T]) -> Iterator[Tuple[Index, T]]:
3919     """Like `reversed(enumerate(sequence))` if that were possible."""
3920     index = len(sequence) - 1
3921     for element in reversed(sequence):
3922         yield (index, element)
3923         index -= 1
3924
3925
3926 def enumerate_with_length(
3927     line: Line, reversed: bool = False
3928 ) -> Iterator[Tuple[Index, Leaf, int]]:
3929     """Return an enumeration of leaves with their length.
3930
3931     Stops prematurely on multiline strings and standalone comments.
3932     """
3933     op = cast(
3934         Callable[[Sequence[Leaf]], Iterator[Tuple[Index, Leaf]]],
3935         enumerate_reversed if reversed else enumerate,
3936     )
3937     for index, leaf in op(line.leaves):
3938         length = len(leaf.prefix) + len(leaf.value)
3939         if "\n" in leaf.value:
3940             return  # Multiline strings, we can't continue.
3941
3942         for comment in line.comments_after(leaf):
3943             length += len(comment.value)
3944
3945         yield index, leaf, length
3946
3947
3948 def is_line_short_enough(line: Line, *, line_length: int, line_str: str = "") -> bool:
3949     """Return True if `line` is no longer than `line_length`.
3950
3951     Uses the provided `line_str` rendering, if any, otherwise computes a new one.
3952     """
3953     if not line_str:
3954         line_str = str(line).strip("\n")
3955     return (
3956         len(line_str) <= line_length
3957         and "\n" not in line_str  # multiline strings
3958         and not line.contains_standalone_comments()
3959     )
3960
3961
3962 def can_be_split(line: Line) -> bool:
3963     """Return False if the line cannot be split *for sure*.
3964
3965     This is not an exhaustive search but a cheap heuristic that we can use to
3966     avoid some unfortunate formattings (mostly around wrapping unsplittable code
3967     in unnecessary parentheses).
3968     """
3969     leaves = line.leaves
3970     if len(leaves) < 2:
3971         return False
3972
3973     if leaves[0].type == token.STRING and leaves[1].type == token.DOT:
3974         call_count = 0
3975         dot_count = 0
3976         next = leaves[-1]
3977         for leaf in leaves[-2::-1]:
3978             if leaf.type in OPENING_BRACKETS:
3979                 if next.type not in CLOSING_BRACKETS:
3980                     return False
3981
3982                 call_count += 1
3983             elif leaf.type == token.DOT:
3984                 dot_count += 1
3985             elif leaf.type == token.NAME:
3986                 if not (next.type == token.DOT or next.type in OPENING_BRACKETS):
3987                     return False
3988
3989             elif leaf.type not in CLOSING_BRACKETS:
3990                 return False
3991
3992             if dot_count > 1 and call_count > 1:
3993                 return False
3994
3995     return True
3996
3997
3998 def can_omit_invisible_parens(line: Line, line_length: int) -> bool:
3999     """Does `line` have a shape safe to reformat without optional parens around it?
4000
4001     Returns True for only a subset of potentially nice looking formattings but
4002     the point is to not return false positives that end up producing lines that
4003     are too long.
4004     """
4005     bt = line.bracket_tracker
4006     if not bt.delimiters:
4007         # Without delimiters the optional parentheses are useless.
4008         return True
4009
4010     max_priority = bt.max_delimiter_priority()
4011     if bt.delimiter_count_with_priority(max_priority) > 1:
4012         # With more than one delimiter of a kind the optional parentheses read better.
4013         return False
4014
4015     if max_priority == DOT_PRIORITY:
4016         # A single stranded method call doesn't require optional parentheses.
4017         return True
4018
4019     assert len(line.leaves) >= 2, "Stranded delimiter"
4020
4021     first = line.leaves[0]
4022     second = line.leaves[1]
4023     penultimate = line.leaves[-2]
4024     last = line.leaves[-1]
4025
4026     # With a single delimiter, omit if the expression starts or ends with
4027     # a bracket.
4028     if first.type in OPENING_BRACKETS and second.type not in CLOSING_BRACKETS:
4029         remainder = False
4030         length = 4 * line.depth
4031         for _index, leaf, leaf_length in enumerate_with_length(line):
4032             if leaf.type in CLOSING_BRACKETS and leaf.opening_bracket is first:
4033                 remainder = True
4034             if remainder:
4035                 length += leaf_length
4036                 if length > line_length:
4037                     break
4038
4039                 if leaf.type in OPENING_BRACKETS:
4040                     # There are brackets we can further split on.
4041                     remainder = False
4042
4043         else:
4044             # checked the entire string and line length wasn't exceeded
4045             if len(line.leaves) == _index + 1:
4046                 return True
4047
4048         # Note: we are not returning False here because a line might have *both*
4049         # a leading opening bracket and a trailing closing bracket.  If the
4050         # opening bracket doesn't match our rule, maybe the closing will.
4051
4052     if (
4053         last.type == token.RPAR
4054         or last.type == token.RBRACE
4055         or (
4056             # don't use indexing for omitting optional parentheses;
4057             # it looks weird
4058             last.type == token.RSQB
4059             and last.parent
4060             and last.parent.type != syms.trailer
4061         )
4062     ):
4063         if penultimate.type in OPENING_BRACKETS:
4064             # Empty brackets don't help.
4065             return False
4066
4067         if is_multiline_string(first):
4068             # Additional wrapping of a multiline string in this situation is
4069             # unnecessary.
4070             return True
4071
4072         length = 4 * line.depth
4073         seen_other_brackets = False
4074         for _index, leaf, leaf_length in enumerate_with_length(line):
4075             length += leaf_length
4076             if leaf is last.opening_bracket:
4077                 if seen_other_brackets or length <= line_length:
4078                     return True
4079
4080             elif leaf.type in OPENING_BRACKETS:
4081                 # There are brackets we can further split on.
4082                 seen_other_brackets = True
4083
4084     return False
4085
4086
4087 def get_cache_file(mode: FileMode) -> Path:
4088     return CACHE_DIR / f"cache.{mode.get_cache_key()}.pickle"
4089
4090
4091 def read_cache(mode: FileMode) -> Cache:
4092     """Read the cache if it exists and is well formed.
4093
4094     If it is not well formed, the call to write_cache later should resolve the issue.
4095     """
4096     cache_file = get_cache_file(mode)
4097     if not cache_file.exists():
4098         return {}
4099
4100     with cache_file.open("rb") as fobj:
4101         try:
4102             cache: Cache = pickle.load(fobj)
4103         except (pickle.UnpicklingError, ValueError):
4104             return {}
4105
4106     return cache
4107
4108
4109 def get_cache_info(path: Path) -> CacheInfo:
4110     """Return the information used to check if a file is already formatted or not."""
4111     stat = path.stat()
4112     return stat.st_mtime, stat.st_size
4113
4114
4115 def filter_cached(cache: Cache, sources: Iterable[Path]) -> Tuple[Set[Path], Set[Path]]:
4116     """Split an iterable of paths in `sources` into two sets.
4117
4118     The first contains paths of files that modified on disk or are not in the
4119     cache. The other contains paths to non-modified files.
4120     """
4121     todo, done = set(), set()
4122     for src in sources:
4123         src = src.resolve()
4124         if cache.get(src) != get_cache_info(src):
4125             todo.add(src)
4126         else:
4127             done.add(src)
4128     return todo, done
4129
4130
4131 def write_cache(cache: Cache, sources: Iterable[Path], mode: FileMode) -> None:
4132     """Update the cache file."""
4133     cache_file = get_cache_file(mode)
4134     try:
4135         CACHE_DIR.mkdir(parents=True, exist_ok=True)
4136         new_cache = {**cache, **{src.resolve(): get_cache_info(src) for src in sources}}
4137         with tempfile.NamedTemporaryFile(dir=str(cache_file.parent), delete=False) as f:
4138             pickle.dump(new_cache, f, protocol=4)
4139         os.replace(f.name, cache_file)
4140     except OSError:
4141         pass
4142
4143
4144 def patch_click() -> None:
4145     """Make Click not crash.
4146
4147     On certain misconfigured environments, Python 3 selects the ASCII encoding as the
4148     default which restricts paths that it can access during the lifetime of the
4149     application.  Click refuses to work in this scenario by raising a RuntimeError.
4150
4151     In case of Black the likelihood that non-ASCII characters are going to be used in
4152     file paths is minimal since it's Python source code.  Moreover, this crash was
4153     spurious on Python 3.7 thanks to PEP 538 and PEP 540.
4154     """
4155     try:
4156         from click import core
4157         from click import _unicodefun  # type: ignore
4158     except ModuleNotFoundError:
4159         return
4160
4161     for module in (core, _unicodefun):
4162         if hasattr(module, "_verify_python3_env"):
4163             module._verify_python3_env = lambda: None
4164
4165
4166 def patched_main() -> None:
4167     freeze_support()
4168     patch_click()
4169     main()
4170
4171
4172 if __name__ == "__main__":
4173     patched_main()