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
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.
- `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
import asyncio
from concurrent.futures import Executor, ProcessPoolExecutor
+from datetime import datetime
from functools import partial
import logging
from multiprocessing import freeze_support
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,
PYTHON_VARIANT_HEADER,
SKIP_STRING_NORMALIZATION_HEADER,
FAST_OR_SAFE_HEADER,
+ DIFF_HEADER,
]
# Response headers
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)
)
+
+ # 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,
--- /dev/null
+--- [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
--- /dev/null
+def abc ():
+ return ["hello", "world",
+ "!"]
+
+print( "Incorrect formatting"
+)
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
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)
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)
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