Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check stability for both preview and non-preview styles #3423

Merged
merged 9 commits into from Dec 17, 2022
2 changes: 2 additions & 0 deletions CHANGES.md
Expand Up @@ -15,6 +15,8 @@
<!-- Changes that affect Black's preview style -->

- 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

Expand Down
2 changes: 2 additions & 0 deletions src/black/linegen.py
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion tests/data/py_310/pattern_matching_extras.py
Expand Up @@ -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
39 changes: 37 additions & 2 deletions tests/util.py
Expand Up @@ -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
Expand Down Expand Up @@ -70,12 +71,46 @@ 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,
)
# 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,
)


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.
Comment on lines 125 to 127
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# 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.

Unfortunately this optimization is now gone and I can't find a way to bring it back :(

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why? The optimization still applies.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you are right. My bad.

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.
Expand Down