X-Git-Url: https://git.madduck.net/etc/vim.git/blobdiff_plain/8de552eb4f0fbf1ad84812cde71489cc00d3ed1f..7e1c5b2ba6355f1a6916cf5545dc577cf2e6c727:/black.py diff --git a/black.py b/black.py index bb8ec2e..53d332a 100644 --- a/black.py +++ b/black.py @@ -235,23 +235,36 @@ def format_str(src_contents: str, line_length: int) -> FileContent: return dst_contents +GRAMMARS = [ + pygram.python_grammar_no_print_statement_no_exec_statement, + pygram.python_grammar_no_print_statement, + pygram.python_grammar_no_exec_statement, + pygram.python_grammar, +] + + def lib2to3_parse(src_txt: str) -> Node: """Given a string with source, return the lib2to3 Node.""" grammar = pygram.python_grammar_no_print_statement - drv = driver.Driver(grammar, pytree.convert) if src_txt[-1] != '\n': nl = '\r\n' if '\r\n' in src_txt[:1024] else '\n' src_txt += nl - try: - result = drv.parse_string(src_txt, True) - except ParseError as pe: - lineno, column = pe.context[1] - lines = src_txt.splitlines() + for grammar in GRAMMARS: + drv = driver.Driver(grammar, pytree.convert) try: - faulty_line = lines[lineno - 1] - except IndexError: - faulty_line = "" - raise ValueError(f"Cannot parse: {lineno}:{column}: {faulty_line}") from None + result = drv.parse_string(src_txt, True) + break + + except ParseError as pe: + lineno, column = pe.context[1] + lines = src_txt.splitlines() + try: + faulty_line = lines[lineno - 1] + except IndexError: + faulty_line = "" + exc = ValueError(f"Cannot parse: {lineno}:{column}: {faulty_line}") + else: + raise exc from None if isinstance(result, Leaf): result = Node(syms.file_input, [result]) @@ -307,6 +320,15 @@ class DebugVisitor(Visitor[T]): out(f' {node.prefix!r}', fg='green', bold=False, nl=False) out(f' {node.value!r}', fg='blue', bold=False) + @classmethod + def show(cls, code: str) -> None: + """Pretty-prints a given string of `code`. + + Convenience method for debugging. + """ + v: DebugVisitor[None] = DebugVisitor() + list(v.visit(lib2to3_parse(code))) + KEYWORDS = set(keyword.kwlist) WHITESPACE = {token.DEDENT, token.INDENT, token.NEWLINE} @@ -406,7 +428,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. @@ -872,14 +894,17 @@ def whitespace(leaf: Leaf) -> str: # noqa C901 return SPACE if prevp.type == token.COMMA else NO if prevp.type == token.EQUAL: - if prevp.parent and prevp.parent.type in { - syms.arglist, - syms.argument, - syms.parameters, - syms.typedargslist, - syms.varargslist, - }: - return NO + if prevp.parent: + if prevp.parent.type in { + syms.arglist, syms.argument, syms.parameters, syms.varargslist + }: + return NO + + elif prevp.parent.type == syms.typedargslist: + # A bit hacky: if the equal sign has whitespace, it means we + # previously found it's a typed argument. So, we're using + # that, too. + return prevp.prefix elif prevp.type == token.DOUBLESTAR: if prevp.parent and prevp.parent.type in { @@ -903,6 +928,17 @@ def whitespace(leaf: Leaf) -> str: # noqa C901 ): return NO + elif ( + prevp.type == token.RIGHTSHIFT + and prevp.parent + and prevp.parent.type == syms.shift_expr + and prevp.prev_sibling + and prevp.prev_sibling.type == token.NAME + and prevp.prev_sibling.value == 'print' # type: ignore + ): + # Python 2 print chevron + return NO + elif prev.type in OPENING_BRACKETS: return NO @@ -914,7 +950,7 @@ def whitespace(leaf: Leaf) -> str: # noqa C901 if not prev or prev.type != token.COMMA: return NO - if p.type == syms.varargslist: + elif p.type == syms.varargslist: # lambdas if t == token.RPAR: return NO @@ -1156,7 +1192,7 @@ def make_comment(content: str) -> str: if content[0] == '#': content = content[1:] - if content and content[0] not in {' ', '!', '#'}: + if content and content[0] not in ' !:#': content = ' ' + content return '#' + content @@ -1538,7 +1574,12 @@ def assert_equivalent(src: str, dst: str) -> None: try: src_ast = ast.parse(src) except Exception as exc: - raise AssertionError(f"cannot parse source: {exc}") from None + major, minor = sys.version_info[:2] + raise AssertionError( + f"cannot use --safe with this file; failed to parse source file " + f"with Python {major}.{minor}'s builtin AST. Re-run with --fast " + f"or stop using deprecated Python 2 syntax. AST error message: {exc}" + ) try: dst_ast = ast.parse(dst)