From 4035a9bb32c3a167db60185eb6ba4263e6d2555d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 3 Dec 2021 14:08:05 -0800 Subject: [PATCH 1/2] tell users to use -t py310 --- CHANGES.md | 1 + src/black/parsing.py | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index e5f4a1fdf82..dae7df7c9d5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ ### _Black_ +- Point users to using `--target-version py310` if we detect 3.10-only syntax (#2668) - Cell magics are now only processed if they are known Python cell magics. Earlier, all cell magics were tokenized, leading to possible indentation errors e.g. with `%%writefile`. (#2630) diff --git a/src/black/parsing.py b/src/black/parsing.py index e38405637cd..be656b813af 100644 --- a/src/black/parsing.py +++ b/src/black/parsing.py @@ -95,7 +95,8 @@ def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) - if not src_txt.endswith("\n"): src_txt += "\n" - for grammar in get_grammars(set(target_versions)): + grammars = get_grammars(set(target_versions)) + for grammar in grammars: drv = driver.Driver(grammar) try: result = drv.parse_string(src_txt, True) @@ -110,6 +111,10 @@ def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) - faulty_line = "" exc = InvalidInput(f"Cannot parse: {lineno}:{column}: {faulty_line}") else: + if pygram.python_grammar_soft_keywords not in grammars and matches_grammar(src_txt, pygram.python_grammar_soft_keywords): + original_msg = exc.args[0] + msg = f"{original_msg}\nConsider using --target-version py310 to parse Python 3.10 code." + raise InvalidInput(msg) from None raise exc from None if isinstance(result, Leaf): @@ -117,6 +122,16 @@ def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) - return result +def matches_grammar(src_txt: str, grammar: Grammar) -> bool: + drv = driver.Driver(grammar) + try: + drv.parse_string(src_txt, True) + except ParseError: + return False + else: + return True + + def lib2to3_unparse(node: Node) -> str: """Given a lib2to3 node, return its string representation.""" code = str(node) From 96ff65b72d9fd44658d84a4f51a8be89d6e0635f Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 3 Dec 2021 14:11:29 -0800 Subject: [PATCH 2/2] add test --- src/black/parsing.py | 11 +++++++++-- tests/test_format.py | 9 +++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/black/parsing.py b/src/black/parsing.py index be656b813af..825c50eb6d5 100644 --- a/src/black/parsing.py +++ b/src/black/parsing.py @@ -42,6 +42,11 @@ ast3 = ast27 = ast +PY310_HINT: Final[ + str +] = "Consider using --target-version py310 to parse Python 3.10 code." + + class InvalidInput(ValueError): """Raised when input source code fails all parse attempts.""" @@ -111,9 +116,11 @@ def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) - faulty_line = "" exc = InvalidInput(f"Cannot parse: {lineno}:{column}: {faulty_line}") else: - if pygram.python_grammar_soft_keywords not in grammars and matches_grammar(src_txt, pygram.python_grammar_soft_keywords): + if pygram.python_grammar_soft_keywords not in grammars and matches_grammar( + src_txt, pygram.python_grammar_soft_keywords + ): original_msg = exc.args[0] - msg = f"{original_msg}\nConsider using --target-version py310 to parse Python 3.10 code." + msg = f"{original_msg}\n{PY310_HINT}" raise InvalidInput(msg) from None raise exc from None diff --git a/tests/test_format.py b/tests/test_format.py index d44be1e8712..30099aaf1bc 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -210,6 +210,15 @@ def test_patma_invalid() -> None: exc_info.match("Cannot parse: 10:11") +def test_patma_hint() -> None: + source, expected = read_data("pattern_matching_simple") + mode = black.Mode(target_versions={black.TargetVersion.PY39}) + with pytest.raises(black.parsing.InvalidInput) as exc_info: + assert_format(source, expected, mode, minimum_version=(3, 10)) + + exc_info.match(black.parsing.PY310_HINT) + + def test_docstring_no_string_normalization() -> None: """Like test_docstring but with string normalization off.""" source, expected = read_data("docstring_no_string_normalization")