Add piping from stdin to stdout with a - (#25)
Being able to format code by piping it through the formatter makes it much easier to integrate with tools like google/vim-codefmt or Chiel92/vim-autoformat.
This commit is contained in:
parent
2fa31ff314
commit
10d8976a79
51
black.py
51
black.py
@ -74,7 +74,9 @@ class CannotSplit(Exception):
|
|||||||
@click.argument(
|
@click.argument(
|
||||||
'src',
|
'src',
|
||||||
nargs=-1,
|
nargs=-1,
|
||||||
type=click.Path(exists=True, file_okay=True, dir_okay=True, readable=True),
|
type=click.Path(
|
||||||
|
exists=True, file_okay=True, dir_okay=True, readable=True, allow_dash=True
|
||||||
|
),
|
||||||
)
|
)
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def main(
|
def main(
|
||||||
@ -89,6 +91,8 @@ def main(
|
|||||||
elif p.is_file():
|
elif p.is_file():
|
||||||
# if a file was explicitly given, we don't care about its extension
|
# if a file was explicitly given, we don't care about its extension
|
||||||
sources.append(p)
|
sources.append(p)
|
||||||
|
elif s == '-':
|
||||||
|
sources.append(Path('-'))
|
||||||
else:
|
else:
|
||||||
err(f'invalid path: {s}')
|
err(f'invalid path: {s}')
|
||||||
if len(sources) == 0:
|
if len(sources) == 0:
|
||||||
@ -97,9 +101,12 @@ def main(
|
|||||||
p = sources[0]
|
p = sources[0]
|
||||||
report = Report()
|
report = Report()
|
||||||
try:
|
try:
|
||||||
changed = format_file_in_place(
|
if not p.is_file() and str(p) == '-':
|
||||||
p, line_length=line_length, fast=fast, write_back=not check
|
changed = format_stdin_to_stdout(line_length=line_length, fast=fast)
|
||||||
)
|
else:
|
||||||
|
changed = format_file_in_place(
|
||||||
|
p, line_length=line_length, fast=fast, write_back=not check
|
||||||
|
)
|
||||||
report.done(p, changed)
|
report.done(p, changed)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
report.failed(p, str(exc))
|
report.failed(p, str(exc))
|
||||||
@ -156,34 +163,50 @@ def format_file_in_place(
|
|||||||
src: Path, line_length: int, fast: bool, write_back: bool = False
|
src: Path, line_length: int, fast: bool, write_back: bool = False
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Format the file and rewrite if changed. Return True if changed."""
|
"""Format the file and rewrite if changed. Return True if changed."""
|
||||||
|
with tokenize.open(src) as src_buffer:
|
||||||
|
src_contents = src_buffer.read()
|
||||||
try:
|
try:
|
||||||
contents, encoding = format_file(src, line_length=line_length, fast=fast)
|
contents = format_file_contents(
|
||||||
|
src_contents, line_length=line_length, fast=fast
|
||||||
|
)
|
||||||
except NothingChanged:
|
except NothingChanged:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if write_back:
|
if write_back:
|
||||||
with open(src, "w", encoding=encoding) as f:
|
with open(src, "w", encoding=src_buffer.encoding) as f:
|
||||||
f.write(contents)
|
f.write(contents)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def format_file(
|
def format_stdin_to_stdout(line_length: int, fast: bool) -> bool:
|
||||||
src: Path, line_length: int, fast: bool
|
"""Format file on stdin and pipe output to stdout. Return True if changed."""
|
||||||
) -> Tuple[FileContent, Encoding]:
|
contents = sys.stdin.read()
|
||||||
|
try:
|
||||||
|
contents = format_file_contents(contents, line_length=line_length, fast=fast)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except NothingChanged:
|
||||||
|
return False
|
||||||
|
|
||||||
|
finally:
|
||||||
|
sys.stdout.write(contents)
|
||||||
|
|
||||||
|
|
||||||
|
def format_file_contents(
|
||||||
|
src_contents: str, line_length: int, fast: bool
|
||||||
|
) -> FileContent:
|
||||||
"""Reformats a file and returns its contents and encoding."""
|
"""Reformats a file and returns its contents and encoding."""
|
||||||
with tokenize.open(src) as src_buffer:
|
|
||||||
src_contents = src_buffer.read()
|
|
||||||
if src_contents.strip() == '':
|
if src_contents.strip() == '':
|
||||||
raise NothingChanged(src)
|
raise NothingChanged
|
||||||
|
|
||||||
dst_contents = format_str(src_contents, line_length=line_length)
|
dst_contents = format_str(src_contents, line_length=line_length)
|
||||||
if src_contents == dst_contents:
|
if src_contents == dst_contents:
|
||||||
raise NothingChanged(src)
|
raise NothingChanged
|
||||||
|
|
||||||
if not fast:
|
if not fast:
|
||||||
assert_equivalent(src_contents, dst_contents)
|
assert_equivalent(src_contents, dst_contents)
|
||||||
assert_stable(src_contents, dst_contents, line_length=line_length)
|
assert_stable(src_contents, dst_contents, line_length=line_length)
|
||||||
return dst_contents, src_buffer.encoding
|
return dst_contents
|
||||||
|
|
||||||
|
|
||||||
def format_str(src_contents: str, line_length: int) -> FileContent:
|
def format_str(src_contents: str, line_length: int) -> FileContent:
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from io import StringIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
from typing import Any, List, Tuple
|
from typing import Any, List, Tuple
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
@ -10,7 +12,7 @@
|
|||||||
import black
|
import black
|
||||||
|
|
||||||
ll = 88
|
ll = 88
|
||||||
ff = partial(black.format_file, line_length=ll, fast=True)
|
ff = partial(black.format_file_in_place, line_length=ll, fast=True)
|
||||||
fs = partial(black.format_str, line_length=ll)
|
fs = partial(black.format_str, line_length=ll)
|
||||||
THIS_FILE = Path(__file__)
|
THIS_FILE = Path(__file__)
|
||||||
THIS_DIR = THIS_FILE.parent
|
THIS_DIR = THIS_FILE.parent
|
||||||
@ -70,8 +72,7 @@ def test_self(self) -> None:
|
|||||||
self.assertFormatEqual(expected, actual)
|
self.assertFormatEqual(expected, actual)
|
||||||
black.assert_equivalent(source, actual)
|
black.assert_equivalent(source, actual)
|
||||||
black.assert_stable(source, actual, line_length=ll)
|
black.assert_stable(source, actual, line_length=ll)
|
||||||
with self.assertRaises(black.NothingChanged):
|
self.assertFalse(ff(THIS_FILE))
|
||||||
ff(THIS_FILE)
|
|
||||||
|
|
||||||
@patch("black.dump_to_file", dump_to_stderr)
|
@patch("black.dump_to_file", dump_to_stderr)
|
||||||
def test_black(self) -> None:
|
def test_black(self) -> None:
|
||||||
@ -80,8 +81,22 @@ def test_black(self) -> None:
|
|||||||
self.assertFormatEqual(expected, actual)
|
self.assertFormatEqual(expected, actual)
|
||||||
black.assert_equivalent(source, actual)
|
black.assert_equivalent(source, actual)
|
||||||
black.assert_stable(source, actual, line_length=ll)
|
black.assert_stable(source, actual, line_length=ll)
|
||||||
with self.assertRaises(black.NothingChanged):
|
self.assertFalse(ff(THIS_DIR / '..' / 'black.py'))
|
||||||
ff(THIS_FILE)
|
|
||||||
|
def test_piping(self) -> None:
|
||||||
|
source, expected = read_data('../black')
|
||||||
|
hold_stdin, hold_stdout = sys.stdin, sys.stdout
|
||||||
|
try:
|
||||||
|
sys.stdin, sys.stdout = StringIO(source), StringIO()
|
||||||
|
sys.stdin.name = '<stdin>'
|
||||||
|
black.format_stdin_to_stdout(line_length=ll, fast=True)
|
||||||
|
sys.stdout.seek(0)
|
||||||
|
actual = sys.stdout.read()
|
||||||
|
finally:
|
||||||
|
sys.stdin, sys.stdout = hold_stdin, hold_stdout
|
||||||
|
self.assertFormatEqual(expected, actual)
|
||||||
|
black.assert_equivalent(source, actual)
|
||||||
|
black.assert_stable(source, actual, line_length=ll)
|
||||||
|
|
||||||
@patch("black.dump_to_file", dump_to_stderr)
|
@patch("black.dump_to_file", dump_to_stderr)
|
||||||
def test_setup(self) -> None:
|
def test_setup(self) -> None:
|
||||||
@ -90,8 +105,7 @@ def test_setup(self) -> None:
|
|||||||
self.assertFormatEqual(expected, actual)
|
self.assertFormatEqual(expected, actual)
|
||||||
black.assert_equivalent(source, actual)
|
black.assert_equivalent(source, actual)
|
||||||
black.assert_stable(source, actual, line_length=ll)
|
black.assert_stable(source, actual, line_length=ll)
|
||||||
with self.assertRaises(black.NothingChanged):
|
self.assertFalse(ff(THIS_DIR / '..' / 'setup.py'))
|
||||||
ff(THIS_FILE)
|
|
||||||
|
|
||||||
@patch("black.dump_to_file", dump_to_stderr)
|
@patch("black.dump_to_file", dump_to_stderr)
|
||||||
def test_function(self) -> None:
|
def test_function(self) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user