X-Git-Url: https://git.madduck.net/etc/vim.git/blobdiff_plain/e818260f1a223929d8f2056f484b73eaa8687622..8ebbd268880f15834b70910a6dc61e1ee7596b7c:/black.py?ds=inline diff --git a/black.py b/black.py index 7dc6ef8..4599bdd 100644 --- a/black.py +++ b/black.py @@ -2,7 +2,7 @@ import asyncio import pickle from asyncio.base_events import BaseEventLoop from concurrent.futures import Executor, ProcessPoolExecutor -from enum import Enum +from enum import Enum, Flag from functools import partial, wraps import keyword import logging @@ -44,7 +44,7 @@ from blib2to3.pgen2 import driver, token from blib2to3.pgen2.parse import ParseError -__version__ = "18.5b0" +__version__ = "18.5b1" DEFAULT_LINE_LENGTH = 88 CACHE_DIR = Path(user_cache_dir("black", version=__version__)) @@ -122,6 +122,13 @@ class Changed(Enum): YES = 2 +class FileMode(Flag): + AUTO_DETECT = 0 + PYTHON36 = 1 + PYI = 2 + NO_STRING_NORMALIZATION = 4 + + @click.command() @click.option( "-l", @@ -176,6 +183,12 @@ class Changed(Enum): "**kwargs. [default: per-file auto-detection]" ), ) +@click.option( + "-S", + "--skip-string-normalization", + is_flag=True, + help="Don't normalize string quotes or prefixes.", +) @click.version_option(version=__version__) @click.argument( "src", @@ -193,6 +206,7 @@ def main( fast: bool, pyi: bool, py36: bool, + skip_string_normalization: bool, quiet: bool, src: List[str], ) -> None: @@ -216,6 +230,13 @@ def main( write_back = WriteBack.DIFF else: write_back = WriteBack.YES + mode = FileMode.AUTO_DETECT + if py36: + mode |= FileMode.PYTHON36 + if pyi: + mode |= FileMode.PYI + if skip_string_normalization: + mode |= FileMode.NO_STRING_NORMALIZATION report = Report(check=check, quiet=quiet) if len(sources) == 0: out("No paths given. Nothing to do 😴") @@ -227,9 +248,8 @@ def main( src=sources[0], line_length=line_length, fast=fast, - pyi=pyi, - py36=py36, write_back=write_back, + mode=mode, report=report, ) else: @@ -241,9 +261,8 @@ def main( sources=sources, line_length=line_length, fast=fast, - pyi=pyi, - py36=py36, write_back=write_back, + mode=mode, report=report, loop=loop, executor=executor, @@ -261,9 +280,8 @@ def reformat_one( src: Path, line_length: int, fast: bool, - pyi: bool, - py36: bool, write_back: WriteBack, + mode: FileMode, report: "Report", ) -> None: """Reformat a single file under `src` without spawning child processes. @@ -276,17 +294,13 @@ def reformat_one( changed = Changed.NO if not src.is_file() and str(src) == "-": if format_stdin_to_stdout( - line_length=line_length, - fast=fast, - is_pyi=pyi, - force_py36=py36, - write_back=write_back, + line_length=line_length, fast=fast, write_back=write_back, mode=mode ): changed = Changed.YES else: cache: Cache = {} if write_back != WriteBack.DIFF: - cache = read_cache(line_length, pyi, py36) + cache = read_cache(line_length, mode) src = src.resolve() if src in cache and cache[src] == get_cache_info(src): changed = Changed.CACHED @@ -294,13 +308,12 @@ def reformat_one( src, line_length=line_length, fast=fast, - force_pyi=pyi, - force_py36=py36, write_back=write_back, + mode=mode, ): changed = Changed.YES if write_back == WriteBack.YES and changed is not Changed.NO: - write_cache(cache, [src], line_length, pyi, py36) + write_cache(cache, [src], line_length, mode) report.done(src, changed) except Exception as exc: report.failed(src, str(exc)) @@ -310,9 +323,8 @@ async def schedule_formatting( sources: List[Path], line_length: int, fast: bool, - pyi: bool, - py36: bool, write_back: WriteBack, + mode: FileMode, report: "Report", loop: BaseEventLoop, executor: Executor, @@ -326,7 +338,7 @@ async def schedule_formatting( """ cache: Cache = {} if write_back != WriteBack.DIFF: - cache = read_cache(line_length, pyi, py36) + cache = read_cache(line_length, mode) sources, cached = filter_cached(cache, sources) for src in cached: report.done(src, Changed.CACHED) @@ -346,9 +358,8 @@ async def schedule_formatting( src, line_length, fast, - pyi, - py36, write_back, + mode, lock, ): src for src in sorted(sources) @@ -374,16 +385,15 @@ async def schedule_formatting( if cancelled: await asyncio.gather(*cancelled, loop=loop, return_exceptions=True) if write_back == WriteBack.YES and formatted: - write_cache(cache, formatted, line_length, pyi, py36) + write_cache(cache, formatted, line_length, mode) def format_file_in_place( src: Path, line_length: int, fast: bool, - force_pyi: bool = False, - force_py36: bool = False, write_back: WriteBack = WriteBack.NO, + mode: FileMode = FileMode.AUTO_DETECT, lock: Any = None, # multiprocessing.Manager().Lock() is some crazy proxy ) -> bool: """Format file under `src` path. Return True if changed. @@ -391,17 +401,13 @@ def format_file_in_place( If `write_back` is True, write reformatted code back to stdout. `line_length` and `fast` options are passed to :func:`format_file_contents`. """ - is_pyi = force_pyi or src.suffix == ".pyi" - + if src.suffix == ".pyi": + mode |= FileMode.PYI with tokenize.open(src) as src_buffer: src_contents = src_buffer.read() try: dst_contents = format_file_contents( - src_contents, - line_length=line_length, - fast=fast, - is_pyi=is_pyi, - force_py36=force_py36, + src_contents, line_length=line_length, fast=fast, mode=mode ) except NothingChanged: return False @@ -426,9 +432,8 @@ def format_file_in_place( def format_stdin_to_stdout( line_length: int, fast: bool, - is_pyi: bool = False, - force_py36: bool = False, write_back: WriteBack = WriteBack.NO, + mode: FileMode = FileMode.AUTO_DETECT, ) -> bool: """Format file on stdin. Return True if changed. @@ -439,13 +444,7 @@ def format_stdin_to_stdout( src = sys.stdin.read() dst = src try: - dst = format_file_contents( - src, - line_length=line_length, - fast=fast, - is_pyi=is_pyi, - force_py36=force_py36, - ) + dst = format_file_contents(src, line_length=line_length, fast=fast, mode=mode) return True except NothingChanged: @@ -465,8 +464,7 @@ def format_file_contents( *, line_length: int, fast: bool, - is_pyi: bool = False, - force_py36: bool = False, + mode: FileMode = FileMode.AUTO_DETECT, ) -> FileContent: """Reformat contents a file and return new contents. @@ -477,30 +475,18 @@ def format_file_contents( if src_contents.strip() == "": raise NothingChanged - dst_contents = format_str( - src_contents, line_length=line_length, is_pyi=is_pyi, force_py36=force_py36 - ) + dst_contents = format_str(src_contents, line_length=line_length, mode=mode) if src_contents == dst_contents: raise NothingChanged if not fast: assert_equivalent(src_contents, dst_contents) - assert_stable( - src_contents, - dst_contents, - line_length=line_length, - is_pyi=is_pyi, - force_py36=force_py36, - ) + assert_stable(src_contents, dst_contents, line_length=line_length, mode=mode) return dst_contents def format_str( - src_contents: str, - line_length: int, - *, - is_pyi: bool = False, - force_py36: bool = False, + src_contents: str, line_length: int, *, mode: FileMode = FileMode.AUTO_DETECT ) -> FileContent: """Reformat a string and return new contents. @@ -509,11 +495,15 @@ def format_str( src_node = lib2to3_parse(src_contents) dst_contents = "" future_imports = get_future_imports(src_node) - elt = EmptyLineTracker(is_pyi=is_pyi) - py36 = force_py36 or is_python36(src_node) + is_pyi = bool(mode & FileMode.PYI) + py36 = bool(mode & FileMode.PYTHON36) or is_python36(src_node) + normalize_strings = not bool(mode & FileMode.NO_STRING_NORMALIZATION) lines = LineGenerator( - remove_u_prefix=py36 or "unicode_literals" in future_imports, is_pyi=is_pyi + remove_u_prefix=py36 or "unicode_literals" in future_imports, + is_pyi=is_pyi, + normalize_strings=normalize_strings, ) + elt = EmptyLineTracker(is_pyi=is_pyi) empty_line = Line() after = 0 for current_line in lines.visit(src_node): @@ -989,14 +979,11 @@ class Line: @property def is_triple_quoted_string(self) -> bool: - """Is the line a triple quoted docstring?""" + """Is the line a triple quoted string?""" return ( bool(self) and self.leaves[0].type == token.STRING - and ( - self.leaves[0].value.startswith('"""') - or self.leaves[0].value.startswith("'''") - ) + and self.leaves[0].value.startswith(('"""', "'''")) ) def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool: @@ -1257,9 +1244,8 @@ class EmptyLineTracker: if self.previous_line.is_decorator: return 0, 0 - if ( - self.previous_line.is_class - and self.previous_line.depth != current_line.depth + if self.previous_line.depth < current_line.depth and ( + self.previous_line.is_class or self.previous_line.is_def ): return 0, 0 @@ -1313,6 +1299,7 @@ class LineGenerator(Visitor[Line]): """ is_pyi: bool = False + normalize_strings: bool = True current_line: Line = Factory(Line) remove_u_prefix: bool = False @@ -1381,7 +1368,7 @@ class LineGenerator(Visitor[Line]): else: normalize_prefix(node, inside_brackets=any_open_brackets) - if node.type == token.STRING: + if self.normalize_strings and node.type == token.STRING: normalize_string_prefix(node, remove_u_prefix=self.remove_u_prefix) normalize_string_quotes(node) if node.type not in WHITESPACE: @@ -2936,12 +2923,10 @@ def assert_equivalent(src: str, dst: str) -> None: def assert_stable( - src: str, dst: str, line_length: int, is_pyi: bool = False, force_py36: bool = False + src: str, dst: str, line_length: int, mode: FileMode = FileMode.AUTO_DETECT ) -> None: """Raise AssertionError if `dst` reformats differently the second time.""" - newdst = format_str( - dst, line_length=line_length, is_pyi=is_pyi, force_py36=force_py36 - ) + newdst = format_str(dst, line_length=line_length, mode=mode) if dst != newdst: log = dump_to_file( diff(src, dst, "source", "first pass"), @@ -3152,19 +3137,21 @@ def can_omit_invisible_parens(line: Line, line_length: int) -> bool: return False -def get_cache_file(line_length: int, pyi: bool = False, py36: bool = False) -> Path: +def get_cache_file(line_length: int, mode: FileMode) -> Path: + pyi = bool(mode & FileMode.PYI) + py36 = bool(mode & FileMode.PYTHON36) return ( CACHE_DIR / f"cache.{line_length}{'.pyi' if pyi else ''}{'.py36' if py36 else ''}.pickle" ) -def read_cache(line_length: int, pyi: bool = False, py36: bool = False) -> Cache: +def read_cache(line_length: int, mode: FileMode) -> Cache: """Read the cache if it exists and is well formed. If it is not well formed, the call to write_cache later should resolve the issue. """ - cache_file = get_cache_file(line_length, pyi, py36) + cache_file = get_cache_file(line_length, mode) if not cache_file.exists(): return {} @@ -3202,14 +3189,10 @@ def filter_cached( def write_cache( - cache: Cache, - sources: List[Path], - line_length: int, - pyi: bool = False, - py36: bool = False, + cache: Cache, sources: List[Path], line_length: int, mode: FileMode ) -> None: """Update the cache file.""" - cache_file = get_cache_file(line_length, pyi, py36) + cache_file = get_cache_file(line_length, mode) try: if not CACHE_DIR.exists(): CACHE_DIR.mkdir(parents=True)