Skip to content

Commit

Permalink
Fix for psf#4264
Browse files Browse the repository at this point in the history
  • Loading branch information
sumezulike committed Mar 14, 2024
1 parent 90136fb commit 5f29c7c
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 20 deletions.
12 changes: 10 additions & 2 deletions src/black/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@
parse_ast,
stringify_ast,
)
from black.ranges import adjusted_lines, convert_unchanged_lines, parse_line_ranges
from black.ranges import (
adjusted_lines,
convert_unchanged_lines,
parse_line_ranges,
sanitized_lines,
)
from black.report import Changed, NothingChanged, Report
from black.trans import iter_fexpr_spans
from blib2to3.pgen2 import token
Expand Down Expand Up @@ -1220,7 +1225,10 @@ def f(
hey
"""
lines = adjusted_lines(lines, src_contents, src_contents)
if lines:
lines = sanitized_lines(lines, src_contents)
if not lines:
return src_contents # Nothing to format
dst_contents = _format_str_once(src_contents, mode=mode, lines=lines)
# Forced second pass to work around optional trailing commas (becoming
# forced trailing commas on pass 2) interacting differently with optional
Expand Down
29 changes: 26 additions & 3 deletions src/black/ranges.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,32 @@ def is_valid_line_range(lines: Tuple[int, int]) -> bool:
return not lines or lines[0] <= lines[1]


def sanitized_lines(
lines: Collection[Tuple[int, int]], src_contents: str
) -> Collection[Tuple[int, int]]:
"""Returns the valid line ranges for the given source.
This removes ranges that are entirely outside the valid lines.
Other ranges are normalized so that the start values are at least 1 and the
end values are at most the (1-based) index of the last source line.
"""
if not src_contents:
return []
good_lines = []
src_line_count = len(src_contents.splitlines())
for start, end in lines:
if start > src_line_count:
continue
# line-ranges are 1-based
start = max(start, 1)
if end < start:
continue
end = min(end, src_line_count)
good_lines.append((start, end))
return good_lines


def adjusted_lines(
lines: Collection[Tuple[int, int]],
original_source: str,
Expand Down Expand Up @@ -84,14 +110,11 @@ def adjusted_lines(
"""
lines_mappings = _calculate_lines_mappings(original_source, modified_source)

original_line_count = len(original_source.splitlines())
new_lines = []
# Keep an index of the current search. Since the lines and lines_mappings are
# sorted, this makes the search complexity linear.
current_mapping_index = 0
for start, end in sorted(lines):
if end > original_line_count:
end = original_line_count
start_mapping_index = _find_lines_mapping_index(
start,
lines_mappings,
Expand Down
36 changes: 36 additions & 0 deletions tests/data/cases/line_ranges_exceeding_end.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# flags: --line-ranges=6-1000
# NOTE: If you need to modify this file, pay special attention to the --line-ranges=
# flag above as it's formatting specifically these lines.
def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
def foo3(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass

# output
# flags: --line-ranges=6-1000
# NOTE: If you need to modify this file, pay special attention to the --line-ranges=
# flag above as it's formatting specifically these lines.
def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
def foo3(
parameter_1,
parameter_2,
parameter_3,
parameter_4,
parameter_5,
parameter_6,
parameter_7,
):
pass


def foo4(
parameter_1,
parameter_2,
parameter_3,
parameter_4,
parameter_5,
parameter_6,
parameter_7,
):
pass
41 changes: 41 additions & 0 deletions tests/data/cases/line_ranges_outside_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# flags: --line-ranges=5000-6000
# NOTE: If you need to modify this file, pay special attention to the --line-ranges=
# flag above as it's formatting specifically these lines, in this case none.
def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
def foo3(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass

# Adding some unformated code covering a wide range of syntaxes.

if True:
# Incorrectly indented prefix comments.
pass

import typing
from typing import (
Any ,
)
class MyClass( object): # Trailing comment with extra leading space.
#NOTE: The following indentation is incorrect:
@decor( 1 * 3 )
def my_func( arg):
pass

try: # Trailing comment with extra leading space.
for i in range(10): # Trailing comment with extra leading space.
while condition:
if something:
then_something( )
elif something_else:
then_something_else( )
except ValueError as e:
unformatted( )
finally:
unformatted( )

async def test_async_unformatted( ): # Trailing comment with extra leading space.
async for i in some_iter( unformatted ): # Trailing comment with extra leading space.
await asyncio.sleep( 1 )
async with some_context( unformatted ):
print( "unformatted" )
74 changes: 59 additions & 15 deletions tests/test_ranges.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest

from black.ranges import adjusted_lines
from black.ranges import adjusted_lines, sanitized_lines


@pytest.mark.parametrize(
Expand All @@ -27,10 +27,9 @@ def func():
[(1, 0)],
[(-8, 0)],
[(-8, 8)],
[(1, 100)],
[(2, 1)],
[(0, 8), (3, 1)],
[(12, 13)],
[(100, 200)],
],
)
def test_invalid_lines(lines: List[Tuple[int, int]]) -> None:
Expand Down Expand Up @@ -77,10 +76,6 @@ def func(arg1, arg2, arg3):
[(1, 6)],
[(1, 2)],
),
(
[(1, 100)],
[(1, 2)],
),
(
[(6, 6)],
[],
Expand Down Expand Up @@ -116,10 +111,6 @@ def test_removals(
[(1, 2)],
[(2, 5)],
),
(
[(1, 100)],
[(2, 5)],
),
(
[(2, 2)],
[(5, 5)],
Expand Down Expand Up @@ -155,10 +146,6 @@ def test_additions(
[(1, 12)],
[(1, 11)],
),
(
[(1, 100)],
[(1, 11)],
),
(
[(10, 10)],
[(9, 9)],
Expand Down Expand Up @@ -196,3 +183,60 @@ def test_diffs(lines: List[Tuple[int, int]], adjusted: List[Tuple[int, int]]) ->
12. # last line changed
"""
assert adjusted == adjusted_lines(lines, original_source, modified_source)


@pytest.mark.parametrize(
"lines,sanitized",
[
(
[(1, 4)],
[(1, 4)],
),
(
[(2, 3)],
[(2, 3)],
),
(
[(2, 10)],
[(2, 4)],
),
(
[(0, 3)],
[(1, 3)],
),
(
[(0, 10)],
[(1, 4)],
),
(
[(-2, 3)],
[(1, 3)],
),
(
[(0, 0)],
[],
),
(
[(-2, -1)],
[],
),
(
[(-1, 0)],
[],
),
(
[(3, 1), (1, 3), (5, 6)],
[(1, 3)],
),
],
)
def test_sanitize(
lines: List[Tuple[int, int]], sanitized: List[Tuple[int, int]]
) -> None:
source = """\
1. import re
2. def func(arg1,
3. arg2, arg3):
4. pass
"""
assert sanitized == sanitized_lines(lines, source)

0 comments on commit 5f29c7c

Please sign in to comment.