From 34cf59825713d3baed5eb5b179765ec9784a6fd3 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 10:07:22 -0800 Subject: [PATCH 1/8] For all format tests, check that both preview and non-preview succeed without crashes --- tests/util.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/tests/util.py b/tests/util.py index d65c2e651ae..3be7218791f 100644 --- a/tests/util.py +++ b/tests/util.py @@ -2,6 +2,7 @@ import sys import unittest from contextlib import contextmanager +from dataclasses import replace from functools import partial from pathlib import Path from typing import Any, Iterator, List, Optional, Tuple @@ -58,7 +59,7 @@ def _assert_format_equal(expected: str, actual: str) -> None: def assert_format( source: str, - expected: str, + expected: Optional[str] = None, mode: black.Mode = DEFAULT_MODE, *, fast: bool = False, @@ -70,12 +71,36 @@ def assert_format( safety guards so they don't just crash with a SyntaxError. Please note this is separate from TargetVerson Mode configuration. """ + _assert_format_inner( + source, expected, mode, fast=fast, minimum_version=minimum_version + ) + + # For both preview and non-preview tests, ensure that Black doesn't crash on this code, + # but don't pass "expected" because the precise output may differ. + _assert_format_inner( + source, + None, + replace(mode, preview=not mode.preview), + fast=fast, + minimum_version=minimum_version, + ) + + +def _assert_format_inner( + source: str, + expected: Optional[str] = None, + mode: black.Mode = DEFAULT_MODE, + *, + fast: bool = False, + minimum_version: Optional[Tuple[int, int]] = None, +) -> None: actual = black.format_str(source, mode=mode) - _assert_format_equal(expected, actual) + if expected is not None: + _assert_format_equal(expected, actual) # It's not useful to run safety checks if we're expecting no changes anyway. The # assertion right above will raise if reality does actually make changes. This just # avoids wasted CPU cycles. - if not fast and source != expected: + if not fast and source != actual: # Unfortunately the AST equivalence check relies on the built-in ast module # being able to parse the code being formatted. This doesn't always work out # when checking modern code on older versions. From 2349135d5703cc523bb4436ec91aaa0caa4db19d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 10:12:26 -0800 Subject: [PATCH 2/8] fix the failures --- CHANGES.md | 2 ++ src/black/linegen.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index c84feb04934..6e131b6ed3c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,8 @@ - Fix a crash in preview style with assert + parenthesized string (#3415) +- Fix crashes in preview style with walrus operators used in function return annotations + and except clauses (#3423) ### Configuration diff --git a/src/black/linegen.py b/src/black/linegen.py index 219495e9a5e..f1f2a5a88ad 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -1160,6 +1160,8 @@ def maybe_make_parens_invisible_in_atom( syms.expr_stmt, syms.assert_stmt, syms.return_stmt, + syms.except_clause, + syms.funcdef, # these ones aren't useful to end users, but they do please fuzzers syms.for_stmt, syms.del_stmt, From 151ff05d7d4743fbc3c0c58ae9a982293fcf6fef Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 10:20:00 -0800 Subject: [PATCH 3/8] lint, undo unnecessary change --- tests/util.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/util.py b/tests/util.py index 3be7218791f..f8e77342429 100644 --- a/tests/util.py +++ b/tests/util.py @@ -59,7 +59,7 @@ def _assert_format_equal(expected: str, actual: str) -> None: def assert_format( source: str, - expected: Optional[str] = None, + expected: str, mode: black.Mode = DEFAULT_MODE, *, fast: bool = False, @@ -75,8 +75,8 @@ def assert_format( source, expected, mode, fast=fast, minimum_version=minimum_version ) - # For both preview and non-preview tests, ensure that Black doesn't crash on this code, - # but don't pass "expected" because the precise output may differ. + # For both preview and non-preview tests, ensure that Black doesn't crash on + # this code, but don't pass "expected" because the precise output may differ. _assert_format_inner( source, None, From b3882b8d2a6673c7b078b465fa170c6b1b5183dc Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 11:10:30 -0800 Subject: [PATCH 4/8] temp extra --- tests/util.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/util.py b/tests/util.py index f8e77342429..bb3f390880c 100644 --- a/tests/util.py +++ b/tests/util.py @@ -75,6 +75,30 @@ def assert_format( source, expected, mode, fast=fast, minimum_version=minimum_version ) + for line_length in (88, 100, 200, 1): + for string_normalization in (True, False): + for is_pyi in (False, True): + for magic_trailing_comma in (True, False): + for preview in (True, False): + new_mode = replace( + mode, + line_length=line_length, + string_normalization=string_normalization, + is_pyi=is_pyi, + magic_trailing_comma=magic_trailing_comma, + preview=preview, + ) + if mode == new_mode: + continue + print("try", new_mode) + _assert_format_inner( + source, + None, + new_mode, + fast=fast, + minimum_version=minimum_version, + ) + # For both preview and non-preview tests, ensure that Black doesn't crash on # this code, but don't pass "expected" because the precise output may differ. _assert_format_inner( @@ -84,6 +108,25 @@ def assert_format( fast=fast, minimum_version=minimum_version, ) + for preview in (True, False): + _assert_format_inner( + source, + None, + replace( + mode, + magic_trailing_comma=not mode.magic_trailing_comma, + preview=preview, + ), + fast=fast, + minimum_version=minimum_version, + ) + _assert_format_inner( + source, + None, + replace(mode, string_normalization=not mode.string_normalization), + fast=fast, + minimum_version=minimum_version, + ) def _assert_format_inner( From 5e6de530ffb8457f17a372881c3ce600de0f04fc Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 11:11:03 -0800 Subject: [PATCH 5/8] Fix syntax error in match test --- tests/data/py_310/pattern_matching_extras.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/py_310/pattern_matching_extras.py b/tests/data/py_310/pattern_matching_extras.py index 9f6907f7575..0242d264e5b 100644 --- a/tests/data/py_310/pattern_matching_extras.py +++ b/tests/data/py_310/pattern_matching_extras.py @@ -114,6 +114,6 @@ def func(match: case, case: match) -> case: match bar1: case Foo( - normal=x, perhaps=[list, {an: d, dict: 1.0}] as y, otherwise=something, q=t as u + normal=x, perhaps=[list, {"x": d, "y": 1.0}] as y, otherwise=something, q=t as u ): pass From 47b485aa0c2f96ae4897c7e93c182ff51aa0fb39 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 11:47:38 -0800 Subject: [PATCH 6/8] add line-length 1 test --- tests/util.py | 41 ++++------------------------------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/tests/util.py b/tests/util.py index bb3f390880c..0b2fd7da7a8 100644 --- a/tests/util.py +++ b/tests/util.py @@ -75,30 +75,6 @@ def assert_format( source, expected, mode, fast=fast, minimum_version=minimum_version ) - for line_length in (88, 100, 200, 1): - for string_normalization in (True, False): - for is_pyi in (False, True): - for magic_trailing_comma in (True, False): - for preview in (True, False): - new_mode = replace( - mode, - line_length=line_length, - string_normalization=string_normalization, - is_pyi=is_pyi, - magic_trailing_comma=magic_trailing_comma, - preview=preview, - ) - if mode == new_mode: - continue - print("try", new_mode) - _assert_format_inner( - source, - None, - new_mode, - fast=fast, - minimum_version=minimum_version, - ) - # For both preview and non-preview tests, ensure that Black doesn't crash on # this code, but don't pass "expected" because the precise output may differ. _assert_format_inner( @@ -108,22 +84,13 @@ def assert_format( fast=fast, minimum_version=minimum_version, ) - for preview in (True, False): - _assert_format_inner( - source, - None, - replace( - mode, - magic_trailing_comma=not mode.magic_trailing_comma, - preview=preview, - ), - fast=fast, - minimum_version=minimum_version, - ) + # Similarly, setting line length to 1 is a good way to catch + # stability bugs. But only in non-preview mode because preview mode + # currently has a lot of line length 1 bugs. _assert_format_inner( source, None, - replace(mode, string_normalization=not mode.string_normalization), + replace(mode, preview=False, line_length=1), fast=fast, minimum_version=minimum_version, ) From 39c8778955f419b211ccb4ecad8751f282c0e1bb Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 09:32:27 -0800 Subject: [PATCH 7/8] make failures more explicitt --- tests/util.py | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/tests/util.py b/tests/util.py index 0b2fd7da7a8..eefc0149431 100644 --- a/tests/util.py +++ b/tests/util.py @@ -57,6 +57,10 @@ def _assert_format_equal(expected: str, actual: str) -> None: assert actual == expected +class FormatFailure(Exception): + """Used to wrap failures when assert_format() runs in an extra mode.""" + + def assert_format( source: str, expected: str, @@ -77,23 +81,35 @@ def assert_format( # For both preview and non-preview tests, ensure that Black doesn't crash on # this code, but don't pass "expected" because the precise output may differ. - _assert_format_inner( - source, - None, - replace(mode, preview=not mode.preview), - fast=fast, - minimum_version=minimum_version, - ) + try: + _assert_format_inner( + source, + None, + replace(mode, preview=not mode.preview), + fast=fast, + minimum_version=minimum_version, + ) + except Exception as e: + text = "non-preview" if mode.preview else "preview" + raise FormatFailure( + f"Black crashed formatting this case in {text} mode." + ) from e # Similarly, setting line length to 1 is a good way to catch # stability bugs. But only in non-preview mode because preview mode # currently has a lot of line length 1 bugs. - _assert_format_inner( - source, - None, - replace(mode, preview=False, line_length=1), - fast=fast, - minimum_version=minimum_version, - ) + try: + _assert_format_inner( + source, + None, + replace(mode, preview=False, line_length=1), + fast=fast, + minimum_version=minimum_version, + ) + except Exception as e: + text = "non-preview" if mode.preview else "preview" + raise FormatFailure( + "Black crashed formatting this case with line-length set to 1." + ) from e def _assert_format_inner( From 8443797abf4e9ee5f1845a4f8e44d2cf700c21ba Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 09:50:57 -0800 Subject: [PATCH 8/8] Update tests/util.py Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> --- tests/util.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/util.py b/tests/util.py index eefc0149431..967d576fafe 100644 --- a/tests/util.py +++ b/tests/util.py @@ -106,7 +106,6 @@ def assert_format( minimum_version=minimum_version, ) except Exception as e: - text = "non-preview" if mode.preview else "preview" raise FormatFailure( "Black crashed formatting this case with line-length set to 1." ) from e