Skip to content

Commit 7b5a657

Browse files
authored
Fix --line-ranges behavior when ranges are at EOF (#4273)
Fixes #4264
1 parent 1abcffc commit 7b5a657

File tree

6 files changed

+148
-2
lines changed

6 files changed

+148
-2
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
of Black would incorrectly format the contents of certain unusual f-strings containing
1616
nested strings with the same quote type. Now, Black will crash on such strings until
1717
support for the new f-string syntax is implemented. (#4270)
18+
- Fixed a bug where line-ranges exceeding the last code line would not work as expected
19+
(#4273)
1820

1921
### Preview style
2022

src/black/__init__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,12 @@
8484
parse_ast,
8585
stringify_ast,
8686
)
87-
from black.ranges import adjusted_lines, convert_unchanged_lines, parse_line_ranges
87+
from black.ranges import (
88+
adjusted_lines,
89+
convert_unchanged_lines,
90+
parse_line_ranges,
91+
sanitized_lines,
92+
)
8893
from black.report import Changed, NothingChanged, Report
8994
from black.trans import iter_fexpr_spans
9095
from blib2to3.pgen2 import token
@@ -1220,6 +1225,10 @@ def f(
12201225
hey
12211226
12221227
"""
1228+
if lines:
1229+
lines = sanitized_lines(lines, src_contents)
1230+
if not lines:
1231+
return src_contents # Nothing to format
12231232
dst_contents = _format_str_once(src_contents, mode=mode, lines=lines)
12241233
# Forced second pass to work around optional trailing commas (becoming
12251234
# forced trailing commas on pass 2) interacting differently with optional

src/black/ranges.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,34 @@ def is_valid_line_range(lines: Tuple[int, int]) -> bool:
4545
return not lines or lines[0] <= lines[1]
4646

4747

48+
def sanitized_lines(
49+
lines: Collection[Tuple[int, int]], src_contents: str
50+
) -> Collection[Tuple[int, int]]:
51+
"""Returns the valid line ranges for the given source.
52+
53+
This removes ranges that are entirely outside the valid lines.
54+
55+
Other ranges are normalized so that the start values are at least 1 and the
56+
end values are at most the (1-based) index of the last source line.
57+
"""
58+
if not src_contents:
59+
return []
60+
good_lines = []
61+
src_line_count = src_contents.count("\n")
62+
if not src_contents.endswith("\n"):
63+
src_line_count += 1
64+
for start, end in lines:
65+
if start > src_line_count:
66+
continue
67+
# line-ranges are 1-based
68+
start = max(start, 1)
69+
if end < start:
70+
continue
71+
end = min(end, src_line_count)
72+
good_lines.append((start, end))
73+
return good_lines
74+
75+
4876
def adjusted_lines(
4977
lines: Collection[Tuple[int, int]],
5078
original_source: str,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# flags: --line-ranges=6-1000
2+
# NOTE: If you need to modify this file, pay special attention to the --line-ranges=
3+
# flag above as it's formatting specifically these lines.
4+
def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
5+
def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
6+
def foo3(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
7+
def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
8+
9+
# output
10+
# flags: --line-ranges=6-1000
11+
# NOTE: If you need to modify this file, pay special attention to the --line-ranges=
12+
# flag above as it's formatting specifically these lines.
13+
def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
14+
def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
15+
def foo3(
16+
parameter_1,
17+
parameter_2,
18+
parameter_3,
19+
parameter_4,
20+
parameter_5,
21+
parameter_6,
22+
parameter_7,
23+
):
24+
pass
25+
26+
27+
def foo4(
28+
parameter_1,
29+
parameter_2,
30+
parameter_3,
31+
parameter_4,
32+
parameter_5,
33+
parameter_6,
34+
parameter_7,
35+
):
36+
pass
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# flags: --line-ranges=5000-6000
2+
# NOTE: If you need to modify this file, pay special attention to the --line-ranges=
3+
# flag above as it's formatting specifically these lines, in this case none.
4+
def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
5+
def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
6+
def foo3(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
7+
def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass

tests/test_ranges.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import pytest
66

7-
from black.ranges import adjusted_lines
7+
from black.ranges import adjusted_lines, sanitized_lines
88

99

1010
@pytest.mark.parametrize(
@@ -183,3 +183,67 @@ def test_diffs(lines: List[Tuple[int, int]], adjusted: List[Tuple[int, int]]) ->
183183
12. # last line changed
184184
"""
185185
assert adjusted == adjusted_lines(lines, original_source, modified_source)
186+
187+
188+
@pytest.mark.parametrize(
189+
"lines,sanitized",
190+
[
191+
(
192+
[(1, 4)],
193+
[(1, 4)],
194+
),
195+
(
196+
[(2, 3)],
197+
[(2, 3)],
198+
),
199+
(
200+
[(2, 10)],
201+
[(2, 4)],
202+
),
203+
(
204+
[(0, 3)],
205+
[(1, 3)],
206+
),
207+
(
208+
[(0, 10)],
209+
[(1, 4)],
210+
),
211+
(
212+
[(-2, 3)],
213+
[(1, 3)],
214+
),
215+
(
216+
[(0, 0)],
217+
[],
218+
),
219+
(
220+
[(-2, -1)],
221+
[],
222+
),
223+
(
224+
[(-1, 0)],
225+
[],
226+
),
227+
(
228+
[(3, 1), (1, 3), (5, 6)],
229+
[(1, 3)],
230+
),
231+
],
232+
)
233+
def test_sanitize(
234+
lines: List[Tuple[int, int]], sanitized: List[Tuple[int, int]]
235+
) -> None:
236+
source = """\
237+
1. import re
238+
2. def func(arg1,
239+
3. arg2, arg3):
240+
4. pass
241+
"""
242+
assert sanitized == sanitized_lines(lines, source)
243+
244+
source_no_trailing_nl = """\
245+
1. import re
246+
2. def func(arg1,
247+
3. arg2, arg3):
248+
4. pass"""
249+
assert sanitized == sanitized_lines(lines, source_no_trailing_nl)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy