Skip to content

Commit 1511959

Browse files
Merge branch 'main' into format/hex-code-literals
2 parents 625c085 + 4e3303f commit 1511959

File tree

5 files changed

+188
-6
lines changed

5 files changed

+188
-6
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<!-- Changes that affect Black's preview style -->
1818

1919
- Format hex code in unicode escape sequences in string literals (#2916)
20+
- Add parentheses around `if`-`else` expressions (#2278)
2021
- Improve the performance on large expressions that contain many strings (#3467)
2122
- Fix a crash in preview style with assert + parenthesized string (#3415)
2223
- Fix crashes in preview style with walrus operators used in function return annotations

src/black/__init__.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -478,16 +478,20 @@ def main( # noqa: C901
478478
)
479479

480480
normalized = [
481-
(source, source)
482-
if source == "-"
483-
else (normalize_path_maybe_ignore(Path(source), root), source)
481+
(
482+
(source, source)
483+
if source == "-"
484+
else (normalize_path_maybe_ignore(Path(source), root), source)
485+
)
484486
for source in src
485487
]
486488
srcs_string = ", ".join(
487489
[
488-
f'"{_norm}"'
489-
if _norm
490-
else f'\033[31m"{source} (skipping - invalid)"\033[34m'
490+
(
491+
f'"{_norm}"'
492+
if _norm
493+
else f'\033[31m"{source} (skipping - invalid)"\033[34m'
494+
)
491495
for _norm, source in normalized
492496
]
493497
)

src/black/linegen.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,22 @@ def visit_default(self, node: LN) -> Iterator[Line]:
141141
self.current_line.append(node)
142142
yield from super().visit_default(node)
143143

144+
def visit_test(self, node: Node) -> Iterator[Line]:
145+
"""Visit an `x if y else z` test"""
146+
147+
if Preview.parenthesize_conditional_expressions in self.mode:
148+
already_parenthesized = (
149+
node.prev_sibling and node.prev_sibling.type == token.LPAR
150+
)
151+
152+
if not already_parenthesized:
153+
lpar = Leaf(token.LPAR, "")
154+
rpar = Leaf(token.RPAR, "")
155+
node.insert_child(0, lpar)
156+
node.append_child(rpar)
157+
158+
yield from self.visit_default(node)
159+
144160
def visit_INDENT(self, node: Leaf) -> Iterator[Line]:
145161
"""Increase indentation level, maybe yield a line."""
146162
# In blib2to3 INDENT never holds comments.

src/black/mode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ class Preview(Enum):
162162
# NOTE: string_processing requires wrap_long_dict_values_in_parens
163163
# for https://github.com/psf/black/issues/3117 to be fixed.
164164
string_processing = auto()
165+
parenthesize_conditional_expressions = auto()
165166
skip_magic_trailing_comma_in_subscript = auto()
166167
wrap_long_dict_values_in_parens = auto()
167168

tests/data/conditional_expression.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
long_kwargs_single_line = my_function(
2+
foo="test, this is a sample value",
3+
bar=some_long_value_name_foo_bar_baz if some_boolean_variable else some_fallback_value_foo_bar_baz,
4+
baz="hello, this is a another value",
5+
)
6+
7+
multiline_kwargs_indented = my_function(
8+
foo="test, this is a sample value",
9+
bar=some_long_value_name_foo_bar_baz
10+
if some_boolean_variable
11+
else some_fallback_value_foo_bar_baz,
12+
baz="hello, this is a another value",
13+
)
14+
15+
imploding_kwargs = my_function(
16+
foo="test, this is a sample value",
17+
bar=a
18+
if foo
19+
else b,
20+
baz="hello, this is a another value",
21+
)
22+
23+
imploding_line = (
24+
1
25+
if 1 + 1 == 2
26+
else 0
27+
)
28+
29+
exploding_line = "hello this is a slightly long string" if some_long_value_name_foo_bar_baz else "this one is a little shorter"
30+
31+
positional_argument_test(some_long_value_name_foo_bar_baz if some_boolean_variable else some_fallback_value_foo_bar_baz)
32+
33+
def weird_default_argument(x=some_long_value_name_foo_bar_baz
34+
if SOME_CONSTANT
35+
else some_fallback_value_foo_bar_baz):
36+
pass
37+
38+
nested = "hello this is a slightly long string" if (some_long_value_name_foo_bar_baz if
39+
nesting_test_expressions else some_fallback_value_foo_bar_baz) \
40+
else "this one is a little shorter"
41+
42+
generator_expression = (
43+
some_long_value_name_foo_bar_baz if some_boolean_variable else some_fallback_value_foo_bar_baz for some_boolean_variable in some_iterable
44+
)
45+
46+
47+
def limit_offset_sql(self, low_mark, high_mark):
48+
"""Return LIMIT/OFFSET SQL clause."""
49+
limit, offset = self._get_limit_offset_params(low_mark, high_mark)
50+
return " ".join(
51+
sql
52+
for sql in (
53+
"LIMIT %d" % limit if limit else None,
54+
("OFFSET %d" % offset) if offset else None,
55+
)
56+
if sql
57+
)
58+
59+
60+
def something():
61+
clone._iterable_class = (
62+
NamedValuesListIterable
63+
if named
64+
else FlatValuesListIterable
65+
if flat
66+
else ValuesListIterable
67+
)
68+
69+
# output
70+
71+
long_kwargs_single_line = my_function(
72+
foo="test, this is a sample value",
73+
bar=(
74+
some_long_value_name_foo_bar_baz
75+
if some_boolean_variable
76+
else some_fallback_value_foo_bar_baz
77+
),
78+
baz="hello, this is a another value",
79+
)
80+
81+
multiline_kwargs_indented = my_function(
82+
foo="test, this is a sample value",
83+
bar=(
84+
some_long_value_name_foo_bar_baz
85+
if some_boolean_variable
86+
else some_fallback_value_foo_bar_baz
87+
),
88+
baz="hello, this is a another value",
89+
)
90+
91+
imploding_kwargs = my_function(
92+
foo="test, this is a sample value",
93+
bar=a if foo else b,
94+
baz="hello, this is a another value",
95+
)
96+
97+
imploding_line = 1 if 1 + 1 == 2 else 0
98+
99+
exploding_line = (
100+
"hello this is a slightly long string"
101+
if some_long_value_name_foo_bar_baz
102+
else "this one is a little shorter"
103+
)
104+
105+
positional_argument_test(
106+
some_long_value_name_foo_bar_baz
107+
if some_boolean_variable
108+
else some_fallback_value_foo_bar_baz
109+
)
110+
111+
112+
def weird_default_argument(
113+
x=(
114+
some_long_value_name_foo_bar_baz
115+
if SOME_CONSTANT
116+
else some_fallback_value_foo_bar_baz
117+
),
118+
):
119+
pass
120+
121+
122+
nested = (
123+
"hello this is a slightly long string"
124+
if (
125+
some_long_value_name_foo_bar_baz
126+
if nesting_test_expressions
127+
else some_fallback_value_foo_bar_baz
128+
)
129+
else "this one is a little shorter"
130+
)
131+
132+
generator_expression = (
133+
(
134+
some_long_value_name_foo_bar_baz
135+
if some_boolean_variable
136+
else some_fallback_value_foo_bar_baz
137+
)
138+
for some_boolean_variable in some_iterable
139+
)
140+
141+
142+
def limit_offset_sql(self, low_mark, high_mark):
143+
"""Return LIMIT/OFFSET SQL clause."""
144+
limit, offset = self._get_limit_offset_params(low_mark, high_mark)
145+
return " ".join(
146+
sql
147+
for sql in (
148+
"LIMIT %d" % limit if limit else None,
149+
("OFFSET %d" % offset) if offset else None,
150+
)
151+
if sql
152+
)
153+
154+
155+
def something():
156+
clone._iterable_class = (
157+
NamedValuesListIterable
158+
if named
159+
else FlatValuesListIterable if flat else ValuesListIterable
160+
)

0 commit comments

Comments
 (0)