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

[3889] Fix long lines with power operator(s) getting splitted before line length #3942

Merged
merged 9 commits into from Oct 16, 2023
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -16,6 +16,7 @@

<!-- Changes that affect Black's preview style -->

- Fix long lines with power operators getting splitted before the line length (#3942)
- Long type hints are now wrapped in parentheses and properly indented when split across
multiple lines (#3899)
- Magic trailing commas are now respected in return types. (#3916)
Expand Down
21 changes: 20 additions & 1 deletion src/black/linegen.py
Expand Up @@ -536,6 +536,17 @@ def __post_init__(self) -> None:
self.visit_case_block = self.visit_match_case


def _hugging_power_ops_line_to_string(
line: Line,
features: Collection[Feature],
mode: Mode,
) -> Optional[str]:
try:
return line_to_string(next(hug_power_op(line, features, mode)))
except CannotTransform:
return None


def transform_line(
line: Line, mode: Mode, features: Collection[Feature] = ()
) -> Iterator[Line]:
Expand All @@ -551,6 +562,14 @@ def transform_line(

line_str = line_to_string(line)

# We need the line string when power operators are hugging to determine if we should
# split the line. Default to line_str, if no power operator are present on the line.
line_str_hugging_power_ops = (
(_hugging_power_ops_line_to_string(line, features, mode) or line_str)
if Preview.fix_power_op_line_length in mode
else line_str
)

ll = mode.line_length
sn = mode.string_normalization
string_merge = StringMerger(ll, sn)
Expand All @@ -564,7 +583,7 @@ def transform_line(
and not line.should_split_rhs
and not line.magic_trailing_comma
and (
is_line_short_enough(line, mode=mode, line_str=line_str)
is_line_short_enough(line, mode=mode, line_str=line_str_hugging_power_ops)
or line.contains_unsplittable_type_ignore()
)
and not (line.inside_brackets and line.contains_standalone_comments())
Expand Down
1 change: 1 addition & 0 deletions src/black/mode.py
Expand Up @@ -188,6 +188,7 @@ class Preview(Enum):
dummy_implementations = auto()
walrus_subscript = auto()
module_docstring_newlines = auto()
fix_power_op_line_length = auto()


class Deprecated(UserWarning):
Expand Down
18 changes: 18 additions & 0 deletions tests/data/cases/power_op_spacing.py
Expand Up @@ -29,6 +29,13 @@ def function_dont_replace_spaces():
p = {(k, k**2): v**2 for k, v in pairs}
q = [10**i for i in range(6)]
r = x**y
s = 1 ** 1
t = (
1
** 1
**1
** 1
)

a = 5.0**~4.0
b = 5.0 ** f()
Expand All @@ -47,6 +54,13 @@ def function_dont_replace_spaces():
o = settings(max_examples=10**6.0)
p = {(k, k**2): v**2.0 for k, v in pairs}
q = [10.5**i for i in range(6)]
s = 1.0 ** 1.0
t = (
1.0
** 1.0
**1.0
** 1.0
)


# WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873)
Expand Down Expand Up @@ -97,6 +111,8 @@ def function_dont_replace_spaces():
p = {(k, k**2): v**2 for k, v in pairs}
q = [10**i for i in range(6)]
r = x**y
s = 1**1
t = 1**1**1**1

a = 5.0**~4.0
b = 5.0 ** f()
Expand All @@ -115,6 +131,8 @@ def function_dont_replace_spaces():
o = settings(max_examples=10**6.0)
p = {(k, k**2): v**2.0 for k, v in pairs}
q = [10.5**i for i in range(6)]
s = 1.0**1.0
t = 1.0**1.0**1.0**1.0


# WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873)
Expand Down
97 changes: 97 additions & 0 deletions tests/data/cases/preview_power_op_spacing.py
@@ -0,0 +1,97 @@
# flags: --preview
a = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1
b = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1
c = 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1
d = 1**1 ** 1**1 ** 1**1 ** 1**1 ** 1**1**1 ** 1 ** 1**1 ** 1**1**1**1**1 ** 1 ** 1**1**1 **1**1** 1 ** 1 ** 1
e = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟
f = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟

a = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0
b = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0
c = 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0
d = 1.0**1.0 ** 1.0**1.0 ** 1.0**1.0 ** 1.0**1.0 ** 1.0**1.0**1.0 ** 1.0 ** 1.0**1.0 ** 1.0**1.0**1.0

# output
a = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1
b = (
1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
** 1
)
c = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1
d = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1
e = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟
f = (
𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
** 𨉟
)

a = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0
b = (
1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
** 1.0
)
c = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0
d = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0