X-Git-Url: https://git.madduck.net/etc/vim.git/blobdiff_plain/e74117f172e29e8a980e2c9de929ad50d3769150..4c480bcac288883040e6fda3c75825a0da54eb66:/black.py diff --git a/black.py b/black.py index 24c57ca..e2c427c 100644 --- a/black.py +++ b/black.py @@ -20,7 +20,7 @@ from blib2to3 import pygram, pytree from blib2to3.pgen2 import driver, token from blib2to3.pgen2.parse import ParseError -__version__ = "18.3a0" +__version__ = "18.3a1" DEFAULT_LINE_LENGTH = 88 # types syms = pygram.python_symbols @@ -55,6 +55,15 @@ class CannotSplit(Exception): help='How many character per line to allow.', show_default=True, ) +@click.option( + '--check', + is_flag=True, + help=( + "Don't write back the files, just return the status. Return code 0 " + "means nothing changed. Return code 1 means some files were " + "reformatted. Return code 123 means there was an internal error." + ), +) @click.option( '--fast/--safe', is_flag=True, @@ -67,7 +76,9 @@ class CannotSplit(Exception): type=click.Path(exists=True, file_okay=True, dir_okay=True, readable=True), ) @click.pass_context -def main(ctx: click.Context, line_length: int, fast: bool, src: List[str]) -> None: +def main( + ctx: click.Context, line_length: int, check: bool, fast: bool, src: List[str] +) -> None: """The uncompromising code formatter.""" sources: List[Path] = [] for s in src: @@ -85,7 +96,9 @@ def main(ctx: click.Context, line_length: int, fast: bool, src: List[str]) -> No p = sources[0] report = Report() try: - changed = format_file_in_place(p, line_length=line_length, fast=fast) + changed = format_file_in_place( + p, line_length=line_length, fast=fast, write_back=not check + ) report.done(p, changed) except Exception as exc: report.failed(p, str(exc)) @@ -96,7 +109,9 @@ def main(ctx: click.Context, line_length: int, fast: bool, src: List[str]) -> No return_code = 1 try: return_code = loop.run_until_complete( - schedule_formatting(sources, line_length, fast, loop, executor) + schedule_formatting( + sources, line_length, not check, fast, loop, executor + ) ) finally: loop.close() @@ -106,13 +121,14 @@ def main(ctx: click.Context, line_length: int, fast: bool, src: List[str]) -> No async def schedule_formatting( sources: List[Path], line_length: int, + write_back: bool, fast: bool, loop: BaseEventLoop, executor: Executor, ) -> int: tasks = { src: loop.run_in_executor( - executor, format_file_in_place, src, line_length, fast + executor, format_file_in_place, src, line_length, fast, write_back ) for src in sources } @@ -135,15 +151,18 @@ async def schedule_formatting( return report.return_code -def format_file_in_place(src: Path, line_length: int, fast: bool) -> bool: +def format_file_in_place( + src: Path, line_length: int, fast: bool, write_back: bool = False +) -> bool: """Format the file and rewrite if changed. Return True if changed.""" try: contents, encoding = format_file(src, line_length=line_length, fast=fast) except NothingChanged: return False - with open(src, "w", encoding=encoding) as f: - f.write(contents) + if write_back: + with open(src, "w", encoding=encoding) as f: + f.write(contents) return True @@ -358,7 +377,7 @@ class BracketTracker: """Returns True if there is an yet unmatched open bracket on the line.""" return bool(self.bracket_match) - def max_priority(self, exclude: Iterable[LeafID] = ()) -> int: + def max_priority(self, exclude: Iterable[LeafID] =()) -> int: """Returns the highest priority of a delimiter found on the line. Values are consistent with what `is_delimiter()` returns. @@ -765,6 +784,7 @@ def whitespace(leaf: Leaf) -> str: DOUBLESPACE = ' ' t = leaf.type p = leaf.parent + v = leaf.value if t == token.COLON: return NO @@ -780,13 +800,51 @@ def whitespace(leaf: Leaf) -> str: if t == STANDALONE_COMMENT: return NO + if t in CLOSING_BRACKETS: + return NO + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + if not prevp or prevp.type in OPENING_BRACKETS: + return NO + + if prevp.type == token.EQUAL: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: + return NO + + elif prevp.type == token.DOUBLESTAR: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.dictsetmaker, + }: + return NO + + elif prevp.type == token.COLON: + if prevp.parent and prevp.parent.type == syms.subscript: + return NO + + elif prevp.parent and prevp.parent.type == syms.factor: + return NO + + elif prev.type in OPENING_BRACKETS: + return NO + if p.type in {syms.parameters, syms.arglist}: # untyped function signatures or calls if t == token.RPAR: return NO - prev = leaf.prev_sibling if not prev or prev.type != token.COMMA: return NO @@ -795,13 +853,11 @@ def whitespace(leaf: Leaf) -> str: if t == token.RPAR: return NO - prev = leaf.prev_sibling if prev and prev.type != token.COMMA: return NO elif p.type == syms.typedargslist: # typed function signatures - prev = leaf.prev_sibling if not prev: return NO @@ -819,7 +875,6 @@ def whitespace(leaf: Leaf) -> str: elif p.type == syms.tname: # type names - prev = leaf.prev_sibling if not prev: prevp = preceding_leaf(p) if not prevp or prevp.type != token.COMMA: @@ -830,7 +885,6 @@ def whitespace(leaf: Leaf) -> str: if t == token.LPAR or t == token.RPAR: return NO - prev = leaf.prev_sibling if not prev: if t == token.DOT: prevp = preceding_leaf(p) @@ -848,7 +902,6 @@ def whitespace(leaf: Leaf) -> str: if t == token.EQUAL: return NO - prev = leaf.prev_sibling if not prev: prevp = preceding_leaf(p) if not prevp or prevp.type == token.LPAR: @@ -862,88 +915,27 @@ def whitespace(leaf: Leaf) -> str: return NO elif p.type == syms.dotted_name: - prev = leaf.prev_sibling if prev: return NO prevp = preceding_leaf(p) - if not prevp or prevp.type == token.AT: + if not prevp or prevp.type == token.AT or prevp.type == token.DOT: return NO elif p.type == syms.classdef: if t == token.LPAR: return NO - prev = leaf.prev_sibling if prev and prev.type == token.LPAR: return NO elif p.type == syms.subscript: # indexing - if t == token.COLON: - return NO - - prev = leaf.prev_sibling if not prev or prev.type == token.COLON: return NO - elif p.type in { - syms.test, - syms.not_test, - syms.xor_expr, - syms.or_test, - syms.and_test, - syms.arith_expr, - syms.shift_expr, - syms.yield_expr, - syms.term, - syms.power, - syms.comparison, - }: - # various arithmetic and logic expressions - prev = leaf.prev_sibling - if not prev: - prevp = preceding_leaf(p) - if not prevp or prevp.type in OPENING_BRACKETS: - return NO - - if prevp.type == token.EQUAL: - if prevp.parent and prevp.parent.type in { - syms.varargslist, syms.parameters, syms.arglist, syms.argument - }: - return NO - - return SPACE - elif p.type == syms.atom: - if t in CLOSING_BRACKETS: - return NO - - prev = leaf.prev_sibling - if not prev: - prevp = preceding_leaf(p) - if not prevp: - return NO - - if prevp.type in OPENING_BRACKETS: - return NO - - if prevp.type == token.EQUAL: - if prevp.parent and prevp.parent.type in { - syms.varargslist, syms.parameters, syms.arglist, syms.argument - }: - return NO - - if prevp.type == token.DOUBLESTAR: - if prevp.parent and prevp.parent.type in { - syms.varargslist, syms.parameters, syms.arglist, syms.dictsetmaker - }: - return NO - - elif prev.type in OPENING_BRACKETS: - return NO - - elif t == token.DOT: + if prev and t == token.DOT: # dots, but not the first one. return NO @@ -953,13 +945,11 @@ def whitespace(leaf: Leaf) -> str: p.type == syms.subscriptlist ): # list interior, including unpacking - prev = leaf.prev_sibling if not prev: return NO elif p.type == syms.dictsetmaker: # dict and set interior, including unpacking - prev = leaf.prev_sibling if not prev: return NO @@ -968,7 +958,6 @@ def whitespace(leaf: Leaf) -> str: elif p.type == syms.factor or p.type == syms.star_expr: # unary ops - prev = leaf.prev_sibling if not prev: prevp = preceding_leaf(p) if not prevp or prevp.type in OPENING_BRACKETS: @@ -987,10 +976,17 @@ def whitespace(leaf: Leaf) -> str: elif t == token.NAME or t == token.NUMBER: return NO - elif p.type == syms.import_from and t == token.NAME: - prev = leaf.prev_sibling - if prev and prev.type == token.DOT: - return NO + elif p.type == syms.import_from: + if t == token.DOT: + if prev and prev.type == token.DOT: + return NO + + elif t == token.NAME: + if v == 'import': + return SPACE + + if prev and prev.type == token.DOT: + return NO elif p.type == syms.sliceop: return NO @@ -1344,7 +1340,15 @@ class Report: @property def return_code(self) -> int: """Which return code should the app use considering the current state.""" - return 1 if self.failure_count else 0 + # According to http://tldp.org/LDP/abs/html/exitcodes.html starting with + # 126 we have special returncodes reserved by the shell. + if self.failure_count: + return 123 + + elif self.change_count: + return 1 + + return 0 def __str__(self) -> str: """A color report of the current state.