Add diff support to blackd (#969)
This commit is contained in:
parent
b65af236cf
commit
df6e1a41f7
@ -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:
|
||||
|
||||
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
|
||||
@ -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`.
|
||||
- `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.
|
||||
@ -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` 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
|
||||
|
18
blackd.py
18
blackd.py
@ -1,5 +1,6 @@
|
||||
import asyncio
|
||||
from concurrent.futures import Executor, ProcessPoolExecutor
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
import logging
|
||||
from multiprocessing import freeze_support
|
||||
@ -21,6 +22,7 @@
|
||||
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,
|
||||
@ -28,6 +30,7 @@
|
||||
PYTHON_VARIANT_HEADER,
|
||||
SKIP_STRING_NORMALIZATION_HEADER,
|
||||
FAST_OR_SAFE_HEADER,
|
||||
DIFF_HEADER,
|
||||
]
|
||||
|
||||
# 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)
|
||||
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,
|
||||
|
14
tests/data/blackd_diff.diff
Normal file
14
tests/data/blackd_diff.diff
Normal file
@ -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")
|
||||
+
|
6
tests/data/blackd_diff.py
Normal file
6
tests/data/blackd_diff.py
Normal file
@ -0,0 +1,6 @@
|
||||
def abc ():
|
||||
return ["hello", "world",
|
||||
"!"]
|
||||
|
||||
print( "Incorrect formatting"
|
||||
)
|
@ -35,6 +35,7 @@
|
||||
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
|
||||
@ -259,7 +260,7 @@ def test_piping_diff(self) -> None:
|
||||
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)
|
||||
|
||||
@ -340,7 +341,7 @@ def test_expression_diff(self) -> None:
|
||||
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)
|
||||
@ -1689,6 +1690,27 @@ async def test_blackd_pyi(self) -> None:
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user