Skip to content
This repository was archived by the owner on Jul 11, 2022. It is now read-only.

Commit e911c79

Browse files
committed
Don't remove single empty lines outside of bracketed expressions
Fixes pytest-dev#19
1 parent d9c6b99 commit e911c79

File tree

5 files changed

+203
-12
lines changed

5 files changed

+203
-12
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ More details can be found in [CONTRIBUTING](CONTRIBUTING.md).
259259

260260
### 18.3a3
261261

262+
* don't remove single empty lines outside of bracketed expressions
263+
(#19)
264+
262265
* added ability to pipe formatting from stdin to stdin (#25)
263266

264267
* restored ability to format code with legacy usage of `async` as

black.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -745,8 +745,9 @@ def line(self, indent: int = 0) -> Iterator[Line]:
745745

746746
def visit_default(self, node: LN) -> Iterator[Line]:
747747
if isinstance(node, Leaf):
748+
any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
748749
for comment in generate_comments(node):
749-
if self.current_line.bracket_tracker.any_open_brackets():
750+
if any_open_brackets:
750751
# any comment within brackets is subject to splitting
751752
self.current_line.append(comment)
752753
elif comment.type == token.COMMENT:
@@ -758,7 +759,7 @@ def visit_default(self, node: LN) -> Iterator[Line]:
758759
# regular standalone comment, to be processed later (see
759760
# docstring in `generate_comments()`
760761
self.standalone_comments.append(comment)
761-
normalize_prefix(node)
762+
normalize_prefix(node, inside_brackets=any_open_brackets)
762763
if node.type not in WHITESPACE:
763764
for comment in self.standalone_comments:
764765
yield from self.line()
@@ -1238,7 +1239,7 @@ def left_hand_split(line: Line, py36: bool = False) -> Iterator[Line]:
12381239
current_leaves = body_leaves
12391240
# Since body is a new indent level, remove spurious leading whitespace.
12401241
if body_leaves:
1241-
normalize_prefix(body_leaves[0])
1242+
normalize_prefix(body_leaves[0], inside_brackets=True)
12421243
# Build the new lines.
12431244
for result, leaves in (
12441245
(head, head_leaves), (body, body_leaves), (tail, tail_leaves)
@@ -1278,7 +1279,7 @@ def right_hand_split(line: Line, py36: bool = False) -> Iterator[Line]:
12781279
head_leaves.reverse()
12791280
# Since body is a new indent level, remove spurious leading whitespace.
12801281
if body_leaves:
1281-
normalize_prefix(body_leaves[0])
1282+
normalize_prefix(body_leaves[0], inside_brackets=True)
12821283
# Build the new lines.
12831284
for result, leaves in (
12841285
(head, head_leaves), (body, body_leaves), (tail, tail_leaves)
@@ -1342,7 +1343,7 @@ def delimiter_split(line: Line, py36: bool = False) -> Iterator[Line]:
13421343
trailing_comma_safe = trailing_comma_safe and py36
13431344
leaf_priority = delimiters.get(id(leaf))
13441345
if leaf_priority == delimiter_priority:
1345-
normalize_prefix(current_line.leaves[0])
1346+
normalize_prefix(current_line.leaves[0], inside_brackets=True)
13461347
yield current_line
13471348

13481349
current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
@@ -1353,7 +1354,7 @@ def delimiter_split(line: Line, py36: bool = False) -> Iterator[Line]:
13531354
and trailing_comma_safe
13541355
):
13551356
current_line.append(Leaf(token.COMMA, ','))
1356-
normalize_prefix(current_line.leaves[0])
1357+
normalize_prefix(current_line.leaves[0], inside_brackets=True)
13571358
yield current_line
13581359

13591360

@@ -1371,13 +1372,18 @@ def is_import(leaf: Leaf) -> bool:
13711372
)
13721373

13731374

1374-
def normalize_prefix(leaf: Leaf) -> None:
1375-
"""Leave existing extra newlines for imports. Remove everything else."""
1376-
if is_import(leaf):
1375+
def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None:
1376+
"""Leave existing extra newlines if not `inside_brackets`.
1377+
1378+
Remove everything else. Note: don't use backslashes for formatting or
1379+
you'll lose your voting rights.
1380+
"""
1381+
if not inside_brackets:
13771382
spl = leaf.prefix.split('#', 1)
1378-
nl_count = spl[0].count('\n')
1379-
leaf.prefix = '\n' * nl_count
1380-
return
1383+
if '\\' not in spl[0]:
1384+
nl_count = spl[0].count('\n')
1385+
leaf.prefix = '\n' * nl_count
1386+
return
13811387

13821388
leaf.prefix = ''
13831389

tests/comments2.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ def inline_comments_in_brackets_ruin_everything():
159159
""",
160160
arg3=True,
161161
)
162+
162163
############################################################################
163164
call2(
164165
# short

tests/empty_lines.py

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
def f():
2+
NO = ''
3+
SPACE = ' '
4+
DOUBLESPACE = ' '
5+
6+
t = leaf.type
7+
p = leaf.parent # trailing comment
8+
v = leaf.value
9+
10+
if t in ALWAYS_NO_SPACE:
11+
pass
12+
if t == token.COMMENT: # another trailing comment
13+
return DOUBLESPACE
14+
15+
assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
16+
17+
prev = leaf.prev_sibling
18+
if not prev:
19+
prevp = preceding_leaf(p)
20+
if not prevp or prevp.type in OPENING_BRACKETS:
21+
22+
return NO
23+
24+
if prevp.type == token.EQUAL:
25+
if prevp.parent and prevp.parent.type in {
26+
syms.typedargslist,
27+
syms.varargslist,
28+
syms.parameters,
29+
syms.arglist,
30+
syms.argument,
31+
}:
32+
return NO
33+
34+
elif prevp.type == token.DOUBLESTAR:
35+
if prevp.parent and prevp.parent.type in {
36+
syms.typedargslist,
37+
syms.varargslist,
38+
syms.parameters,
39+
syms.arglist,
40+
syms.dictsetmaker,
41+
}:
42+
return NO
43+
44+
45+
###############################################################################
46+
# SECTION BECAUSE SECTIONS
47+
###############################################################################
48+
49+
50+
def g():
51+
NO = ''
52+
SPACE = ' '
53+
DOUBLESPACE = ' '
54+
55+
t = leaf.type
56+
p = leaf.parent
57+
v = leaf.value
58+
59+
# Comment because comments
60+
61+
if t in ALWAYS_NO_SPACE:
62+
pass
63+
if t == token.COMMENT:
64+
return DOUBLESPACE
65+
66+
# Another comment because more comments
67+
assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
68+
69+
prev = leaf.prev_sibling
70+
if not prev:
71+
prevp = preceding_leaf(p)
72+
73+
if not prevp or prevp.type in OPENING_BRACKETS:
74+
# Start of the line or a bracketed expression.
75+
# More than one line for the comment.
76+
return NO
77+
78+
if prevp.type == token.EQUAL:
79+
if prevp.parent and prevp.parent.type in {
80+
syms.typedargslist,
81+
syms.varargslist,
82+
syms.parameters,
83+
syms.arglist,
84+
syms.argument,
85+
}:
86+
return NO
87+
88+
89+
# output
90+
91+
92+
def f():
93+
NO = ''
94+
SPACE = ' '
95+
DOUBLESPACE = ' '
96+
97+
t = leaf.type
98+
p = leaf.parent # trailing comment
99+
v = leaf.value
100+
101+
if t in ALWAYS_NO_SPACE:
102+
pass
103+
if t == token.COMMENT: # another trailing comment
104+
return DOUBLESPACE
105+
106+
assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
107+
108+
prev = leaf.prev_sibling
109+
if not prev:
110+
prevp = preceding_leaf(p)
111+
if not prevp or prevp.type in OPENING_BRACKETS:
112+
return NO
113+
114+
if prevp.type == token.EQUAL:
115+
if prevp.parent and prevp.parent.type in {
116+
syms.typedargslist,
117+
syms.varargslist,
118+
syms.parameters,
119+
syms.arglist,
120+
syms.argument,
121+
}:
122+
return NO
123+
124+
elif prevp.type == token.DOUBLESTAR:
125+
if prevp.parent and prevp.parent.type in {
126+
syms.typedargslist,
127+
syms.varargslist,
128+
syms.parameters,
129+
syms.arglist,
130+
syms.dictsetmaker,
131+
}:
132+
return NO
133+
134+
135+
###############################################################################
136+
# SECTION BECAUSE SECTIONS
137+
###############################################################################
138+
def g():
139+
NO = ''
140+
SPACE = ' '
141+
DOUBLESPACE = ' '
142+
143+
t = leaf.type
144+
p = leaf.parent
145+
v = leaf.value
146+
147+
# Comment because comments
148+
if t in ALWAYS_NO_SPACE:
149+
pass
150+
if t == token.COMMENT:
151+
return DOUBLESPACE
152+
153+
# Another comment because more comments
154+
assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
155+
156+
prev = leaf.prev_sibling
157+
if not prev:
158+
prevp = preceding_leaf(p)
159+
160+
if not prevp or prevp.type in OPENING_BRACKETS:
161+
# Start of the line or a bracketed expression.
162+
# More than one line for the comment.
163+
return NO
164+
165+
if prevp.type == token.EQUAL:
166+
if prevp.parent and prevp.parent.type in {
167+
syms.typedargslist,
168+
syms.varargslist,
169+
syms.parameters,
170+
syms.arglist,
171+
syms.argument,
172+
}:
173+
return NO

tests/test_black.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,14 @@ def test_composition(self) -> None:
171171
black.assert_equivalent(source, actual)
172172
black.assert_stable(source, actual, line_length=ll)
173173

174+
@patch("black.dump_to_file", dump_to_stderr)
175+
def test_empty_lines(self) -> None:
176+
source, expected = read_data('empty_lines')
177+
actual = fs(source)
178+
self.assertFormatEqual(expected, actual)
179+
black.assert_equivalent(source, actual)
180+
black.assert_stable(source, actual, line_length=ll)
181+
174182
def test_report(self) -> None:
175183
report = black.Report()
176184
out_lines = []

0 commit comments

Comments
 (0)