X-Git-Url: https://git.madduck.net/etc/vim.git/blobdiff_plain/cc48bc56caa79b0d605e80302c32be0457cfde39..0bf683a65929cb2153ed5e6d881ab6ed21e49e8c:/black.py diff --git a/black.py b/black.py index 7edf2ae..d688748 100644 --- a/black.py +++ b/black.py @@ -94,11 +94,12 @@ class WriteBack(Enum): NO = 0 YES = 1 DIFF = 2 + CHECK = 3 @classmethod def from_configuration(cls, *, check: bool, diff: bool) -> "WriteBack": if check and not diff: - return cls.NO + return cls.CHECK return cls.DIFF if diff else cls.YES @@ -398,7 +399,9 @@ def reformat_one( mode=mode, ): changed = Changed.YES - if write_back == WriteBack.YES and changed is not Changed.NO: + if (write_back is WriteBack.YES and changed is not Changed.CACHED) or ( + write_back is WriteBack.CHECK and changed is Changed.NO + ): write_cache(cache, [src], line_length, mode) report.done(src, changed) except Exception as exc: @@ -428,50 +431,58 @@ async def schedule_formatting( sources, cached = filter_cached(cache, sources) for src in sorted(cached): report.done(src, Changed.CACHED) + if not sources: + return + cancelled = [] - formatted = [] - if sources: - lock = None - if write_back == WriteBack.DIFF: - # For diff output, we need locks to ensure we don't interleave output - # from different processes. - manager = Manager() - lock = manager.Lock() - tasks = { - loop.run_in_executor( - executor, - format_file_in_place, - src, - line_length, - fast, - write_back, - mode, - lock, - ): src - for src in sorted(sources) - } - pending: Iterable[asyncio.Task] = tasks.keys() - try: - loop.add_signal_handler(signal.SIGINT, cancel, pending) - loop.add_signal_handler(signal.SIGTERM, cancel, pending) - except NotImplementedError: - # There are no good alternatives for these on Windows - pass - while pending: - done, _ = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED) - for task in done: - src = tasks.pop(task) - if task.cancelled(): - cancelled.append(task) - elif task.exception(): - report.failed(src, str(task.exception())) - else: - formatted.append(src) - report.done(src, Changed.YES if task.result() else Changed.NO) + sources_to_cache = [] + lock = None + if write_back == WriteBack.DIFF: + # For diff output, we need locks to ensure we don't interleave output + # from different processes. + manager = Manager() + lock = manager.Lock() + tasks = { + loop.run_in_executor( + executor, + format_file_in_place, + src, + line_length, + fast, + write_back, + mode, + lock, + ): src + for src in sorted(sources) + } + pending: Iterable[asyncio.Task] = tasks.keys() + try: + loop.add_signal_handler(signal.SIGINT, cancel, pending) + loop.add_signal_handler(signal.SIGTERM, cancel, pending) + except NotImplementedError: + # There are no good alternatives for these on Windows. + pass + while pending: + done, _ = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED) + for task in done: + src = tasks.pop(task) + if task.cancelled(): + cancelled.append(task) + elif task.exception(): + report.failed(src, str(task.exception())) + else: + changed = Changed.YES if task.result() else Changed.NO + # If the file was written back or was successfully checked as + # well-formatted, store this information in the cache. + if write_back is WriteBack.YES or ( + write_back is WriteBack.CHECK and changed is Changed.NO + ): + sources_to_cache.append(src) + report.done(src, changed) if cancelled: await asyncio.gather(*cancelled, loop=loop, return_exceptions=True) - if write_back == WriteBack.YES and formatted: - write_cache(cache, formatted, line_length, mode) + if sources_to_cache: + write_cache(cache, sources_to_cache, line_length, mode) def format_file_in_place( @@ -484,7 +495,8 @@ def format_file_in_place( ) -> bool: """Format file under `src` path. Return True if changed. - If `write_back` is True, write reformatted code back to stdout. + If `write_back` is DIFF, write a diff to stdout. If it is YES, write reformatted + code to the file. `line_length` and `fast` options are passed to :func:`format_file_contents`. """ if src.suffix == ".pyi": @@ -533,7 +545,8 @@ def format_stdin_to_stdout( ) -> bool: """Format file on stdin. Return True if changed. - If `write_back` is True, write reformatted code back to stdout. + If `write_back` is YES, write reformatted code back to stdout. If it is DIFF, + write a diff to stdout. `line_length`, `fast`, `is_pyi`, and `force_py36` arguments are passed to :func:`format_file_contents`. """ @@ -2498,28 +2511,27 @@ def normalize_string_quotes(leaf: Leaf) -> None: def normalize_numeric_literal(leaf: Leaf, allow_underscores: bool) -> None: - """Normalizes numeric (float, int, and complex) literals.""" - # We want all letters (e in exponents, j in complex literals, a-f - # in hex literals) to be lowercase. + """Normalizes numeric (float, int, and complex) literals. + + All letters used in the representation are normalized to lowercase, long number + literals are split using underscores. + """ text = leaf.value.lower() if text.startswith(("0o", "0x", "0b")): - # Leave octal, hex, and binary literals alone for now. + # Leave octal, hex, and binary literals alone. pass elif "e" in text: before, after = text.split("e") + sign = "" if after.startswith("-"): after = after[1:] sign = "-" elif after.startswith("+"): after = after[1:] - sign = "" - else: - sign = "" before = format_float_or_int_string(before, allow_underscores) after = format_int_string(after, allow_underscores) text = f"{before}e{sign}{after}" - # Complex numbers and Python 2 longs - elif "j" in text or "l" in text: + elif text.endswith(("j", "l")): number = text[:-1] suffix = text[-1] text = f"{format_float_or_int_string(number, allow_underscores)}{suffix}" @@ -2532,6 +2544,7 @@ def format_float_or_int_string(text: str, allow_underscores: bool) -> str: """Formats a float string like "1.0".""" if "." not in text: return format_int_string(text, allow_underscores) + before, after = text.split(".") before = format_int_string(before, allow_underscores) if before else "0" after = format_int_string(after, allow_underscores) if after else "0" @@ -2541,14 +2554,16 @@ def format_float_or_int_string(text: str, allow_underscores: bool) -> str: def format_int_string(text: str, allow_underscores: bool) -> str: """Normalizes underscores in a string to e.g. 1_000_000. - Input must be a string consisting only of digits and underscores. + Input must be a string of at least six digits and optional underscores. """ if not allow_underscores: return text + text = text.replace("_", "") if len(text) <= 6: # No underscores for numbers <= 6 digits long. return text + return format(int(text), "3_")