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.
2 from asyncio.base_events import BaseEventLoop
3 from concurrent.futures import Executor, ProcessPoolExecutor
4 from datetime import datetime
5 from enum import Enum, Flag
6 from functools import lru_cache, partial, wraps
11 from multiprocessing import Manager, freeze_support
13 from pathlib import Path
39 from appdirs import user_cache_dir
40 from attr import dataclass, Factory
45 from blib2to3.pytree import Node, Leaf, type_repr
46 from blib2to3 import pygram, pytree
47 from blib2to3.pgen2 import driver, token
48 from blib2to3.pgen2.parse import ParseError
51 __version__ = "18.9b0"
52 DEFAULT_LINE_LENGTH = 88
54 r"/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|_build|buck-out|build|dist)/"
56 DEFAULT_INCLUDES = r"\.pyi?$"
57 CACHE_DIR = Path(user_cache_dir("black", version=__version__))
69 LN = Union[Leaf, Node]
70 SplitFunc = Callable[["Line", bool], Iterator["Line"]]
73 CacheInfo = Tuple[Timestamp, FileSize]
74 Cache = Dict[Path, CacheInfo]
75 out = partial(click.secho, bold=True, err=True)
76 err = partial(click.secho, fg="red", err=True)
78 pygram.initialize(CACHE_DIR)
79 syms = pygram.python_symbols
82 class NothingChanged(UserWarning):
83 """Raised when reformatted code is the same as source."""
86 class CannotSplit(Exception):
87 """A readable split that fits the allotted line length is impossible."""
90 class InvalidInput(ValueError):
91 """Raised when input source code fails all parse attempts."""
94 class WriteBack(Enum):
101 def from_configuration(cls, *, check: bool, diff: bool) -> "WriteBack":
102 if check and not diff:
105 return cls.DIFF if diff else cls.YES
114 class FileMode(Flag):
118 NO_STRING_NORMALIZATION = 4
119 NO_NUMERIC_UNDERSCORE_NORMALIZATION = 8
122 def from_configuration(
127 skip_string_normalization: bool,
128 skip_numeric_underscore_normalization: bool,
130 mode = cls.AUTO_DETECT
135 if skip_string_normalization:
136 mode |= cls.NO_STRING_NORMALIZATION
137 if skip_numeric_underscore_normalization:
138 mode |= cls.NO_NUMERIC_UNDERSCORE_NORMALIZATION
142 def read_pyproject_toml(
143 ctx: click.Context, param: click.Parameter, value: Union[str, int, bool, None]
145 """Inject Black configuration from "pyproject.toml" into defaults in `ctx`.
147 Returns the path to a successfully found and read configuration file, None
150 assert not isinstance(value, (int, bool)), "Invalid parameter type passed"
152 root = find_project_root(ctx.params.get("src", ()))
153 path = root / "pyproject.toml"
160 pyproject_toml = toml.load(value)
161 config = pyproject_toml.get("tool", {}).get("black", {})
162 except (toml.TomlDecodeError, OSError) as e:
163 raise click.FileError(
164 filename=value, hint=f"Error reading configuration file: {e}"
170 if ctx.default_map is None:
172 ctx.default_map.update( # type: ignore # bad types in .pyi
173 {k.replace("--", "").replace("-", "_"): v for k, v in config.items()}
178 @click.command(context_settings=dict(help_option_names=["-h", "--help"]))
183 default=DEFAULT_LINE_LENGTH,
184 help="How many characters per line to allow.",
191 "Allow using Python 3.6-only syntax on all input files. This will put "
192 "trailing commas in function signatures and calls also after *args and "
193 "**kwargs. [default: per-file auto-detection]"
200 "Format all input files like typing stubs regardless of file extension "
201 "(useful when piping source on standard input)."
206 "--skip-string-normalization",
208 help="Don't normalize string quotes or prefixes.",
212 "--skip-numeric-underscore-normalization",
214 help="Don't normalize underscores in numeric literals.",
220 "Don't write the files back, just return the status. Return code 0 "
221 "means nothing would change. Return code 1 means some files would be "
222 "reformatted. Return code 123 means there was an internal error."
228 help="Don't write the files back, just output a diff for each file on stdout.",
233 help="If --fast given, skip temporary sanity checks. [default: --safe]",
238 default=DEFAULT_INCLUDES,
240 "A regular expression that matches files and directories that should be "
241 "included on recursive searches. An empty value means all files are "
242 "included regardless of the name. Use forward slashes for directories on "
243 "all platforms (Windows, too). Exclusions are calculated first, inclusions "
251 default=DEFAULT_EXCLUDES,
253 "A regular expression that matches files and directories that should be "
254 "excluded on recursive searches. An empty value means no paths are excluded. "
255 "Use forward slashes for directories on all platforms (Windows, too). "
256 "Exclusions are calculated first, inclusions later."
265 "Don't emit non-error messages to stderr. Errors are still emitted, "
266 "silence those with 2>/dev/null."
274 "Also emit messages to stderr about files that were not changed or were "
275 "ignored due to --exclude=."
278 @click.version_option(version=__version__)
283 exists=True, file_okay=True, dir_okay=True, readable=True, allow_dash=True
290 exists=False, file_okay=True, dir_okay=False, readable=True, allow_dash=False
293 callback=read_pyproject_toml,
294 help="Read configuration from PATH.",
305 skip_string_normalization: bool,
306 skip_numeric_underscore_normalization: bool,
312 config: Optional[str],
314 """The uncompromising code formatter."""
315 write_back = WriteBack.from_configuration(check=check, diff=diff)
316 mode = FileMode.from_configuration(
319 skip_string_normalization=skip_string_normalization,
320 skip_numeric_underscore_normalization=skip_numeric_underscore_normalization,
322 if config and verbose:
323 out(f"Using configuration from {config}.", bold=False, fg="blue")
325 include_regex = re_compile_maybe_verbose(include)
327 err(f"Invalid regular expression for include given: {include!r}")
330 exclude_regex = re_compile_maybe_verbose(exclude)
332 err(f"Invalid regular expression for exclude given: {exclude!r}")
334 report = Report(check=check, quiet=quiet, verbose=verbose)
335 root = find_project_root(src)
336 sources: Set[Path] = set()
341 gen_python_files_in_dir(p, root, include_regex, exclude_regex, report)
343 elif p.is_file() or s == "-":
344 # if a file was explicitly given, we don't care about its extension
347 err(f"invalid path: {s}")
348 if len(sources) == 0:
349 if verbose or not quiet:
350 out("No paths given. Nothing to do 😴")
353 if len(sources) == 1:
356 line_length=line_length,
358 write_back=write_back,
363 loop = asyncio.get_event_loop()
364 executor = ProcessPoolExecutor(max_workers=os.cpu_count())
366 loop.run_until_complete(
369 line_length=line_length,
371 write_back=write_back,
380 if verbose or not quiet:
381 bang = "💥 💔 💥" if report.return_code else "✨ 🍰 ✨"
382 out(f"All done! {bang}")
383 click.secho(str(report), err=True)
384 ctx.exit(report.return_code)
391 write_back: WriteBack,
395 """Reformat a single file under `src` without spawning child processes.
397 If `quiet` is True, non-error messages are not output. `line_length`,
398 `write_back`, `fast` and `pyi` options are passed to
399 :func:`format_file_in_place` or :func:`format_stdin_to_stdout`.
403 if not src.is_file() and str(src) == "-":
404 if format_stdin_to_stdout(
405 line_length=line_length, fast=fast, write_back=write_back, mode=mode
407 changed = Changed.YES
410 if write_back != WriteBack.DIFF:
411 cache = read_cache(line_length, mode)
412 res_src = src.resolve()
413 if res_src in cache and cache[res_src] == get_cache_info(res_src):
414 changed = Changed.CACHED
415 if changed is not Changed.CACHED and format_file_in_place(
417 line_length=line_length,
419 write_back=write_back,
422 changed = Changed.YES
423 if (write_back is WriteBack.YES and changed is not Changed.CACHED) or (
424 write_back is WriteBack.CHECK and changed is Changed.NO
426 write_cache(cache, [src], line_length, mode)
427 report.done(src, changed)
428 except Exception as exc:
429 report.failed(src, str(exc))
432 async def schedule_formatting(
436 write_back: WriteBack,
442 """Run formatting of `sources` in parallel using the provided `executor`.
444 (Use ProcessPoolExecutors for actual parallelism.)
446 `line_length`, `write_back`, `fast`, and `pyi` options are passed to
447 :func:`format_file_in_place`.
450 if write_back != WriteBack.DIFF:
451 cache = read_cache(line_length, mode)
452 sources, cached = filter_cached(cache, sources)
453 for src in sorted(cached):
454 report.done(src, Changed.CACHED)
459 sources_to_cache = []
461 if write_back == WriteBack.DIFF:
462 # For diff output, we need locks to ensure we don't interleave output
463 # from different processes.
465 lock = manager.Lock()
467 loop.run_in_executor(
469 format_file_in_place,
477 for src in sorted(sources)
479 pending: Iterable[asyncio.Task] = tasks.keys()
481 loop.add_signal_handler(signal.SIGINT, cancel, pending)
482 loop.add_signal_handler(signal.SIGTERM, cancel, pending)
483 except NotImplementedError:
484 # There are no good alternatives for these on Windows.
487 done, _ = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
489 src = tasks.pop(task)
491 cancelled.append(task)
492 elif task.exception():
493 report.failed(src, str(task.exception()))
495 changed = Changed.YES if task.result() else Changed.NO
496 # If the file was written back or was successfully checked as
497 # well-formatted, store this information in the cache.
498 if write_back is WriteBack.YES or (
499 write_back is WriteBack.CHECK and changed is Changed.NO
501 sources_to_cache.append(src)
502 report.done(src, changed)
504 await asyncio.gather(*cancelled, loop=loop, return_exceptions=True)
506 write_cache(cache, sources_to_cache, line_length, mode)
509 def format_file_in_place(
513 write_back: WriteBack = WriteBack.NO,
514 mode: FileMode = FileMode.AUTO_DETECT,
515 lock: Any = None, # multiprocessing.Manager().Lock() is some crazy proxy
517 """Format file under `src` path. Return True if changed.
519 If `write_back` is DIFF, write a diff to stdout. If it is YES, write reformatted
521 `line_length` and `fast` options are passed to :func:`format_file_contents`.
523 if src.suffix == ".pyi":
526 then = datetime.utcfromtimestamp(src.stat().st_mtime)
527 with open(src, "rb") as buf:
528 src_contents, encoding, newline = decode_bytes(buf.read())
530 dst_contents = format_file_contents(
531 src_contents, line_length=line_length, fast=fast, mode=mode
533 except NothingChanged:
536 if write_back == write_back.YES:
537 with open(src, "w", encoding=encoding, newline=newline) as f:
538 f.write(dst_contents)
539 elif write_back == write_back.DIFF:
540 now = datetime.utcnow()
541 src_name = f"{src}\t{then} +0000"
542 dst_name = f"{src}\t{now} +0000"
543 diff_contents = diff(src_contents, dst_contents, src_name, dst_name)
547 f = io.TextIOWrapper(
553 f.write(diff_contents)
561 def format_stdin_to_stdout(
564 write_back: WriteBack = WriteBack.NO,
565 mode: FileMode = FileMode.AUTO_DETECT,
567 """Format file on stdin. Return True if changed.
569 If `write_back` is YES, write reformatted code back to stdout. If it is DIFF,
570 write a diff to stdout.
571 `line_length`, `fast`, `is_pyi`, and `force_py36` arguments are passed to
572 :func:`format_file_contents`.
574 then = datetime.utcnow()
575 src, encoding, newline = decode_bytes(sys.stdin.buffer.read())
578 dst = format_file_contents(src, line_length=line_length, fast=fast, mode=mode)
581 except NothingChanged:
585 f = io.TextIOWrapper(
586 sys.stdout.buffer, encoding=encoding, newline=newline, write_through=True
588 if write_back == WriteBack.YES:
590 elif write_back == WriteBack.DIFF:
591 now = datetime.utcnow()
592 src_name = f"STDIN\t{then} +0000"
593 dst_name = f"STDOUT\t{now} +0000"
594 f.write(diff(src, dst, src_name, dst_name))
598 def format_file_contents(
603 mode: FileMode = FileMode.AUTO_DETECT,
605 """Reformat contents a file and return new contents.
607 If `fast` is False, additionally confirm that the reformatted code is
608 valid by calling :func:`assert_equivalent` and :func:`assert_stable` on it.
609 `line_length` is passed to :func:`format_str`.
611 if src_contents.strip() == "":
614 dst_contents = format_str(src_contents, line_length=line_length, mode=mode)
615 if src_contents == dst_contents:
619 assert_equivalent(src_contents, dst_contents)
620 assert_stable(src_contents, dst_contents, line_length=line_length, mode=mode)
625 src_contents: str, line_length: int, *, mode: FileMode = FileMode.AUTO_DETECT
627 """Reformat a string and return new contents.
629 `line_length` determines how many characters per line are allowed.
631 src_node = lib2to3_parse(src_contents.lstrip())
633 future_imports = get_future_imports(src_node)
634 is_pyi = bool(mode & FileMode.PYI)
635 py36 = bool(mode & FileMode.PYTHON36) or is_python36(src_node)
636 normalize_strings = not bool(mode & FileMode.NO_STRING_NORMALIZATION)
637 normalize_fmt_off(src_node)
638 lines = LineGenerator(
639 remove_u_prefix=py36 or "unicode_literals" in future_imports,
641 normalize_strings=normalize_strings,
642 allow_underscores=py36
643 and not bool(mode & FileMode.NO_NUMERIC_UNDERSCORE_NORMALIZATION),
645 elt = EmptyLineTracker(is_pyi=is_pyi)
648 for current_line in lines.visit(src_node):
649 for _ in range(after):
650 dst_contents += str(empty_line)
651 before, after = elt.maybe_empty_lines(current_line)
652 for _ in range(before):
653 dst_contents += str(empty_line)
654 for line in split_line(current_line, line_length=line_length, py36=py36):
655 dst_contents += str(line)
659 def decode_bytes(src: bytes) -> Tuple[FileContent, Encoding, NewLine]:
660 """Return a tuple of (decoded_contents, encoding, newline).
662 `newline` is either CRLF or LF but `decoded_contents` is decoded with
663 universal newlines (i.e. only contains LF).
665 srcbuf = io.BytesIO(src)
666 encoding, lines = tokenize.detect_encoding(srcbuf.readline)
668 return "", encoding, "\n"
670 newline = "\r\n" if b"\r\n" == lines[0][-2:] else "\n"
672 with io.TextIOWrapper(srcbuf, encoding) as tiow:
673 return tiow.read(), encoding, newline
677 pygram.python_grammar_no_print_statement_no_exec_statement,
678 pygram.python_grammar_no_print_statement,
679 pygram.python_grammar,
683 def lib2to3_parse(src_txt: str) -> Node:
684 """Given a string with source, return the lib2to3 Node."""
685 if src_txt[-1:] != "\n":
687 for grammar in GRAMMARS:
688 drv = driver.Driver(grammar, pytree.convert)
690 result = drv.parse_string(src_txt, True)
693 except ParseError as pe:
694 lineno, column = pe.context[1]
695 lines = src_txt.splitlines()
697 faulty_line = lines[lineno - 1]
699 faulty_line = "<line number missing in source>"
700 exc = InvalidInput(f"Cannot parse: {lineno}:{column}: {faulty_line}")
704 if isinstance(result, Leaf):
705 result = Node(syms.file_input, [result])
709 def lib2to3_unparse(node: Node) -> str:
710 """Given a lib2to3 node, return its string representation."""
718 class Visitor(Generic[T]):
719 """Basic lib2to3 visitor that yields things of type `T` on `visit()`."""
721 def visit(self, node: LN) -> Iterator[T]:
722 """Main method to visit `node` and its children.
724 It tries to find a `visit_*()` method for the given `node.type`, like
725 `visit_simple_stmt` for Node objects or `visit_INDENT` for Leaf objects.
726 If no dedicated `visit_*()` method is found, chooses `visit_default()`
729 Then yields objects of type `T` from the selected visitor.
732 name = token.tok_name[node.type]
734 name = type_repr(node.type)
735 yield from getattr(self, f"visit_{name}", self.visit_default)(node)
737 def visit_default(self, node: LN) -> Iterator[T]:
738 """Default `visit_*()` implementation. Recurses to children of `node`."""
739 if isinstance(node, Node):
740 for child in node.children:
741 yield from self.visit(child)
745 class DebugVisitor(Visitor[T]):
748 def visit_default(self, node: LN) -> Iterator[T]:
749 indent = " " * (2 * self.tree_depth)
750 if isinstance(node, Node):
751 _type = type_repr(node.type)
752 out(f"{indent}{_type}", fg="yellow")
754 for child in node.children:
755 yield from self.visit(child)
758 out(f"{indent}/{_type}", fg="yellow", bold=False)
760 _type = token.tok_name.get(node.type, str(node.type))
761 out(f"{indent}{_type}", fg="blue", nl=False)
763 # We don't have to handle prefixes for `Node` objects since
764 # that delegates to the first child anyway.
765 out(f" {node.prefix!r}", fg="green", bold=False, nl=False)
766 out(f" {node.value!r}", fg="blue", bold=False)
769 def show(cls, code: Union[str, Leaf, Node]) -> None:
770 """Pretty-print the lib2to3 AST of a given string of `code`.
772 Convenience method for debugging.
774 v: DebugVisitor[None] = DebugVisitor()
775 if isinstance(code, str):
776 code = lib2to3_parse(code)
780 KEYWORDS = set(keyword.kwlist)
781 WHITESPACE = {token.DEDENT, token.INDENT, token.NEWLINE}
782 FLOW_CONTROL = {"return", "raise", "break", "continue"}
793 STANDALONE_COMMENT = 153
794 token.tok_name[STANDALONE_COMMENT] = "STANDALONE_COMMENT"
795 LOGIC_OPERATORS = {"and", "or"}
820 STARS = {token.STAR, token.DOUBLESTAR}
823 syms.argument, # double star in arglist
824 syms.trailer, # single argument to call
826 syms.varargslist, # lambdas
828 UNPACKING_PARENTS = {
829 syms.atom, # single element of a list or set literal
833 syms.testlist_star_expr,
868 COMPREHENSION_PRIORITY = 20
870 TERNARY_PRIORITY = 16
873 COMPARATOR_PRIORITY = 10
884 token.DOUBLESLASH: 4,
894 class BracketTracker:
895 """Keeps track of brackets on a line."""
898 bracket_match: Dict[Tuple[Depth, NodeType], Leaf] = Factory(dict)
899 delimiters: Dict[LeafID, Priority] = Factory(dict)
900 previous: Optional[Leaf] = None
901 _for_loop_depths: List[int] = Factory(list)
902 _lambda_argument_depths: List[int] = Factory(list)
904 def mark(self, leaf: Leaf) -> None:
905 """Mark `leaf` with bracket-related metadata. Keep track of delimiters.
907 All leaves receive an int `bracket_depth` field that stores how deep
908 within brackets a given leaf is. 0 means there are no enclosing brackets
909 that started on this line.
911 If a leaf is itself a closing bracket, it receives an `opening_bracket`
912 field that it forms a pair with. This is a one-directional link to
913 avoid reference cycles.
915 If a leaf is a delimiter (a token on which Black can split the line if
916 needed) and it's on depth 0, its `id()` is stored in the tracker's
919 if leaf.type == token.COMMENT:
922 self.maybe_decrement_after_for_loop_variable(leaf)
923 self.maybe_decrement_after_lambda_arguments(leaf)
924 if leaf.type in CLOSING_BRACKETS:
926 opening_bracket = self.bracket_match.pop((self.depth, leaf.type))
927 leaf.opening_bracket = opening_bracket
928 leaf.bracket_depth = self.depth
930 delim = is_split_before_delimiter(leaf, self.previous)
931 if delim and self.previous is not None:
932 self.delimiters[id(self.previous)] = delim
934 delim = is_split_after_delimiter(leaf, self.previous)
936 self.delimiters[id(leaf)] = delim
937 if leaf.type in OPENING_BRACKETS:
938 self.bracket_match[self.depth, BRACKET[leaf.type]] = leaf
941 self.maybe_increment_lambda_arguments(leaf)
942 self.maybe_increment_for_loop_variable(leaf)
944 def any_open_brackets(self) -> bool:
945 """Return True if there is an yet unmatched open bracket on the line."""
946 return bool(self.bracket_match)
948 def max_delimiter_priority(self, exclude: Iterable[LeafID] = ()) -> int:
949 """Return the highest priority of a delimiter found on the line.
951 Values are consistent with what `is_split_*_delimiter()` return.
952 Raises ValueError on no delimiters.
954 return max(v for k, v in self.delimiters.items() if k not in exclude)
956 def delimiter_count_with_priority(self, priority: int = 0) -> int:
957 """Return the number of delimiters with the given `priority`.
959 If no `priority` is passed, defaults to max priority on the line.
961 if not self.delimiters:
964 priority = priority or self.max_delimiter_priority()
965 return sum(1 for p in self.delimiters.values() if p == priority)
967 def maybe_increment_for_loop_variable(self, leaf: Leaf) -> bool:
968 """In a for loop, or comprehension, the variables are often unpacks.
970 To avoid splitting on the comma in this situation, increase the depth of
971 tokens between `for` and `in`.
973 if leaf.type == token.NAME and leaf.value == "for":
975 self._for_loop_depths.append(self.depth)
980 def maybe_decrement_after_for_loop_variable(self, leaf: Leaf) -> bool:
981 """See `maybe_increment_for_loop_variable` above for explanation."""
983 self._for_loop_depths
984 and self._for_loop_depths[-1] == self.depth
985 and leaf.type == token.NAME
986 and leaf.value == "in"
989 self._for_loop_depths.pop()
994 def maybe_increment_lambda_arguments(self, leaf: Leaf) -> bool:
995 """In a lambda expression, there might be more than one argument.
997 To avoid splitting on the comma in this situation, increase the depth of
998 tokens between `lambda` and `:`.
1000 if leaf.type == token.NAME and leaf.value == "lambda":
1002 self._lambda_argument_depths.append(self.depth)
1007 def maybe_decrement_after_lambda_arguments(self, leaf: Leaf) -> bool:
1008 """See `maybe_increment_lambda_arguments` above for explanation."""
1010 self._lambda_argument_depths
1011 and self._lambda_argument_depths[-1] == self.depth
1012 and leaf.type == token.COLON
1015 self._lambda_argument_depths.pop()
1020 def get_open_lsqb(self) -> Optional[Leaf]:
1021 """Return the most recent opening square bracket (if any)."""
1022 return self.bracket_match.get((self.depth - 1, token.RSQB))
1027 """Holds leaves and comments. Can be printed with `str(line)`."""
1030 leaves: List[Leaf] = Factory(list)
1031 # The LeafID keys of comments must remain ordered by the corresponding leaf's index
1033 comments: Dict[LeafID, List[Leaf]] = Factory(dict)
1034 bracket_tracker: BracketTracker = Factory(BracketTracker)
1035 inside_brackets: bool = False
1036 should_explode: bool = False
1038 def append(self, leaf: Leaf, preformatted: bool = False) -> None:
1039 """Add a new `leaf` to the end of the line.
1041 Unless `preformatted` is True, the `leaf` will receive a new consistent
1042 whitespace prefix and metadata applied by :class:`BracketTracker`.
1043 Trailing commas are maybe removed, unpacked for loop variables are
1044 demoted from being delimiters.
1046 Inline comments are put aside.
1048 has_value = leaf.type in BRACKETS or bool(leaf.value.strip())
1052 if token.COLON == leaf.type and self.is_class_paren_empty:
1053 del self.leaves[-2:]
1054 if self.leaves and not preformatted:
1055 # Note: at this point leaf.prefix should be empty except for
1056 # imports, for which we only preserve newlines.
1057 leaf.prefix += whitespace(
1058 leaf, complex_subscript=self.is_complex_subscript(leaf)
1060 if self.inside_brackets or not preformatted:
1061 self.bracket_tracker.mark(leaf)
1062 self.maybe_remove_trailing_comma(leaf)
1063 if not self.append_comment(leaf):
1064 self.leaves.append(leaf)
1066 def append_safe(self, leaf: Leaf, preformatted: bool = False) -> None:
1067 """Like :func:`append()` but disallow invalid standalone comment structure.
1069 Raises ValueError when any `leaf` is appended after a standalone comment
1070 or when a standalone comment is not the first leaf on the line.
1072 if self.bracket_tracker.depth == 0:
1074 raise ValueError("cannot append to standalone comments")
1076 if self.leaves and leaf.type == STANDALONE_COMMENT:
1078 "cannot append standalone comments to a populated line"
1081 self.append(leaf, preformatted=preformatted)
1084 def is_comment(self) -> bool:
1085 """Is this line a standalone comment?"""
1086 return len(self.leaves) == 1 and self.leaves[0].type == STANDALONE_COMMENT
1089 def is_decorator(self) -> bool:
1090 """Is this line a decorator?"""
1091 return bool(self) and self.leaves[0].type == token.AT
1094 def is_import(self) -> bool:
1095 """Is this an import line?"""
1096 return bool(self) and is_import(self.leaves[0])
1099 def is_class(self) -> bool:
1100 """Is this line a class definition?"""
1103 and self.leaves[0].type == token.NAME
1104 and self.leaves[0].value == "class"
1108 def is_stub_class(self) -> bool:
1109 """Is this line a class definition with a body consisting only of "..."?"""
1110 return self.is_class and self.leaves[-3:] == [
1111 Leaf(token.DOT, ".") for _ in range(3)
1115 def is_def(self) -> bool:
1116 """Is this a function definition? (Also returns True for async defs.)"""
1118 first_leaf = self.leaves[0]
1123 second_leaf: Optional[Leaf] = self.leaves[1]
1126 return (first_leaf.type == token.NAME and first_leaf.value == "def") or (
1127 first_leaf.type == token.ASYNC
1128 and second_leaf is not None
1129 and second_leaf.type == token.NAME
1130 and second_leaf.value == "def"
1134 def is_class_paren_empty(self) -> bool:
1135 """Is this a class with no base classes but using parentheses?
1137 Those are unnecessary and should be removed.
1141 and len(self.leaves) == 4
1143 and self.leaves[2].type == token.LPAR
1144 and self.leaves[2].value == "("
1145 and self.leaves[3].type == token.RPAR
1146 and self.leaves[3].value == ")"
1150 def is_triple_quoted_string(self) -> bool:
1151 """Is the line a triple quoted string?"""
1154 and self.leaves[0].type == token.STRING
1155 and self.leaves[0].value.startswith(('"""', "'''"))
1158 def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool:
1159 """If so, needs to be split before emitting."""
1160 for leaf in self.leaves:
1161 if leaf.type == STANDALONE_COMMENT:
1162 if leaf.bracket_depth <= depth_limit:
1167 def contains_multiline_strings(self) -> bool:
1168 for leaf in self.leaves:
1169 if is_multiline_string(leaf):
1174 def maybe_remove_trailing_comma(self, closing: Leaf) -> bool:
1175 """Remove trailing comma if there is one and it's safe."""
1178 and self.leaves[-1].type == token.COMMA
1179 and closing.type in CLOSING_BRACKETS
1183 if closing.type == token.RBRACE:
1184 self.remove_trailing_comma()
1187 if closing.type == token.RSQB:
1188 comma = self.leaves[-1]
1189 if comma.parent and comma.parent.type == syms.listmaker:
1190 self.remove_trailing_comma()
1193 # For parens let's check if it's safe to remove the comma.
1194 # Imports are always safe.
1196 self.remove_trailing_comma()
1199 # Otherwise, if the trailing one is the only one, we might mistakenly
1200 # change a tuple into a different type by removing the comma.
1201 depth = closing.bracket_depth + 1
1203 opening = closing.opening_bracket
1204 for _opening_index, leaf in enumerate(self.leaves):
1211 for leaf in self.leaves[_opening_index + 1 :]:
1215 bracket_depth = leaf.bracket_depth
1216 if bracket_depth == depth and leaf.type == token.COMMA:
1218 if leaf.parent and leaf.parent.type == syms.arglist:
1223 self.remove_trailing_comma()
1228 def append_comment(self, comment: Leaf) -> bool:
1229 """Add an inline or standalone comment to the line."""
1231 comment.type == STANDALONE_COMMENT
1232 and self.bracket_tracker.any_open_brackets()
1237 if comment.type != token.COMMENT:
1241 comment.type = STANDALONE_COMMENT
1246 leaf_id = id(self.leaves[-1])
1247 if leaf_id not in self.comments:
1248 self.comments[leaf_id] = [comment]
1250 self.comments[leaf_id].append(comment)
1253 def comments_after(self, leaf: Leaf) -> List[Leaf]:
1254 """Generate comments that should appear directly after `leaf`."""
1255 return self.comments.get(id(leaf), [])
1257 def remove_trailing_comma(self) -> None:
1258 """Remove the trailing comma and moves the comments attached to it."""
1259 # Remember, the LeafID keys of self.comments are ordered by the
1260 # corresponding leaf's index in self.leaves
1261 # If id(self.leaves[-2]) is in self.comments, the order doesn't change.
1262 # Otherwise, we insert it into self.comments, and it becomes the last entry.
1263 # However, since we delete id(self.leaves[-1]) from self.comments, the invariant
1265 self.comments.setdefault(id(self.leaves[-2]), []).extend(
1266 self.comments.get(id(self.leaves[-1]), [])
1268 self.comments.pop(id(self.leaves[-1]), None)
1271 def is_complex_subscript(self, leaf: Leaf) -> bool:
1272 """Return True iff `leaf` is part of a slice with non-trivial exprs."""
1273 open_lsqb = self.bracket_tracker.get_open_lsqb()
1274 if open_lsqb is None:
1277 subscript_start = open_lsqb.next_sibling
1279 if isinstance(subscript_start, Node):
1280 if subscript_start.type == syms.listmaker:
1283 if subscript_start.type == syms.subscriptlist:
1284 subscript_start = child_towards(subscript_start, leaf)
1285 return subscript_start is not None and any(
1286 n.type in TEST_DESCENDANTS for n in subscript_start.pre_order()
1289 def __str__(self) -> str:
1290 """Render the line."""
1294 indent = " " * self.depth
1295 leaves = iter(self.leaves)
1296 first = next(leaves)
1297 res = f"{first.prefix}{indent}{first.value}"
1300 for comment in itertools.chain.from_iterable(self.comments.values()):
1304 def __bool__(self) -> bool:
1305 """Return True if the line has leaves or comments."""
1306 return bool(self.leaves or self.comments)
1310 class EmptyLineTracker:
1311 """Provides a stateful method that returns the number of potential extra
1312 empty lines needed before and after the currently processed line.
1314 Note: this tracker works on lines that haven't been split yet. It assumes
1315 the prefix of the first leaf consists of optional newlines. Those newlines
1316 are consumed by `maybe_empty_lines()` and included in the computation.
1319 is_pyi: bool = False
1320 previous_line: Optional[Line] = None
1321 previous_after: int = 0
1322 previous_defs: List[int] = Factory(list)
1324 def maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
1325 """Return the number of extra empty lines before and after the `current_line`.
1327 This is for separating `def`, `async def` and `class` with extra empty
1328 lines (two on module-level).
1330 before, after = self._maybe_empty_lines(current_line)
1331 before -= self.previous_after
1332 self.previous_after = after
1333 self.previous_line = current_line
1334 return before, after
1336 def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
1338 if current_line.depth == 0:
1339 max_allowed = 1 if self.is_pyi else 2
1340 if current_line.leaves:
1341 # Consume the first leaf's extra newlines.
1342 first_leaf = current_line.leaves[0]
1343 before = first_leaf.prefix.count("\n")
1344 before = min(before, max_allowed)
1345 first_leaf.prefix = ""
1348 depth = current_line.depth
1349 while self.previous_defs and self.previous_defs[-1] >= depth:
1350 self.previous_defs.pop()
1352 before = 0 if depth else 1
1354 before = 1 if depth else 2
1355 if current_line.is_decorator or current_line.is_def or current_line.is_class:
1356 return self._maybe_empty_lines_for_class_or_def(current_line, before)
1360 and self.previous_line.is_import
1361 and not current_line.is_import
1362 and depth == self.previous_line.depth
1364 return (before or 1), 0
1368 and self.previous_line.is_class
1369 and current_line.is_triple_quoted_string
1375 def _maybe_empty_lines_for_class_or_def(
1376 self, current_line: Line, before: int
1377 ) -> Tuple[int, int]:
1378 if not current_line.is_decorator:
1379 self.previous_defs.append(current_line.depth)
1380 if self.previous_line is None:
1381 # Don't insert empty lines before the first line in the file.
1384 if self.previous_line.is_decorator:
1387 if self.previous_line.depth < current_line.depth and (
1388 self.previous_line.is_class or self.previous_line.is_def
1393 self.previous_line.is_comment
1394 and self.previous_line.depth == current_line.depth
1400 if self.previous_line.depth > current_line.depth:
1402 elif current_line.is_class or self.previous_line.is_class:
1403 if current_line.is_stub_class and self.previous_line.is_stub_class:
1404 # No blank line between classes with an empty body
1408 elif current_line.is_def and not self.previous_line.is_def:
1409 # Blank line between a block of functions and a block of non-functions
1415 if current_line.depth and newlines:
1421 class LineGenerator(Visitor[Line]):
1422 """Generates reformatted Line objects. Empty lines are not emitted.
1424 Note: destroys the tree it's visiting by mutating prefixes of its leaves
1425 in ways that will no longer stringify to valid Python code on the tree.
1428 is_pyi: bool = False
1429 normalize_strings: bool = True
1430 current_line: Line = Factory(Line)
1431 remove_u_prefix: bool = False
1432 allow_underscores: bool = False
1434 def line(self, indent: int = 0) -> Iterator[Line]:
1437 If the line is empty, only emit if it makes sense.
1438 If the line is too long, split it first and then generate.
1440 If any lines were generated, set up a new current_line.
1442 if not self.current_line:
1443 self.current_line.depth += indent
1444 return # Line is empty, don't emit. Creating a new one unnecessary.
1446 complete_line = self.current_line
1447 self.current_line = Line(depth=complete_line.depth + indent)
1450 def visit_default(self, node: LN) -> Iterator[Line]:
1451 """Default `visit_*()` implementation. Recurses to children of `node`."""
1452 if isinstance(node, Leaf):
1453 any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
1454 for comment in generate_comments(node):
1455 if any_open_brackets:
1456 # any comment within brackets is subject to splitting
1457 self.current_line.append(comment)
1458 elif comment.type == token.COMMENT:
1459 # regular trailing comment
1460 self.current_line.append(comment)
1461 yield from self.line()
1464 # regular standalone comment
1465 yield from self.line()
1467 self.current_line.append(comment)
1468 yield from self.line()
1470 normalize_prefix(node, inside_brackets=any_open_brackets)
1471 if self.normalize_strings and node.type == token.STRING:
1472 normalize_string_prefix(node, remove_u_prefix=self.remove_u_prefix)
1473 normalize_string_quotes(node)
1474 if node.type == token.NUMBER:
1475 normalize_numeric_literal(node, self.allow_underscores)
1476 if node.type not in WHITESPACE:
1477 self.current_line.append(node)
1478 yield from super().visit_default(node)
1480 def visit_INDENT(self, node: Node) -> Iterator[Line]:
1481 """Increase indentation level, maybe yield a line."""
1482 # In blib2to3 INDENT never holds comments.
1483 yield from self.line(+1)
1484 yield from self.visit_default(node)
1486 def visit_DEDENT(self, node: Node) -> Iterator[Line]:
1487 """Decrease indentation level, maybe yield a line."""
1488 # The current line might still wait for trailing comments. At DEDENT time
1489 # there won't be any (they would be prefixes on the preceding NEWLINE).
1490 # Emit the line then.
1491 yield from self.line()
1493 # While DEDENT has no value, its prefix may contain standalone comments
1494 # that belong to the current indentation level. Get 'em.
1495 yield from self.visit_default(node)
1497 # Finally, emit the dedent.
1498 yield from self.line(-1)
1501 self, node: Node, keywords: Set[str], parens: Set[str]
1502 ) -> Iterator[Line]:
1503 """Visit a statement.
1505 This implementation is shared for `if`, `while`, `for`, `try`, `except`,
1506 `def`, `with`, `class`, `assert` and assignments.
1508 The relevant Python language `keywords` for a given statement will be
1509 NAME leaves within it. This methods puts those on a separate line.
1511 `parens` holds a set of string leaf values immediately after which
1512 invisible parens should be put.
1514 normalize_invisible_parens(node, parens_after=parens)
1515 for child in node.children:
1516 if child.type == token.NAME and child.value in keywords: # type: ignore
1517 yield from self.line()
1519 yield from self.visit(child)
1521 def visit_suite(self, node: Node) -> Iterator[Line]:
1522 """Visit a suite."""
1523 if self.is_pyi and is_stub_suite(node):
1524 yield from self.visit(node.children[2])
1526 yield from self.visit_default(node)
1528 def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
1529 """Visit a statement without nested statements."""
1530 is_suite_like = node.parent and node.parent.type in STATEMENT
1532 if self.is_pyi and is_stub_body(node):
1533 yield from self.visit_default(node)
1535 yield from self.line(+1)
1536 yield from self.visit_default(node)
1537 yield from self.line(-1)
1540 if not self.is_pyi or not node.parent or not is_stub_suite(node.parent):
1541 yield from self.line()
1542 yield from self.visit_default(node)
1544 def visit_async_stmt(self, node: Node) -> Iterator[Line]:
1545 """Visit `async def`, `async for`, `async with`."""
1546 yield from self.line()
1548 children = iter(node.children)
1549 for child in children:
1550 yield from self.visit(child)
1552 if child.type == token.ASYNC:
1555 internal_stmt = next(children)
1556 for child in internal_stmt.children:
1557 yield from self.visit(child)
1559 def visit_decorators(self, node: Node) -> Iterator[Line]:
1560 """Visit decorators."""
1561 for child in node.children:
1562 yield from self.line()
1563 yield from self.visit(child)
1565 def visit_SEMI(self, leaf: Leaf) -> Iterator[Line]:
1566 """Remove a semicolon and put the other statement on a separate line."""
1567 yield from self.line()
1569 def visit_ENDMARKER(self, leaf: Leaf) -> Iterator[Line]:
1570 """End of file. Process outstanding comments and end with a newline."""
1571 yield from self.visit_default(leaf)
1572 yield from self.line()
1574 def visit_STANDALONE_COMMENT(self, leaf: Leaf) -> Iterator[Line]:
1575 if not self.current_line.bracket_tracker.any_open_brackets():
1576 yield from self.line()
1577 yield from self.visit_default(leaf)
1579 def __attrs_post_init__(self) -> None:
1580 """You are in a twisty little maze of passages."""
1583 self.visit_assert_stmt = partial(v, keywords={"assert"}, parens={"assert", ","})
1584 self.visit_if_stmt = partial(
1585 v, keywords={"if", "else", "elif"}, parens={"if", "elif"}
1587 self.visit_while_stmt = partial(v, keywords={"while", "else"}, parens={"while"})
1588 self.visit_for_stmt = partial(v, keywords={"for", "else"}, parens={"for", "in"})
1589 self.visit_try_stmt = partial(
1590 v, keywords={"try", "except", "else", "finally"}, parens=Ø
1592 self.visit_except_clause = partial(v, keywords={"except"}, parens=Ø)
1593 self.visit_with_stmt = partial(v, keywords={"with"}, parens=Ø)
1594 self.visit_funcdef = partial(v, keywords={"def"}, parens=Ø)
1595 self.visit_classdef = partial(v, keywords={"class"}, parens=Ø)
1596 self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS)
1597 self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"})
1598 self.visit_import_from = partial(v, keywords=Ø, parens={"import"})
1599 self.visit_async_funcdef = self.visit_async_stmt
1600 self.visit_decorated = self.visit_decorators
1603 IMPLICIT_TUPLE = {syms.testlist, syms.testlist_star_expr, syms.exprlist}
1604 BRACKET = {token.LPAR: token.RPAR, token.LSQB: token.RSQB, token.LBRACE: token.RBRACE}
1605 OPENING_BRACKETS = set(BRACKET.keys())
1606 CLOSING_BRACKETS = set(BRACKET.values())
1607 BRACKETS = OPENING_BRACKETS | CLOSING_BRACKETS
1608 ALWAYS_NO_SPACE = CLOSING_BRACKETS | {token.COMMA, STANDALONE_COMMENT}
1611 def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str: # noqa C901
1612 """Return whitespace prefix if needed for the given `leaf`.
1614 `complex_subscript` signals whether the given leaf is part of a subscription
1615 which has non-trivial arguments, like arithmetic expressions or function calls.
1623 if t in ALWAYS_NO_SPACE:
1626 if t == token.COMMENT:
1629 assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
1630 if t == token.COLON and p.type not in {
1637 prev = leaf.prev_sibling
1639 prevp = preceding_leaf(p)
1640 if not prevp or prevp.type in OPENING_BRACKETS:
1643 if t == token.COLON:
1644 if prevp.type == token.COLON:
1647 elif prevp.type != token.COMMA and not complex_subscript:
1652 if prevp.type == token.EQUAL:
1654 if prevp.parent.type in {
1662 elif prevp.parent.type == syms.typedargslist:
1663 # A bit hacky: if the equal sign has whitespace, it means we
1664 # previously found it's a typed argument. So, we're using
1668 elif prevp.type in STARS:
1669 if is_vararg(prevp, within=VARARGS_PARENTS | UNPACKING_PARENTS):
1672 elif prevp.type == token.COLON:
1673 if prevp.parent and prevp.parent.type in {syms.subscript, syms.sliceop}:
1674 return SPACE if complex_subscript else NO
1678 and prevp.parent.type == syms.factor
1679 and prevp.type in MATH_OPERATORS
1684 prevp.type == token.RIGHTSHIFT
1686 and prevp.parent.type == syms.shift_expr
1687 and prevp.prev_sibling
1688 and prevp.prev_sibling.type == token.NAME
1689 and prevp.prev_sibling.value == "print" # type: ignore
1691 # Python 2 print chevron
1694 elif prev.type in OPENING_BRACKETS:
1697 if p.type in {syms.parameters, syms.arglist}:
1698 # untyped function signatures or calls
1699 if not prev or prev.type != token.COMMA:
1702 elif p.type == syms.varargslist:
1704 if prev and prev.type != token.COMMA:
1707 elif p.type == syms.typedargslist:
1708 # typed function signatures
1712 if t == token.EQUAL:
1713 if prev.type != syms.tname:
1716 elif prev.type == token.EQUAL:
1717 # A bit hacky: if the equal sign has whitespace, it means we
1718 # previously found it's a typed argument. So, we're using that, too.
1721 elif prev.type != token.COMMA:
1724 elif p.type == syms.tname:
1727 prevp = preceding_leaf(p)
1728 if not prevp or prevp.type != token.COMMA:
1731 elif p.type == syms.trailer:
1732 # attributes and calls
1733 if t == token.LPAR or t == token.RPAR:
1738 prevp = preceding_leaf(p)
1739 if not prevp or prevp.type != token.NUMBER:
1742 elif t == token.LSQB:
1745 elif prev.type != token.COMMA:
1748 elif p.type == syms.argument:
1750 if t == token.EQUAL:
1754 prevp = preceding_leaf(p)
1755 if not prevp or prevp.type == token.LPAR:
1758 elif prev.type in {token.EQUAL} | STARS:
1761 elif p.type == syms.decorator:
1765 elif p.type == syms.dotted_name:
1769 prevp = preceding_leaf(p)
1770 if not prevp or prevp.type == token.AT or prevp.type == token.DOT:
1773 elif p.type == syms.classdef:
1777 if prev and prev.type == token.LPAR:
1780 elif p.type in {syms.subscript, syms.sliceop}:
1783 assert p.parent is not None, "subscripts are always parented"
1784 if p.parent.type == syms.subscriptlist:
1789 elif not complex_subscript:
1792 elif p.type == syms.atom:
1793 if prev and t == token.DOT:
1794 # dots, but not the first one.
1797 elif p.type == syms.dictsetmaker:
1799 if prev and prev.type == token.DOUBLESTAR:
1802 elif p.type in {syms.factor, syms.star_expr}:
1805 prevp = preceding_leaf(p)
1806 if not prevp or prevp.type in OPENING_BRACKETS:
1809 prevp_parent = prevp.parent
1810 assert prevp_parent is not None
1811 if prevp.type == token.COLON and prevp_parent.type in {
1817 elif prevp.type == token.EQUAL and prevp_parent.type == syms.argument:
1820 elif t in {token.NAME, token.NUMBER, token.STRING}:
1823 elif p.type == syms.import_from:
1825 if prev and prev.type == token.DOT:
1828 elif t == token.NAME:
1832 if prev and prev.type == token.DOT:
1835 elif p.type == syms.sliceop:
1841 def preceding_leaf(node: Optional[LN]) -> Optional[Leaf]:
1842 """Return the first leaf that precedes `node`, if any."""
1844 res = node.prev_sibling
1846 if isinstance(res, Leaf):
1850 return list(res.leaves())[-1]
1859 def child_towards(ancestor: Node, descendant: LN) -> Optional[LN]:
1860 """Return the child of `ancestor` that contains `descendant`."""
1861 node: Optional[LN] = descendant
1862 while node and node.parent != ancestor:
1867 def container_of(leaf: Leaf) -> LN:
1868 """Return `leaf` or one of its ancestors that is the topmost container of it.
1870 By "container" we mean a node where `leaf` is the very first child.
1872 same_prefix = leaf.prefix
1873 container: LN = leaf
1875 parent = container.parent
1879 if parent.children[0].prefix != same_prefix:
1882 if parent.type == syms.file_input:
1885 if parent.prev_sibling is not None and parent.prev_sibling.type in BRACKETS:
1892 def is_split_after_delimiter(leaf: Leaf, previous: Optional[Leaf] = None) -> int:
1893 """Return the priority of the `leaf` delimiter, given a line break after it.
1895 The delimiter priorities returned here are from those delimiters that would
1896 cause a line break after themselves.
1898 Higher numbers are higher priority.
1900 if leaf.type == token.COMMA:
1901 return COMMA_PRIORITY
1906 def is_split_before_delimiter(leaf: Leaf, previous: Optional[Leaf] = None) -> int:
1907 """Return the priority of the `leaf` delimiter, given a line break before it.
1909 The delimiter priorities returned here are from those delimiters that would
1910 cause a line break before themselves.
1912 Higher numbers are higher priority.
1914 if is_vararg(leaf, within=VARARGS_PARENTS | UNPACKING_PARENTS):
1915 # * and ** might also be MATH_OPERATORS but in this case they are not.
1916 # Don't treat them as a delimiter.
1920 leaf.type == token.DOT
1922 and leaf.parent.type not in {syms.import_from, syms.dotted_name}
1923 and (previous is None or previous.type in CLOSING_BRACKETS)
1928 leaf.type in MATH_OPERATORS
1930 and leaf.parent.type not in {syms.factor, syms.star_expr}
1932 return MATH_PRIORITIES[leaf.type]
1934 if leaf.type in COMPARATORS:
1935 return COMPARATOR_PRIORITY
1938 leaf.type == token.STRING
1939 and previous is not None
1940 and previous.type == token.STRING
1942 return STRING_PRIORITY
1944 if leaf.type not in {token.NAME, token.ASYNC}:
1950 and leaf.parent.type in {syms.comp_for, syms.old_comp_for}
1951 or leaf.type == token.ASYNC
1954 not isinstance(leaf.prev_sibling, Leaf)
1955 or leaf.prev_sibling.value != "async"
1957 return COMPREHENSION_PRIORITY
1962 and leaf.parent.type in {syms.comp_if, syms.old_comp_if}
1964 return COMPREHENSION_PRIORITY
1966 if leaf.value in {"if", "else"} and leaf.parent and leaf.parent.type == syms.test:
1967 return TERNARY_PRIORITY
1969 if leaf.value == "is":
1970 return COMPARATOR_PRIORITY
1975 and leaf.parent.type in {syms.comp_op, syms.comparison}
1977 previous is not None
1978 and previous.type == token.NAME
1979 and previous.value == "not"
1982 return COMPARATOR_PRIORITY
1987 and leaf.parent.type == syms.comp_op
1989 previous is not None
1990 and previous.type == token.NAME
1991 and previous.value == "is"
1994 return COMPARATOR_PRIORITY
1996 if leaf.value in LOGIC_OPERATORS and leaf.parent:
1997 return LOGIC_PRIORITY
2002 FMT_OFF = {"# fmt: off", "# fmt:off", "# yapf: disable"}
2003 FMT_ON = {"# fmt: on", "# fmt:on", "# yapf: enable"}
2006 def generate_comments(leaf: LN) -> Iterator[Leaf]:
2007 """Clean the prefix of the `leaf` and generate comments from it, if any.
2009 Comments in lib2to3 are shoved into the whitespace prefix. This happens
2010 in `pgen2/driver.py:Driver.parse_tokens()`. This was a brilliant implementation
2011 move because it does away with modifying the grammar to include all the
2012 possible places in which comments can be placed.
2014 The sad consequence for us though is that comments don't "belong" anywhere.
2015 This is why this function generates simple parentless Leaf objects for
2016 comments. We simply don't know what the correct parent should be.
2018 No matter though, we can live without this. We really only need to
2019 differentiate between inline and standalone comments. The latter don't
2020 share the line with any code.
2022 Inline comments are emitted as regular token.COMMENT leaves. Standalone
2023 are emitted with a fake STANDALONE_COMMENT token identifier.
2025 for pc in list_comments(leaf.prefix, is_endmarker=leaf.type == token.ENDMARKER):
2026 yield Leaf(pc.type, pc.value, prefix="\n" * pc.newlines)
2031 """Describes a piece of syntax that is a comment.
2033 It's not a :class:`blib2to3.pytree.Leaf` so that:
2035 * it can be cached (`Leaf` objects should not be reused more than once as
2036 they store their lineno, column, prefix, and parent information);
2037 * `newlines` and `consumed` fields are kept separate from the `value`. This
2038 simplifies handling of special marker comments like ``# fmt: off/on``.
2041 type: int # token.COMMENT or STANDALONE_COMMENT
2042 value: str # content of the comment
2043 newlines: int # how many newlines before the comment
2044 consumed: int # how many characters of the original leaf's prefix did we consume
2047 @lru_cache(maxsize=4096)
2048 def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]:
2049 """Return a list of :class:`ProtoComment` objects parsed from the given `prefix`."""
2050 result: List[ProtoComment] = []
2051 if not prefix or "#" not in prefix:
2056 for index, line in enumerate(prefix.split("\n")):
2057 consumed += len(line) + 1 # adding the length of the split '\n'
2058 line = line.lstrip()
2061 if not line.startswith("#"):
2064 if index == 0 and not is_endmarker:
2065 comment_type = token.COMMENT # simple trailing comment
2067 comment_type = STANDALONE_COMMENT
2068 comment = make_comment(line)
2071 type=comment_type, value=comment, newlines=nlines, consumed=consumed
2078 def make_comment(content: str) -> str:
2079 """Return a consistently formatted comment from the given `content` string.
2081 All comments (except for "##", "#!", "#:", '#'", "#%%") should have a single
2082 space between the hash sign and the content.
2084 If `content` didn't start with a hash sign, one is provided.
2086 content = content.rstrip()
2090 if content[0] == "#":
2091 content = content[1:]
2092 if content and content[0] not in " !:#'%":
2093 content = " " + content
2094 return "#" + content
2098 line: Line, line_length: int, inner: bool = False, py36: bool = False
2099 ) -> Iterator[Line]:
2100 """Split a `line` into potentially many lines.
2102 They should fit in the allotted `line_length` but might not be able to.
2103 `inner` signifies that there were a pair of brackets somewhere around the
2104 current `line`, possibly transitively. This means we can fallback to splitting
2105 by delimiters if the LHS/RHS don't yield any results.
2107 If `py36` is True, splitting may generate syntax that is only compatible
2108 with Python 3.6 and later.
2114 line_str = str(line).strip("\n")
2115 if not line.should_explode and is_line_short_enough(
2116 line, line_length=line_length, line_str=line_str
2121 split_funcs: List[SplitFunc]
2123 split_funcs = [left_hand_split]
2126 def rhs(line: Line, py36: bool = False) -> Iterator[Line]:
2127 for omit in generate_trailers_to_omit(line, line_length):
2128 lines = list(right_hand_split(line, line_length, py36, omit=omit))
2129 if is_line_short_enough(lines[0], line_length=line_length):
2133 # All splits failed, best effort split with no omits.
2134 # This mostly happens to multiline strings that are by definition
2135 # reported as not fitting a single line.
2136 yield from right_hand_split(line, py36)
2138 if line.inside_brackets:
2139 split_funcs = [delimiter_split, standalone_comment_split, rhs]
2142 for split_func in split_funcs:
2143 # We are accumulating lines in `result` because we might want to abort
2144 # mission and return the original line in the end, or attempt a different
2146 result: List[Line] = []
2148 for l in split_func(line, py36):
2149 if str(l).strip("\n") == line_str:
2150 raise CannotSplit("Split function returned an unchanged result")
2153 split_line(l, line_length=line_length, inner=True, py36=py36)
2166 def left_hand_split(line: Line, py36: bool = False) -> Iterator[Line]:
2167 """Split line into many lines, starting with the first matching bracket pair.
2169 Note: this usually looks weird, only use this for function definitions.
2170 Prefer RHS otherwise. This is why this function is not symmetrical with
2171 :func:`right_hand_split` which also handles optional parentheses.
2173 tail_leaves: List[Leaf] = []
2174 body_leaves: List[Leaf] = []
2175 head_leaves: List[Leaf] = []
2176 current_leaves = head_leaves
2177 matching_bracket = None
2178 for leaf in line.leaves:
2180 current_leaves is body_leaves
2181 and leaf.type in CLOSING_BRACKETS
2182 and leaf.opening_bracket is matching_bracket
2184 current_leaves = tail_leaves if body_leaves else head_leaves
2185 current_leaves.append(leaf)
2186 if current_leaves is head_leaves:
2187 if leaf.type in OPENING_BRACKETS:
2188 matching_bracket = leaf
2189 current_leaves = body_leaves
2190 if not matching_bracket:
2191 raise CannotSplit("No brackets found")
2193 head = bracket_split_build_line(head_leaves, line, matching_bracket)
2194 body = bracket_split_build_line(body_leaves, line, matching_bracket, is_body=True)
2195 tail = bracket_split_build_line(tail_leaves, line, matching_bracket)
2196 bracket_split_succeeded_or_raise(head, body, tail)
2197 for result in (head, body, tail):
2202 def right_hand_split(
2203 line: Line, line_length: int, py36: bool = False, omit: Collection[LeafID] = ()
2204 ) -> Iterator[Line]:
2205 """Split line into many lines, starting with the last matching bracket pair.
2207 If the split was by optional parentheses, attempt splitting without them, too.
2208 `omit` is a collection of closing bracket IDs that shouldn't be considered for
2211 Note: running this function modifies `bracket_depth` on the leaves of `line`.
2213 tail_leaves: List[Leaf] = []
2214 body_leaves: List[Leaf] = []
2215 head_leaves: List[Leaf] = []
2216 current_leaves = tail_leaves
2217 opening_bracket = None
2218 closing_bracket = None
2219 for leaf in reversed(line.leaves):
2220 if current_leaves is body_leaves:
2221 if leaf is opening_bracket:
2222 current_leaves = head_leaves if body_leaves else tail_leaves
2223 current_leaves.append(leaf)
2224 if current_leaves is tail_leaves:
2225 if leaf.type in CLOSING_BRACKETS and id(leaf) not in omit:
2226 opening_bracket = leaf.opening_bracket
2227 closing_bracket = leaf
2228 current_leaves = body_leaves
2229 if not (opening_bracket and closing_bracket and head_leaves):
2230 # If there is no opening or closing_bracket that means the split failed and
2231 # all content is in the tail. Otherwise, if `head_leaves` are empty, it means
2232 # the matching `opening_bracket` wasn't available on `line` anymore.
2233 raise CannotSplit("No brackets found")
2235 tail_leaves.reverse()
2236 body_leaves.reverse()
2237 head_leaves.reverse()
2238 head = bracket_split_build_line(head_leaves, line, opening_bracket)
2239 body = bracket_split_build_line(body_leaves, line, opening_bracket, is_body=True)
2240 tail = bracket_split_build_line(tail_leaves, line, opening_bracket)
2241 bracket_split_succeeded_or_raise(head, body, tail)
2243 # the body shouldn't be exploded
2244 not body.should_explode
2245 # the opening bracket is an optional paren
2246 and opening_bracket.type == token.LPAR
2247 and not opening_bracket.value
2248 # the closing bracket is an optional paren
2249 and closing_bracket.type == token.RPAR
2250 and not closing_bracket.value
2251 # it's not an import (optional parens are the only thing we can split on
2252 # in this case; attempting a split without them is a waste of time)
2253 and not line.is_import
2254 # there are no standalone comments in the body
2255 and not body.contains_standalone_comments(0)
2256 # and we can actually remove the parens
2257 and can_omit_invisible_parens(body, line_length)
2259 omit = {id(closing_bracket), *omit}
2261 yield from right_hand_split(line, line_length, py36=py36, omit=omit)
2267 or is_line_short_enough(body, line_length=line_length)
2270 "Splitting failed, body is still too long and can't be split."
2273 elif head.contains_multiline_strings() or tail.contains_multiline_strings():
2275 "The current optional pair of parentheses is bound to fail to "
2276 "satisfy the splitting algorithm because the head or the tail "
2277 "contains multiline strings which by definition never fit one "
2281 ensure_visible(opening_bracket)
2282 ensure_visible(closing_bracket)
2283 for result in (head, body, tail):
2288 def bracket_split_succeeded_or_raise(head: Line, body: Line, tail: Line) -> None:
2289 """Raise :exc:`CannotSplit` if the last left- or right-hand split failed.
2291 Do nothing otherwise.
2293 A left- or right-hand split is based on a pair of brackets. Content before
2294 (and including) the opening bracket is left on one line, content inside the
2295 brackets is put on a separate line, and finally content starting with and
2296 following the closing bracket is put on a separate line.
2298 Those are called `head`, `body`, and `tail`, respectively. If the split
2299 produced the same line (all content in `head`) or ended up with an empty `body`
2300 and the `tail` is just the closing bracket, then it's considered failed.
2302 tail_len = len(str(tail).strip())
2305 raise CannotSplit("Splitting brackets produced the same line")
2309 f"Splitting brackets on an empty body to save "
2310 f"{tail_len} characters is not worth it"
2314 def bracket_split_build_line(
2315 leaves: List[Leaf], original: Line, opening_bracket: Leaf, *, is_body: bool = False
2317 """Return a new line with given `leaves` and respective comments from `original`.
2319 If `is_body` is True, the result line is one-indented inside brackets and as such
2320 has its first leaf's prefix normalized and a trailing comma added when expected.
2322 result = Line(depth=original.depth)
2324 result.inside_brackets = True
2327 # Since body is a new indent level, remove spurious leading whitespace.
2328 normalize_prefix(leaves[0], inside_brackets=True)
2329 # Ensure a trailing comma when expected.
2330 if original.is_import:
2331 if leaves[-1].type != token.COMMA:
2332 leaves.append(Leaf(token.COMMA, ","))
2335 result.append(leaf, preformatted=True)
2336 for comment_after in original.comments_after(leaf):
2337 result.append(comment_after, preformatted=True)
2339 result.should_explode = should_explode(result, opening_bracket)
2343 def dont_increase_indentation(split_func: SplitFunc) -> SplitFunc:
2344 """Normalize prefix of the first leaf in every line returned by `split_func`.
2346 This is a decorator over relevant split functions.
2350 def split_wrapper(line: Line, py36: bool = False) -> Iterator[Line]:
2351 for l in split_func(line, py36):
2352 normalize_prefix(l.leaves[0], inside_brackets=True)
2355 return split_wrapper
2358 @dont_increase_indentation
2359 def delimiter_split(line: Line, py36: bool = False) -> Iterator[Line]:
2360 """Split according to delimiters of the highest priority.
2362 If `py36` is True, the split will add trailing commas also in function
2363 signatures that contain `*` and `**`.
2366 last_leaf = line.leaves[-1]
2368 raise CannotSplit("Line empty")
2370 bt = line.bracket_tracker
2372 delimiter_priority = bt.max_delimiter_priority(exclude={id(last_leaf)})
2374 raise CannotSplit("No delimiters found")
2376 if delimiter_priority == DOT_PRIORITY:
2377 if bt.delimiter_count_with_priority(delimiter_priority) == 1:
2378 raise CannotSplit("Splitting a single attribute from its owner looks wrong")
2380 current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
2381 lowest_depth = sys.maxsize
2382 trailing_comma_safe = True
2384 def append_to_line(leaf: Leaf) -> Iterator[Line]:
2385 """Append `leaf` to current line or to new line if appending impossible."""
2386 nonlocal current_line
2388 current_line.append_safe(leaf, preformatted=True)
2392 current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
2393 current_line.append(leaf)
2395 for leaf in line.leaves:
2396 yield from append_to_line(leaf)
2398 for comment_after in line.comments_after(leaf):
2399 yield from append_to_line(comment_after)
2401 lowest_depth = min(lowest_depth, leaf.bracket_depth)
2402 if leaf.bracket_depth == lowest_depth and is_vararg(
2403 leaf, within=VARARGS_PARENTS
2405 trailing_comma_safe = trailing_comma_safe and py36
2406 leaf_priority = bt.delimiters.get(id(leaf))
2407 if leaf_priority == delimiter_priority:
2410 current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
2414 and delimiter_priority == COMMA_PRIORITY
2415 and current_line.leaves[-1].type != token.COMMA
2416 and current_line.leaves[-1].type != STANDALONE_COMMENT
2418 current_line.append(Leaf(token.COMMA, ","))
2422 @dont_increase_indentation
2423 def standalone_comment_split(line: Line, py36: bool = False) -> Iterator[Line]:
2424 """Split standalone comments from the rest of the line."""
2425 if not line.contains_standalone_comments(0):
2426 raise CannotSplit("Line does not have any standalone comments")
2428 current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
2430 def append_to_line(leaf: Leaf) -> Iterator[Line]:
2431 """Append `leaf` to current line or to new line if appending impossible."""
2432 nonlocal current_line
2434 current_line.append_safe(leaf, preformatted=True)
2438 current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
2439 current_line.append(leaf)
2441 for leaf in line.leaves:
2442 yield from append_to_line(leaf)
2444 for comment_after in line.comments_after(leaf):
2445 yield from append_to_line(comment_after)
2451 def is_import(leaf: Leaf) -> bool:
2452 """Return True if the given leaf starts an import statement."""
2459 (v == "import" and p and p.type == syms.import_name)
2460 or (v == "from" and p and p.type == syms.import_from)
2465 def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None:
2466 """Leave existing extra newlines if not `inside_brackets`. Remove everything
2469 Note: don't use backslashes for formatting or you'll lose your voting rights.
2471 if not inside_brackets:
2472 spl = leaf.prefix.split("#")
2473 if "\\" not in spl[0]:
2474 nl_count = spl[-1].count("\n")
2477 leaf.prefix = "\n" * nl_count
2483 def normalize_string_prefix(leaf: Leaf, remove_u_prefix: bool = False) -> None:
2484 """Make all string prefixes lowercase.
2486 If remove_u_prefix is given, also removes any u prefix from the string.
2488 Note: Mutates its argument.
2490 match = re.match(r"^([furbFURB]*)(.*)$", leaf.value, re.DOTALL)
2491 assert match is not None, f"failed to match string {leaf.value!r}"
2492 orig_prefix = match.group(1)
2493 new_prefix = orig_prefix.lower()
2495 new_prefix = new_prefix.replace("u", "")
2496 leaf.value = f"{new_prefix}{match.group(2)}"
2499 def normalize_string_quotes(leaf: Leaf) -> None:
2500 """Prefer double quotes but only if it doesn't cause more escaping.
2502 Adds or removes backslashes as appropriate. Doesn't parse and fix
2503 strings nested in f-strings (yet).
2505 Note: Mutates its argument.
2507 value = leaf.value.lstrip("furbFURB")
2508 if value[:3] == '"""':
2511 elif value[:3] == "'''":
2514 elif value[0] == '"':
2520 first_quote_pos = leaf.value.find(orig_quote)
2521 if first_quote_pos == -1:
2522 return # There's an internal error
2524 prefix = leaf.value[:first_quote_pos]
2525 unescaped_new_quote = re.compile(rf"(([^\\]|^)(\\\\)*){new_quote}")
2526 escaped_new_quote = re.compile(rf"([^\\]|^)\\((?:\\\\)*){new_quote}")
2527 escaped_orig_quote = re.compile(rf"([^\\]|^)\\((?:\\\\)*){orig_quote}")
2528 body = leaf.value[first_quote_pos + len(orig_quote) : -len(orig_quote)]
2529 if "r" in prefix.casefold():
2530 if unescaped_new_quote.search(body):
2531 # There's at least one unescaped new_quote in this raw string
2532 # so converting is impossible
2535 # Do not introduce or remove backslashes in raw strings
2538 # remove unnecessary escapes
2539 new_body = sub_twice(escaped_new_quote, rf"\1\2{new_quote}", body)
2540 if body != new_body:
2541 # Consider the string without unnecessary escapes as the original
2543 leaf.value = f"{prefix}{orig_quote}{body}{orig_quote}"
2544 new_body = sub_twice(escaped_orig_quote, rf"\1\2{orig_quote}", new_body)
2545 new_body = sub_twice(unescaped_new_quote, rf"\1\\{new_quote}", new_body)
2546 if "f" in prefix.casefold():
2547 matches = re.findall(r"[^{]\{(.*?)\}[^}]", new_body)
2550 # Do not introduce backslashes in interpolated expressions
2552 if new_quote == '"""' and new_body[-1:] == '"':
2554 new_body = new_body[:-1] + '\\"'
2555 orig_escape_count = body.count("\\")
2556 new_escape_count = new_body.count("\\")
2557 if new_escape_count > orig_escape_count:
2558 return # Do not introduce more escaping
2560 if new_escape_count == orig_escape_count and orig_quote == '"':
2561 return # Prefer double quotes
2563 leaf.value = f"{prefix}{new_quote}{new_body}{new_quote}"
2566 def normalize_numeric_literal(leaf: Leaf, allow_underscores: bool) -> None:
2567 """Normalizes numeric (float, int, and complex) literals.
2569 All letters used in the representation are normalized to lowercase (except
2570 in Python 2 long literals), and long number literals are split using underscores.
2572 text = leaf.value.lower()
2573 if text.startswith(("0o", "0b")):
2574 # Leave octal and binary literals alone.
2576 elif text.startswith("0x"):
2577 # Change hex literals to upper case.
2578 before, after = text[:2], text[2:]
2579 text = f"{before}{after.upper()}"
2581 before, after = text.split("e")
2583 if after.startswith("-"):
2586 elif after.startswith("+"):
2588 before = format_float_or_int_string(before, allow_underscores)
2589 after = format_int_string(after, allow_underscores)
2590 text = f"{before}e{sign}{after}"
2591 elif text.endswith(("j", "l")):
2594 # Capitalize in "2L" because "l" looks too similar to "1".
2597 text = f"{format_float_or_int_string(number, allow_underscores)}{suffix}"
2599 text = format_float_or_int_string(text, allow_underscores)
2603 def format_float_or_int_string(text: str, allow_underscores: bool) -> str:
2604 """Formats a float string like "1.0"."""
2606 return format_int_string(text, allow_underscores)
2608 before, after = text.split(".")
2609 before = format_int_string(before, allow_underscores) if before else "0"
2611 after = format_int_string(after, allow_underscores, count_from_end=False)
2614 return f"{before}.{after}"
2617 def format_int_string(
2618 text: str, allow_underscores: bool, count_from_end: bool = True
2620 """Normalizes underscores in a string to e.g. 1_000_000.
2622 Input must be a string of digits and optional underscores.
2623 If count_from_end is False, we add underscores after groups of three digits
2624 counting from the beginning instead of the end of the strings. This is used
2625 for the fractional part of float literals.
2627 if not allow_underscores:
2630 text = text.replace("_", "")
2632 # No underscores for numbers <= 5 digits long.
2636 # Avoid removing leading zeros, which are important if we're formatting
2637 # part of a number like "0.001".
2638 return format(int("1" + text), "3_")[1:].lstrip("_")
2640 return "_".join(text[i : i + 3] for i in range(0, len(text), 3))
2643 def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
2644 """Make existing optional parentheses invisible or create new ones.
2646 `parens_after` is a set of string leaf values immeditely after which parens
2649 Standardizes on visible parentheses for single-element tuples, and keeps
2650 existing visible parentheses for other tuples and generator expressions.
2652 for pc in list_comments(node.prefix, is_endmarker=False):
2653 if pc.value in FMT_OFF:
2654 # This `node` has a prefix with `# fmt: off`, don't mess with parens.
2658 for index, child in enumerate(list(node.children)):
2660 if child.type == syms.atom:
2661 if maybe_make_parens_invisible_in_atom(child):
2662 lpar = Leaf(token.LPAR, "")
2663 rpar = Leaf(token.RPAR, "")
2664 index = child.remove() or 0
2665 node.insert_child(index, Node(syms.atom, [lpar, child, rpar]))
2666 elif is_one_tuple(child):
2667 # wrap child in visible parentheses
2668 lpar = Leaf(token.LPAR, "(")
2669 rpar = Leaf(token.RPAR, ")")
2671 node.insert_child(index, Node(syms.atom, [lpar, child, rpar]))
2672 elif node.type == syms.import_from:
2673 # "import from" nodes store parentheses directly as part of
2675 if child.type == token.LPAR:
2676 # make parentheses invisible
2677 child.value = "" # type: ignore
2678 node.children[-1].value = "" # type: ignore
2679 elif child.type != token.STAR:
2680 # insert invisible parentheses
2681 node.insert_child(index, Leaf(token.LPAR, ""))
2682 node.append_child(Leaf(token.RPAR, ""))
2685 elif not (isinstance(child, Leaf) and is_multiline_string(child)):
2686 # wrap child in invisible parentheses
2687 lpar = Leaf(token.LPAR, "")
2688 rpar = Leaf(token.RPAR, "")
2689 index = child.remove() or 0
2690 node.insert_child(index, Node(syms.atom, [lpar, child, rpar]))
2692 check_lpar = isinstance(child, Leaf) and child.value in parens_after
2695 def normalize_fmt_off(node: Node) -> None:
2696 """Convert content between `# fmt: off`/`# fmt: on` into standalone comments."""
2699 try_again = convert_one_fmt_off_pair(node)
2702 def convert_one_fmt_off_pair(node: Node) -> bool:
2703 """Convert content of a single `# fmt: off`/`# fmt: on` into a standalone comment.
2705 Returns True if a pair was converted.
2707 for leaf in node.leaves():
2708 previous_consumed = 0
2709 for comment in list_comments(leaf.prefix, is_endmarker=False):
2710 if comment.value in FMT_OFF:
2711 # We only want standalone comments. If there's no previous leaf or
2712 # the previous leaf is indentation, it's a standalone comment in
2714 if comment.type != STANDALONE_COMMENT:
2715 prev = preceding_leaf(leaf)
2716 if prev and prev.type not in WHITESPACE:
2719 ignored_nodes = list(generate_ignored_nodes(leaf))
2720 if not ignored_nodes:
2723 first = ignored_nodes[0] # Can be a container node with the `leaf`.
2724 parent = first.parent
2725 prefix = first.prefix
2726 first.prefix = prefix[comment.consumed :]
2728 comment.value + "\n" + "".join(str(n) for n in ignored_nodes)
2730 if hidden_value.endswith("\n"):
2731 # That happens when one of the `ignored_nodes` ended with a NEWLINE
2732 # leaf (possibly followed by a DEDENT).
2733 hidden_value = hidden_value[:-1]
2735 for ignored in ignored_nodes:
2736 index = ignored.remove()
2737 if first_idx is None:
2739 assert parent is not None, "INTERNAL ERROR: fmt: on/off handling (1)"
2740 assert first_idx is not None, "INTERNAL ERROR: fmt: on/off handling (2)"
2741 parent.insert_child(
2746 prefix=prefix[:previous_consumed] + "\n" * comment.newlines,
2751 previous_consumed = comment.consumed
2756 def generate_ignored_nodes(leaf: Leaf) -> Iterator[LN]:
2757 """Starting from the container of `leaf`, generate all leaves until `# fmt: on`.
2759 Stops at the end of the block.
2761 container: Optional[LN] = container_of(leaf)
2762 while container is not None and container.type != token.ENDMARKER:
2763 for comment in list_comments(container.prefix, is_endmarker=False):
2764 if comment.value in FMT_ON:
2769 container = container.next_sibling
2772 def maybe_make_parens_invisible_in_atom(node: LN) -> bool:
2773 """If it's safe, make the parens in the atom `node` invisible, recursively.
2775 Returns whether the node should itself be wrapped in invisible parentheses.
2779 node.type != syms.atom
2780 or is_empty_tuple(node)
2781 or is_one_tuple(node)
2783 or max_delimiter_priority_in_atom(node) >= COMMA_PRIORITY
2787 first = node.children[0]
2788 last = node.children[-1]
2789 if first.type == token.LPAR and last.type == token.RPAR:
2790 # make parentheses invisible
2791 first.value = "" # type: ignore
2792 last.value = "" # type: ignore
2793 if len(node.children) > 1:
2794 maybe_make_parens_invisible_in_atom(node.children[1])
2800 def is_empty_tuple(node: LN) -> bool:
2801 """Return True if `node` holds an empty tuple."""
2803 node.type == syms.atom
2804 and len(node.children) == 2
2805 and node.children[0].type == token.LPAR
2806 and node.children[1].type == token.RPAR
2810 def is_one_tuple(node: LN) -> bool:
2811 """Return True if `node` holds a tuple with one element, with or without parens."""
2812 if node.type == syms.atom:
2813 if len(node.children) != 3:
2816 lpar, gexp, rpar = node.children
2818 lpar.type == token.LPAR
2819 and gexp.type == syms.testlist_gexp
2820 and rpar.type == token.RPAR
2824 return len(gexp.children) == 2 and gexp.children[1].type == token.COMMA
2827 node.type in IMPLICIT_TUPLE
2828 and len(node.children) == 2
2829 and node.children[1].type == token.COMMA
2833 def is_yield(node: LN) -> bool:
2834 """Return True if `node` holds a `yield` or `yield from` expression."""
2835 if node.type == syms.yield_expr:
2838 if node.type == token.NAME and node.value == "yield": # type: ignore
2841 if node.type != syms.atom:
2844 if len(node.children) != 3:
2847 lpar, expr, rpar = node.children
2848 if lpar.type == token.LPAR and rpar.type == token.RPAR:
2849 return is_yield(expr)
2854 def is_vararg(leaf: Leaf, within: Set[NodeType]) -> bool:
2855 """Return True if `leaf` is a star or double star in a vararg or kwarg.
2857 If `within` includes VARARGS_PARENTS, this applies to function signatures.
2858 If `within` includes UNPACKING_PARENTS, it applies to right hand-side
2859 extended iterable unpacking (PEP 3132) and additional unpacking
2860 generalizations (PEP 448).
2862 if leaf.type not in STARS or not leaf.parent:
2866 if p.type == syms.star_expr:
2867 # Star expressions are also used as assignment targets in extended
2868 # iterable unpacking (PEP 3132). See what its parent is instead.
2874 return p.type in within
2877 def is_multiline_string(leaf: Leaf) -> bool:
2878 """Return True if `leaf` is a multiline string that actually spans many lines."""
2879 value = leaf.value.lstrip("furbFURB")
2880 return value[:3] in {'"""', "'''"} and "\n" in value
2883 def is_stub_suite(node: Node) -> bool:
2884 """Return True if `node` is a suite with a stub body."""
2886 len(node.children) != 4
2887 or node.children[0].type != token.NEWLINE
2888 or node.children[1].type != token.INDENT
2889 or node.children[3].type != token.DEDENT
2893 return is_stub_body(node.children[2])
2896 def is_stub_body(node: LN) -> bool:
2897 """Return True if `node` is a simple statement containing an ellipsis."""
2898 if not isinstance(node, Node) or node.type != syms.simple_stmt:
2901 if len(node.children) != 2:
2904 child = node.children[0]
2906 child.type == syms.atom
2907 and len(child.children) == 3
2908 and all(leaf == Leaf(token.DOT, ".") for leaf in child.children)
2912 def max_delimiter_priority_in_atom(node: LN) -> int:
2913 """Return maximum delimiter priority inside `node`.
2915 This is specific to atoms with contents contained in a pair of parentheses.
2916 If `node` isn't an atom or there are no enclosing parentheses, returns 0.
2918 if node.type != syms.atom:
2921 first = node.children[0]
2922 last = node.children[-1]
2923 if not (first.type == token.LPAR and last.type == token.RPAR):
2926 bt = BracketTracker()
2927 for c in node.children[1:-1]:
2928 if isinstance(c, Leaf):
2931 for leaf in c.leaves():
2934 return bt.max_delimiter_priority()
2940 def ensure_visible(leaf: Leaf) -> None:
2941 """Make sure parentheses are visible.
2943 They could be invisible as part of some statements (see
2944 :func:`normalize_invible_parens` and :func:`visit_import_from`).
2946 if leaf.type == token.LPAR:
2948 elif leaf.type == token.RPAR:
2952 def should_explode(line: Line, opening_bracket: Leaf) -> bool:
2953 """Should `line` immediately be split with `delimiter_split()` after RHS?"""
2955 opening_bracket.parent
2956 and opening_bracket.parent.type in {syms.atom, syms.import_from}
2957 and opening_bracket.value in "[{("
2962 last_leaf = line.leaves[-1]
2963 exclude = {id(last_leaf)} if last_leaf.type == token.COMMA else set()
2964 max_priority = line.bracket_tracker.max_delimiter_priority(exclude=exclude)
2965 except (IndexError, ValueError):
2968 return max_priority == COMMA_PRIORITY
2971 def is_python36(node: Node) -> bool:
2972 """Return True if the current file is using Python 3.6+ features.
2974 Currently looking for:
2976 - underscores in numeric literals; and
2977 - trailing commas after * or ** in function signatures and calls.
2979 for n in node.pre_order():
2980 if n.type == token.STRING:
2981 value_head = n.value[:2] # type: ignore
2982 if value_head in {'f"', 'F"', "f'", "F'", "rf", "fr", "RF", "FR"}:
2985 elif n.type == token.NUMBER:
2986 if "_" in n.value: # type: ignore
2990 n.type in {syms.typedargslist, syms.arglist}
2992 and n.children[-1].type == token.COMMA
2994 for ch in n.children:
2995 if ch.type in STARS:
2998 if ch.type == syms.argument:
2999 for argch in ch.children:
3000 if argch.type in STARS:
3006 def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[LeafID]]:
3007 """Generate sets of closing bracket IDs that should be omitted in a RHS.
3009 Brackets can be omitted if the entire trailer up to and including
3010 a preceding closing bracket fits in one line.
3012 Yielded sets are cumulative (contain results of previous yields, too). First
3016 omit: Set[LeafID] = set()
3019 length = 4 * line.depth
3020 opening_bracket = None
3021 closing_bracket = None
3022 inner_brackets: Set[LeafID] = set()
3023 for index, leaf, leaf_length in enumerate_with_length(line, reversed=True):
3024 length += leaf_length
3025 if length > line_length:
3028 has_inline_comment = leaf_length > len(leaf.value) + len(leaf.prefix)
3029 if leaf.type == STANDALONE_COMMENT or has_inline_comment:
3033 if leaf is opening_bracket:
3034 opening_bracket = None
3035 elif leaf.type in CLOSING_BRACKETS:
3036 inner_brackets.add(id(leaf))
3037 elif leaf.type in CLOSING_BRACKETS:
3038 if index > 0 and line.leaves[index - 1].type in OPENING_BRACKETS:
3039 # Empty brackets would fail a split so treat them as "inner"
3040 # brackets (e.g. only add them to the `omit` set if another
3041 # pair of brackets was good enough.
3042 inner_brackets.add(id(leaf))
3046 omit.add(id(closing_bracket))
3047 omit.update(inner_brackets)
3048 inner_brackets.clear()
3052 opening_bracket = leaf.opening_bracket
3053 closing_bracket = leaf
3056 def get_future_imports(node: Node) -> Set[str]:
3057 """Return a set of __future__ imports in the file."""
3058 imports: Set[str] = set()
3060 def get_imports_from_children(children: List[LN]) -> Generator[str, None, None]:
3061 for child in children:
3062 if isinstance(child, Leaf):
3063 if child.type == token.NAME:
3065 elif child.type == syms.import_as_name:
3066 orig_name = child.children[0]
3067 assert isinstance(orig_name, Leaf), "Invalid syntax parsing imports"
3068 assert orig_name.type == token.NAME, "Invalid syntax parsing imports"
3069 yield orig_name.value
3070 elif child.type == syms.import_as_names:
3071 yield from get_imports_from_children(child.children)
3073 assert False, "Invalid syntax parsing imports"
3075 for child in node.children:
3076 if child.type != syms.simple_stmt:
3078 first_child = child.children[0]
3079 if isinstance(first_child, Leaf):
3080 # Continue looking if we see a docstring; otherwise stop.
3082 len(child.children) == 2
3083 and first_child.type == token.STRING
3084 and child.children[1].type == token.NEWLINE
3089 elif first_child.type == syms.import_from:
3090 module_name = first_child.children[1]
3091 if not isinstance(module_name, Leaf) or module_name.value != "__future__":
3093 imports |= set(get_imports_from_children(first_child.children[3:]))
3099 def gen_python_files_in_dir(
3102 include: Pattern[str],
3103 exclude: Pattern[str],
3105 ) -> Iterator[Path]:
3106 """Generate all files under `path` whose paths are not excluded by the
3107 `exclude` regex, but are included by the `include` regex.
3109 Symbolic links pointing outside of the `root` directory are ignored.
3111 `report` is where output about exclusions goes.
3113 assert root.is_absolute(), f"INTERNAL ERROR: `root` must be absolute but is {root}"
3114 for child in path.iterdir():
3116 normalized_path = "/" + child.resolve().relative_to(root).as_posix()
3118 if child.is_symlink():
3119 report.path_ignored(
3120 child, f"is a symbolic link that points outside {root}"
3127 normalized_path += "/"
3128 exclude_match = exclude.search(normalized_path)
3129 if exclude_match and exclude_match.group(0):
3130 report.path_ignored(child, f"matches the --exclude regular expression")
3134 yield from gen_python_files_in_dir(child, root, include, exclude, report)
3136 elif child.is_file():
3137 include_match = include.search(normalized_path)
3143 def find_project_root(srcs: Iterable[str]) -> Path:
3144 """Return a directory containing .git, .hg, or pyproject.toml.
3146 That directory can be one of the directories passed in `srcs` or their
3149 If no directory in the tree contains a marker that would specify it's the
3150 project root, the root of the file system is returned.
3153 return Path("/").resolve()
3155 common_base = min(Path(src).resolve() for src in srcs)
3156 if common_base.is_dir():
3157 # Append a fake file so `parents` below returns `common_base_dir`, too.
3158 common_base /= "fake-file"
3159 for directory in common_base.parents:
3160 if (directory / ".git").is_dir():
3163 if (directory / ".hg").is_dir():
3166 if (directory / "pyproject.toml").is_file():
3174 """Provides a reformatting counter. Can be rendered with `str(report)`."""
3178 verbose: bool = False
3179 change_count: int = 0
3181 failure_count: int = 0
3183 def done(self, src: Path, changed: Changed) -> None:
3184 """Increment the counter for successful reformatting. Write out a message."""
3185 if changed is Changed.YES:
3186 reformatted = "would reformat" if self.check else "reformatted"
3187 if self.verbose or not self.quiet:
3188 out(f"{reformatted} {src}")
3189 self.change_count += 1
3192 if changed is Changed.NO:
3193 msg = f"{src} already well formatted, good job."
3195 msg = f"{src} wasn't modified on disk since last run."
3196 out(msg, bold=False)
3197 self.same_count += 1
3199 def failed(self, src: Path, message: str) -> None:
3200 """Increment the counter for failed reformatting. Write out a message."""
3201 err(f"error: cannot format {src}: {message}")
3202 self.failure_count += 1
3204 def path_ignored(self, path: Path, message: str) -> None:
3206 out(f"{path} ignored: {message}", bold=False)
3209 def return_code(self) -> int:
3210 """Return the exit code that the app should use.
3212 This considers the current state of changed files and failures:
3213 - if there were any failures, return 123;
3214 - if any files were changed and --check is being used, return 1;
3215 - otherwise return 0.
3217 # According to http://tldp.org/LDP/abs/html/exitcodes.html starting with
3218 # 126 we have special return codes reserved by the shell.
3219 if self.failure_count:
3222 elif self.change_count and self.check:
3227 def __str__(self) -> str:
3228 """Render a color report of the current state.
3230 Use `click.unstyle` to remove colors.
3233 reformatted = "would be reformatted"
3234 unchanged = "would be left unchanged"
3235 failed = "would fail to reformat"
3237 reformatted = "reformatted"
3238 unchanged = "left unchanged"
3239 failed = "failed to reformat"
3241 if self.change_count:
3242 s = "s" if self.change_count > 1 else ""
3244 click.style(f"{self.change_count} file{s} {reformatted}", bold=True)
3247 s = "s" if self.same_count > 1 else ""
3248 report.append(f"{self.same_count} file{s} {unchanged}")
3249 if self.failure_count:
3250 s = "s" if self.failure_count > 1 else ""
3252 click.style(f"{self.failure_count} file{s} {failed}", fg="red")
3254 return ", ".join(report) + "."
3257 def assert_equivalent(src: str, dst: str) -> None:
3258 """Raise AssertionError if `src` and `dst` aren't equivalent."""
3263 def _v(node: ast.AST, depth: int = 0) -> Iterator[str]:
3264 """Simple visitor generating strings to compare ASTs by content."""
3265 yield f"{' ' * depth}{node.__class__.__name__}("
3267 for field in sorted(node._fields):
3269 value = getattr(node, field)
3270 except AttributeError:
3273 yield f"{' ' * (depth+1)}{field}="
3275 if isinstance(value, list):
3277 if isinstance(item, ast.AST):
3278 yield from _v(item, depth + 2)
3280 elif isinstance(value, ast.AST):
3281 yield from _v(value, depth + 2)
3284 yield f"{' ' * (depth+2)}{value!r}, # {value.__class__.__name__}"
3286 yield f"{' ' * depth}) # /{node.__class__.__name__}"
3289 src_ast = ast.parse(src)
3290 except Exception as exc:
3291 major, minor = sys.version_info[:2]
3292 raise AssertionError(
3293 f"cannot use --safe with this file; failed to parse source file "
3294 f"with Python {major}.{minor}'s builtin AST. Re-run with --fast "
3295 f"or stop using deprecated Python 2 syntax. AST error message: {exc}"
3299 dst_ast = ast.parse(dst)
3300 except Exception as exc:
3301 log = dump_to_file("".join(traceback.format_tb(exc.__traceback__)), dst)
3302 raise AssertionError(
3303 f"INTERNAL ERROR: Black produced invalid code: {exc}. "
3304 f"Please report a bug on https://github.com/ambv/black/issues. "
3305 f"This invalid output might be helpful: {log}"
3308 src_ast_str = "\n".join(_v(src_ast))
3309 dst_ast_str = "\n".join(_v(dst_ast))
3310 if src_ast_str != dst_ast_str:
3311 log = dump_to_file(diff(src_ast_str, dst_ast_str, "src", "dst"))
3312 raise AssertionError(
3313 f"INTERNAL ERROR: Black produced code that is not equivalent to "
3315 f"Please report a bug on https://github.com/ambv/black/issues. "
3316 f"This diff might be helpful: {log}"
3321 src: str, dst: str, line_length: int, mode: FileMode = FileMode.AUTO_DETECT
3323 """Raise AssertionError if `dst` reformats differently the second time."""
3324 newdst = format_str(dst, line_length=line_length, mode=mode)
3327 diff(src, dst, "source", "first pass"),
3328 diff(dst, newdst, "first pass", "second pass"),
3330 raise AssertionError(
3331 f"INTERNAL ERROR: Black produced different code on the second pass "
3332 f"of the formatter. "
3333 f"Please report a bug on https://github.com/ambv/black/issues. "
3334 f"This diff might be helpful: {log}"
3338 def dump_to_file(*output: str) -> str:
3339 """Dump `output` to a temporary file. Return path to the file."""
3342 with tempfile.NamedTemporaryFile(
3343 mode="w", prefix="blk_", suffix=".log", delete=False, encoding="utf8"
3345 for lines in output:
3347 if lines and lines[-1] != "\n":
3352 def diff(a: str, b: str, a_name: str, b_name: str) -> str:
3353 """Return a unified diff string between strings `a` and `b`."""
3356 a_lines = [line + "\n" for line in a.split("\n")]
3357 b_lines = [line + "\n" for line in b.split("\n")]
3359 difflib.unified_diff(a_lines, b_lines, fromfile=a_name, tofile=b_name, n=5)
3363 def cancel(tasks: Iterable[asyncio.Task]) -> None:
3364 """asyncio signal handler that cancels all `tasks` and reports to stderr."""
3370 def shutdown(loop: BaseEventLoop) -> None:
3371 """Cancel all pending tasks on `loop`, wait for them, and close the loop."""
3373 # This part is borrowed from asyncio/runners.py in Python 3.7b2.
3374 to_cancel = [task for task in asyncio.Task.all_tasks(loop) if not task.done()]
3378 for task in to_cancel:
3380 loop.run_until_complete(
3381 asyncio.gather(*to_cancel, loop=loop, return_exceptions=True)
3384 # `concurrent.futures.Future` objects cannot be cancelled once they
3385 # are already running. There might be some when the `shutdown()` happened.
3386 # Silence their logger's spew about the event loop being closed.
3387 cf_logger = logging.getLogger("concurrent.futures")
3388 cf_logger.setLevel(logging.CRITICAL)
3392 def sub_twice(regex: Pattern[str], replacement: str, original: str) -> str:
3393 """Replace `regex` with `replacement` twice on `original`.
3395 This is used by string normalization to perform replaces on
3396 overlapping matches.
3398 return regex.sub(replacement, regex.sub(replacement, original))
3401 def re_compile_maybe_verbose(regex: str) -> Pattern[str]:
3402 """Compile a regular expression string in `regex`.
3404 If it contains newlines, use verbose mode.
3407 regex = "(?x)" + regex
3408 return re.compile(regex)
3411 def enumerate_reversed(sequence: Sequence[T]) -> Iterator[Tuple[Index, T]]:
3412 """Like `reversed(enumerate(sequence))` if that were possible."""
3413 index = len(sequence) - 1
3414 for element in reversed(sequence):
3415 yield (index, element)
3419 def enumerate_with_length(
3420 line: Line, reversed: bool = False
3421 ) -> Iterator[Tuple[Index, Leaf, int]]:
3422 """Return an enumeration of leaves with their length.
3424 Stops prematurely on multiline strings and standalone comments.
3427 Callable[[Sequence[Leaf]], Iterator[Tuple[Index, Leaf]]],
3428 enumerate_reversed if reversed else enumerate,
3430 for index, leaf in op(line.leaves):
3431 length = len(leaf.prefix) + len(leaf.value)
3432 if "\n" in leaf.value:
3433 return # Multiline strings, we can't continue.
3435 comment: Optional[Leaf]
3436 for comment in line.comments_after(leaf):
3437 length += len(comment.value)
3439 yield index, leaf, length
3442 def is_line_short_enough(line: Line, *, line_length: int, line_str: str = "") -> bool:
3443 """Return True if `line` is no longer than `line_length`.
3445 Uses the provided `line_str` rendering, if any, otherwise computes a new one.
3448 line_str = str(line).strip("\n")
3450 len(line_str) <= line_length
3451 and "\n" not in line_str # multiline strings
3452 and not line.contains_standalone_comments()
3456 def can_be_split(line: Line) -> bool:
3457 """Return False if the line cannot be split *for sure*.
3459 This is not an exhaustive search but a cheap heuristic that we can use to
3460 avoid some unfortunate formattings (mostly around wrapping unsplittable code
3461 in unnecessary parentheses).
3463 leaves = line.leaves
3467 if leaves[0].type == token.STRING and leaves[1].type == token.DOT:
3471 for leaf in leaves[-2::-1]:
3472 if leaf.type in OPENING_BRACKETS:
3473 if next.type not in CLOSING_BRACKETS:
3477 elif leaf.type == token.DOT:
3479 elif leaf.type == token.NAME:
3480 if not (next.type == token.DOT or next.type in OPENING_BRACKETS):
3483 elif leaf.type not in CLOSING_BRACKETS:
3486 if dot_count > 1 and call_count > 1:
3492 def can_omit_invisible_parens(line: Line, line_length: int) -> bool:
3493 """Does `line` have a shape safe to reformat without optional parens around it?
3495 Returns True for only a subset of potentially nice looking formattings but
3496 the point is to not return false positives that end up producing lines that
3499 bt = line.bracket_tracker
3500 if not bt.delimiters:
3501 # Without delimiters the optional parentheses are useless.
3504 max_priority = bt.max_delimiter_priority()
3505 if bt.delimiter_count_with_priority(max_priority) > 1:
3506 # With more than one delimiter of a kind the optional parentheses read better.
3509 if max_priority == DOT_PRIORITY:
3510 # A single stranded method call doesn't require optional parentheses.
3513 assert len(line.leaves) >= 2, "Stranded delimiter"
3515 first = line.leaves[0]
3516 second = line.leaves[1]
3517 penultimate = line.leaves[-2]
3518 last = line.leaves[-1]
3520 # With a single delimiter, omit if the expression starts or ends with
3522 if first.type in OPENING_BRACKETS and second.type not in CLOSING_BRACKETS:
3524 length = 4 * line.depth
3525 for _index, leaf, leaf_length in enumerate_with_length(line):
3526 if leaf.type in CLOSING_BRACKETS and leaf.opening_bracket is first:
3529 length += leaf_length
3530 if length > line_length:
3533 if leaf.type in OPENING_BRACKETS:
3534 # There are brackets we can further split on.
3538 # checked the entire string and line length wasn't exceeded
3539 if len(line.leaves) == _index + 1:
3542 # Note: we are not returning False here because a line might have *both*
3543 # a leading opening bracket and a trailing closing bracket. If the
3544 # opening bracket doesn't match our rule, maybe the closing will.
3547 last.type == token.RPAR
3548 or last.type == token.RBRACE
3550 # don't use indexing for omitting optional parentheses;
3552 last.type == token.RSQB
3554 and last.parent.type != syms.trailer
3557 if penultimate.type in OPENING_BRACKETS:
3558 # Empty brackets don't help.
3561 if is_multiline_string(first):
3562 # Additional wrapping of a multiline string in this situation is
3566 length = 4 * line.depth
3567 seen_other_brackets = False
3568 for _index, leaf, leaf_length in enumerate_with_length(line):
3569 length += leaf_length
3570 if leaf is last.opening_bracket:
3571 if seen_other_brackets or length <= line_length:
3574 elif leaf.type in OPENING_BRACKETS:
3575 # There are brackets we can further split on.
3576 seen_other_brackets = True
3581 def get_cache_file(line_length: int, mode: FileMode) -> Path:
3582 return CACHE_DIR / f"cache.{line_length}.{mode.value}.pickle"
3585 def read_cache(line_length: int, mode: FileMode) -> Cache:
3586 """Read the cache if it exists and is well formed.
3588 If it is not well formed, the call to write_cache later should resolve the issue.
3590 cache_file = get_cache_file(line_length, mode)
3591 if not cache_file.exists():
3594 with cache_file.open("rb") as fobj:
3596 cache: Cache = pickle.load(fobj)
3597 except pickle.UnpicklingError:
3603 def get_cache_info(path: Path) -> CacheInfo:
3604 """Return the information used to check if a file is already formatted or not."""
3606 return stat.st_mtime, stat.st_size
3609 def filter_cached(cache: Cache, sources: Iterable[Path]) -> Tuple[Set[Path], Set[Path]]:
3610 """Split an iterable of paths in `sources` into two sets.
3612 The first contains paths of files that modified on disk or are not in the
3613 cache. The other contains paths to non-modified files.
3615 todo, done = set(), set()
3618 if cache.get(src) != get_cache_info(src):
3626 cache: Cache, sources: Iterable[Path], line_length: int, mode: FileMode
3628 """Update the cache file."""
3629 cache_file = get_cache_file(line_length, mode)
3631 if not CACHE_DIR.exists():
3632 CACHE_DIR.mkdir(parents=True)
3633 new_cache = {**cache, **{src.resolve(): get_cache_info(src) for src in sources}}
3634 with cache_file.open("wb") as fobj:
3635 pickle.dump(new_cache, fobj, protocol=pickle.HIGHEST_PROTOCOL)
3640 def patch_click() -> None:
3641 """Make Click not crash.
3643 On certain misconfigured environments, Python 3 selects the ASCII encoding as the
3644 default which restricts paths that it can access during the lifetime of the
3645 application. Click refuses to work in this scenario by raising a RuntimeError.
3647 In case of Black the likelihood that non-ASCII characters are going to be used in
3648 file paths is minimal since it's Python source code. Moreover, this crash was
3649 spurious on Python 3.7 thanks to PEP 538 and PEP 540.
3652 from click import core
3653 from click import _unicodefun # type: ignore
3654 except ModuleNotFoundError:
3657 for module in (core, _unicodefun):
3658 if hasattr(module, "_verify_python3_env"):
3659 module._verify_python3_env = lambda: None
3662 def patched_main() -> None:
3668 if __name__ == "__main__":