]> git.madduck.net Git - etc/vim.git/commitdiff

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:

Add diff support to blackd (#969)
authorJoe Antonakakis <jma353@cornell.edu>
Mon, 28 Oct 2019 13:25:26 +0000 (06:25 -0700)
committerŁukasz Langa <lukasz@langa.pl>
Mon, 28 Oct 2019 13:25:26 +0000 (14:25 +0100)
README.md
blackd.py
tests/data/blackd_diff.diff [new file with mode: 0644]
tests/data/blackd_diff.py [new file with mode: 0644]
tests/test_black.py

index 8dceeacc2d205d2a2e65679073854cfab1cf4b5b..8ce310341f8d041163e77d8328f9105555eacc05 100644 (file)
--- a/README.md
+++ b/README.md
@@ -838,6 +838,9 @@ which if present, should have the value `1`, otherwise the request is rejected w
 
 The headers controlling how code is formatted are:
 
 
 The headers controlling how code is formatted are:
 
+If any of these headers are set to invalid values, `blackd` returns a `HTTP 400` error
+response, mentioning the name of the problematic header in the message body.
+
 - `X-Line-Length`: corresponds to the `--line-length` command line flag.
 - `X-Skip-String-Normalization`: corresponds to the `--skip-string-normalization`
   command line flag. If present and its value is not the empty string, no string
 - `X-Line-Length`: corresponds to the `--line-length` command line flag.
 - `X-Skip-String-Normalization`: corresponds to the `--skip-string-normalization`
   command line flag. If present and its value is not the empty string, no string
@@ -849,6 +852,8 @@ The headers controlling how code is formatted are:
   a set of comma-separated Python versions, optionally prefixed with `py`. For example,
   to request code that is compatible with Python 3.5 and 3.6, set the header to
   `py3.5,py3.6`.
   a set of comma-separated Python versions, optionally prefixed with `py`. For example,
   to request code that is compatible with Python 3.5 and 3.6, set the header to
   `py3.5,py3.6`.
+- `X-Diff`: corresponds to the `--diff` command line flag. If present, a diff of the
+  formats will be output.
 
 If any of these headers are set to invalid values, `blackd` returns a `HTTP 400` error
 response, mentioning the name of the problematic header in the message body.
 
 If any of these headers are set to invalid values, `blackd` returns a `HTTP 400` error
 response, mentioning the name of the problematic header in the message body.
@@ -1034,6 +1039,9 @@ More details can be found in [CONTRIBUTING](CONTRIBUTING.md).
 
 - `blackd` now returns the version of _Black_ in the response headers (#1013)
 
 
 - `blackd` now returns the version of _Black_ in the response headers (#1013)
 
+- `blackd` can now output the diff of formats on source code when the `X-Diff` header is
+  provided (#969)
+
 ### 19.3b0
 
 - new option `--target-version` to control which Python versions _Black_-formatted code
 ### 19.3b0
 
 - new option `--target-version` to control which Python versions _Black_-formatted code
index a6c6e8db9c04d920854a17dbc8a6962eb97abead..d79bfe75bc2f8c7b419668e8cf10f28e432362c9 100644 (file)
--- a/blackd.py
+++ b/blackd.py
@@ -1,5 +1,6 @@
 import asyncio
 from concurrent.futures import Executor, ProcessPoolExecutor
 import asyncio
 from concurrent.futures import Executor, ProcessPoolExecutor
+from datetime import datetime
 from functools import partial
 import logging
 from multiprocessing import freeze_support
 from functools import partial
 import logging
 from multiprocessing import freeze_support
@@ -21,6 +22,7 @@ LINE_LENGTH_HEADER = "X-Line-Length"
 PYTHON_VARIANT_HEADER = "X-Python-Variant"
 SKIP_STRING_NORMALIZATION_HEADER = "X-Skip-String-Normalization"
 FAST_OR_SAFE_HEADER = "X-Fast-Or-Safe"
 PYTHON_VARIANT_HEADER = "X-Python-Variant"
 SKIP_STRING_NORMALIZATION_HEADER = "X-Skip-String-Normalization"
 FAST_OR_SAFE_HEADER = "X-Fast-Or-Safe"
+DIFF_HEADER = "X-Diff"
 
 BLACK_HEADERS = [
     PROTOCOL_VERSION_HEADER,
 
 BLACK_HEADERS = [
     PROTOCOL_VERSION_HEADER,
@@ -28,6 +30,7 @@ BLACK_HEADERS = [
     PYTHON_VARIANT_HEADER,
     SKIP_STRING_NORMALIZATION_HEADER,
     FAST_OR_SAFE_HEADER,
     PYTHON_VARIANT_HEADER,
     SKIP_STRING_NORMALIZATION_HEADER,
     FAST_OR_SAFE_HEADER,
+    DIFF_HEADER,
 ]
 
 # Response headers
 ]
 
 # Response headers
@@ -112,10 +115,25 @@ async def handle(request: web.Request, executor: Executor) -> web.Response:
         req_bytes = await request.content.read()
         charset = request.charset if request.charset is not None else "utf8"
         req_str = req_bytes.decode(charset)
         req_bytes = await request.content.read()
         charset = request.charset if request.charset is not None else "utf8"
         req_str = req_bytes.decode(charset)
+        then = datetime.utcnow()
+
         loop = asyncio.get_event_loop()
         formatted_str = await loop.run_in_executor(
             executor, partial(black.format_file_contents, req_str, fast=fast, mode=mode)
         )
         loop = asyncio.get_event_loop()
         formatted_str = await loop.run_in_executor(
             executor, partial(black.format_file_contents, req_str, fast=fast, mode=mode)
         )
+
+        # Only output the diff in the HTTP response
+        only_diff = bool(request.headers.get(DIFF_HEADER, False))
+        if only_diff:
+            now = datetime.utcnow()
+            src_name = f"In\t{then} +0000"
+            dst_name = f"Out\t{now} +0000"
+            loop = asyncio.get_event_loop()
+            formatted_str = await loop.run_in_executor(
+                executor,
+                partial(black.diff, req_str, formatted_str, src_name, dst_name),
+            )
+
         return web.Response(
             content_type=request.content_type,
             charset=charset,
         return web.Response(
             content_type=request.content_type,
             charset=charset,
diff --git a/tests/data/blackd_diff.diff b/tests/data/blackd_diff.diff
new file mode 100644 (file)
index 0000000..c1aa52e
--- /dev/null
@@ -0,0 +1,14 @@
+--- [Deterministic header]
++++ [Deterministic header]
+@@ -1,7 +1,6 @@
+-def abc ():
+-    return ["hello", "world",
+-            "!"]
++def abc():
++    return ["hello", "world", "!"]
+-print(   "Incorrect formatting"    
+-)
++print("Incorrect formatting")
++
\ No newline at end of file
diff --git a/tests/data/blackd_diff.py b/tests/data/blackd_diff.py
new file mode 100644 (file)
index 0000000..c527832
--- /dev/null
@@ -0,0 +1,6 @@
+def abc ():
+    return ["hello", "world",
+            "!"]
+
+print(   "Incorrect formatting"    
+)
index e6a6647fc872a1242cddbe8f6f38e7c1daa3ca6a..d4c88d514265620714cc6b0615a0f3688545507c 100644 (file)
@@ -35,6 +35,7 @@ ff = partial(black.format_file_in_place, mode=black.FileMode(), fast=True)
 fs = partial(black.format_str, mode=black.FileMode())
 THIS_FILE = Path(__file__)
 THIS_DIR = THIS_FILE.parent
 fs = partial(black.format_str, mode=black.FileMode())
 THIS_FILE = Path(__file__)
 THIS_DIR = THIS_FILE.parent
+DETERMINISTIC_HEADER = "[Deterministic header]"
 EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
 PY36_ARGS = [
     f"--target-version={version.name.lower()}" for version in black.PY36_VERSIONS
 EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
 PY36_ARGS = [
     f"--target-version={version.name.lower()}" for version in black.PY36_VERSIONS
@@ -259,7 +260,7 @@ class BlackTestCase(unittest.TestCase):
             black.main, args, input=BytesIO(source.encode("utf8"))
         )
         self.assertEqual(result.exit_code, 0)
             black.main, args, input=BytesIO(source.encode("utf8"))
         )
         self.assertEqual(result.exit_code, 0)
-        actual = diff_header.sub("[Deterministic header]", result.output)
+        actual = diff_header.sub(DETERMINISTIC_HEADER, result.output)
         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
         self.assertEqual(expected, actual)
 
         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
         self.assertEqual(expected, actual)
 
@@ -340,7 +341,7 @@ class BlackTestCase(unittest.TestCase):
         finally:
             os.unlink(tmp_file)
         actual = result.output
         finally:
             os.unlink(tmp_file)
         actual = result.output
-        actual = diff_header.sub("[Deterministic header]", actual)
+        actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
         if expected != actual:
             dump = black.dump_to_file(actual)
         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
         if expected != actual:
             dump = black.dump_to_file(actual)
@@ -1689,6 +1690,27 @@ class BlackDTestCase(AioHTTPTestCase):
         self.assertEqual(response.status, 200)
         self.assertEqual(await response.text(), expected)
 
         self.assertEqual(response.status, 200)
         self.assertEqual(await response.text(), expected)
 
+    @skip_if_exception("ClientOSError")
+    @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+    @unittest_run_loop
+    async def test_blackd_diff(self) -> None:
+        diff_header = re.compile(
+            rf"(In|Out)\t\d\d\d\d-\d\d-\d\d "
+            rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
+        )
+
+        source, _ = read_data("blackd_diff.py")
+        expected, _ = read_data("blackd_diff.diff")
+
+        response = await self.client.post(
+            "/", data=source, headers={blackd.DIFF_HEADER: "true"}
+        )
+        self.assertEqual(response.status, 200)
+
+        actual = await response.text()
+        actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
+        self.assertEqual(actual, expected)
+
     @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
     @unittest_run_loop
     @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
     @unittest_run_loop