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

madduck's git repository

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

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

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

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

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

acks += asottile
[etc/vim.git] / black.py
index 6bb29feea43c58e5e6afb533fe7676d17cbbc083..4995b53d51b0b4b4cc4270b9595a22abd94ead17 100644 (file)
--- a/black.py
+++ b/black.py
@@ -129,6 +129,15 @@ class WriteBack(Enum):
     is_flag=True,
     help="If --fast given, skip temporary sanity checks. [default: --safe]",
 )
     is_flag=True,
     help="If --fast given, skip temporary sanity checks. [default: --safe]",
 )
+@click.option(
+    "-q",
+    "--quiet",
+    is_flag=True,
+    help=(
+        "Don't emit non-error messages to stderr. Errors are still emitted, "
+        "silence those with 2>/dev/null."
+    ),
+)
 @click.version_option(version=__version__)
 @click.argument(
     "src",
 @click.version_option(version=__version__)
 @click.argument(
     "src",
@@ -144,6 +153,7 @@ def main(
     check: bool,
     diff: bool,
     fast: bool,
     check: bool,
     diff: bool,
     fast: bool,
+    quiet: bool,
     src: List[str],
 ) -> None:
     """The uncompromising code formatter."""
     src: List[str],
 ) -> None:
     """The uncompromising code formatter."""
@@ -174,7 +184,7 @@ def main(
         ctx.exit(0)
     elif len(sources) == 1:
         p = sources[0]
         ctx.exit(0)
     elif len(sources) == 1:
         p = sources[0]
-        report = Report(check=check)
+        report = Report(check=check, quiet=quiet)
         try:
             if not p.is_file() and str(p) == "-":
                 changed = format_stdin_to_stdout(
         try:
             if not p.is_file() and str(p) == "-":
                 changed = format_stdin_to_stdout(
@@ -195,7 +205,7 @@ def main(
         try:
             return_code = loop.run_until_complete(
                 schedule_formatting(
         try:
             return_code = loop.run_until_complete(
                 schedule_formatting(
-                    sources, line_length, write_back, fast, loop, executor
+                    sources, line_length, write_back, fast, quiet, loop, executor
                 )
             )
         finally:
                 )
             )
         finally:
@@ -208,6 +218,7 @@ async def schedule_formatting(
     line_length: int,
     write_back: WriteBack,
     fast: bool,
     line_length: int,
     write_back: WriteBack,
     fast: bool,
+    quiet: bool,
     loop: BaseEventLoop,
     executor: Executor,
 ) -> int:
     loop: BaseEventLoop,
     executor: Executor,
 ) -> int:
@@ -235,7 +246,7 @@ async def schedule_formatting(
     loop.add_signal_handler(signal.SIGTERM, cancel, _task_values)
     await asyncio.wait(tasks.values())
     cancelled = []
     loop.add_signal_handler(signal.SIGTERM, cancel, _task_values)
     await asyncio.wait(tasks.values())
     cancelled = []
-    report = Report(check=not write_back)
+    report = Report(check=write_back is WriteBack.NO, quiet=quiet)
     for src, task in tasks.items():
         if not task.done():
             report.failed(src, "timed out, cancelling")
     for src, task in tasks.items():
         if not task.done():
             report.failed(src, "timed out, cancelling")
@@ -249,9 +260,10 @@ async def schedule_formatting(
             report.done(src, task.result())
     if cancelled:
         await asyncio.gather(*cancelled, loop=loop, return_exceptions=True)
             report.done(src, task.result())
     if cancelled:
         await asyncio.gather(*cancelled, loop=loop, return_exceptions=True)
-    else:
+    elif not quiet:
         out("All done! ✨ 🍰 ✨")
         out("All done! ✨ 🍰 ✨")
-    click.echo(str(report))
+    if not quiet:
+        click.echo(str(report))
     return report.return_code
 
 
     return report.return_code
 
 
@@ -1905,10 +1917,20 @@ def normalize_string_quotes(leaf: Leaf) -> None:
     if first_quote_pos == -1:
         return  # There's an internal error
 
     if first_quote_pos == -1:
         return  # There's an internal error
 
+    prefix = leaf.value[:first_quote_pos]
     body = leaf.value[first_quote_pos + len(orig_quote):-len(orig_quote)]
     body = leaf.value[first_quote_pos + len(orig_quote):-len(orig_quote)]
-    new_body = body.replace(f"\\{orig_quote}", orig_quote).replace(
-        new_quote, f"\\{new_quote}"
-    )
+    if "r" in prefix.casefold():
+        if body.count(new_quote) != body.count(f"\\{new_quote}"):
+            # There's at least one unescaped new_quote in this raw string
+            # so converting is impossible
+            return
+
+        # Do not introduce or remove backslashes in raw strings
+        new_body = body
+    else:
+        new_body = body.replace(f"\\{orig_quote}", orig_quote).replace(
+            new_quote, f"\\{new_quote}"
+        )
     if new_quote == '"""' and new_body[-1] == '"':
         # edge case:
         new_body = new_body[:-1] + '\\"'
     if new_quote == '"""' and new_body[-1] == '"':
         # edge case:
         new_body = new_body[:-1] + '\\"'
@@ -1920,7 +1942,6 @@ def normalize_string_quotes(leaf: Leaf) -> None:
     if new_escape_count == orig_escape_count and orig_quote == '"':
         return  # Prefer double quotes
 
     if new_escape_count == orig_escape_count and orig_quote == '"':
         return  # Prefer double quotes
 
-    prefix = leaf.value[:first_quote_pos]
     leaf.value = f"{prefix}{new_quote}{new_body}{new_quote}"
 
 
     leaf.value = f"{prefix}{new_quote}{new_body}{new_quote}"
 
 
@@ -1974,6 +1995,7 @@ def gen_python_files_in_dir(path: Path) -> Iterator[Path]:
 class Report:
     """Provides a reformatting counter. Can be rendered with `str(report)`."""
     check: bool = False
 class Report:
     """Provides a reformatting counter. Can be rendered with `str(report)`."""
     check: bool = False
+    quiet: bool = False
     change_count: int = 0
     same_count: int = 0
     failure_count: int = 0
     change_count: int = 0
     same_count: int = 0
     failure_count: int = 0
@@ -1982,10 +2004,12 @@ class Report:
         """Increment the counter for successful reformatting. Write out a message."""
         if changed:
             reformatted = "would reformat" if self.check else "reformatted"
         """Increment the counter for successful reformatting. Write out a message."""
         if changed:
             reformatted = "would reformat" if self.check else "reformatted"
-            out(f"{reformatted} {src}")
+            if not self.quiet:
+                out(f"{reformatted} {src}")
             self.change_count += 1
         else:
             self.change_count += 1
         else:
-            out(f"{src} already well formatted, good job.", bold=False)
+            if not self.quiet:
+                out(f"{src} already well formatted, good job.", bold=False)
             self.same_count += 1
 
     def failed(self, src: Path, message: str) -> None:
             self.same_count += 1
 
     def failed(self, src: Path, message: str) -> None: