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

Support named escapes (\N{...}) in string processing #2319

Merged
merged 12 commits into from Jun 9, 2021
50 changes: 50 additions & 0 deletions src/black/trans.py
Expand Up @@ -1243,6 +1243,41 @@ def more_splits_should_be_made() -> bool:
last_line.comments = line.comments.copy()
yield Ok(last_line)

def _get_nameescape_slices(self, string: str) -> List[Tuple[int, int]]:
"""
Returns:
List of all ranges of @string which, if @string were to be split there,
would result in the splitting of an \\N{...} expression (which is NOT
allowed).
"""
slices = []
backslash_count = 0
it = iter(enumerate(string))
for idx, c in it:
if not backslash_count:
if c == "\\":
backslash_count += 1
continue
Jackenmen marked this conversation as resolved.
Show resolved Hide resolved
if c == "\\":
backslash_count += 1
continue
backslash_count = 0
if c != "N":
continue
Jackenmen marked this conversation as resolved.
Show resolved Hide resolved

start = idx - 1 # the position of backslash before \N{...}
for idx, c in it:
if c == "}":
end = idx
break
else:
# malformed nameescape expression?
# should have been detected by AST parsing earlier...
raise RuntimeError(f"{self.__class__.__name__} LOGIC ERROR!")
slices.append((start, end))

return slices

def _get_break_idx(self, string: str, max_break_idx: int) -> Optional[int]:
"""
This method contains the algorithm that StringSplitter uses to
Expand Down Expand Up @@ -1307,6 +1342,20 @@ def breaks_fstring_expression(i: Index) -> bool:

return False

nameescape_slices = self._get_nameescape_slices(string)

def breaks_nameescape_expression(i: Index) -> bool:
"""
Returns:
True iff returning @i would result in the splitting of an
\\N{...} expression (which is NOT allowed).
"""
for (start, end) in nameescape_slices:
Jackenmen marked this conversation as resolved.
Show resolved Hide resolved
if start <= i < end:
return True

return False

def passes_all_checks(i: Index) -> bool:
"""
Returns:
Expand All @@ -1330,6 +1379,7 @@ def passes_all_checks(i: Index) -> bool:
and is_not_escaped
and is_big_enough
and not breaks_fstring_expression(i)
and not breaks_nameescape_expression(i)
)

# First, we check all indices BELOW @max_break_idx.
Expand Down
27 changes: 27 additions & 0 deletions tests/data/long_strings.py
Expand Up @@ -207,6 +207,18 @@ def foo():
" of it."
)

x = (
"........................................................................ \N{LAO KO LA}"
)

x = (
"........................................................................... \N{LAO KO LA}"
)

x = (
"............................................................................ \N{LAO KO LA}"
)

Jackenmen marked this conversation as resolved.
Show resolved Hide resolved

# output

Expand Down Expand Up @@ -587,3 +599,18 @@ def foo():
"This is a really long string that can't be merged because it has a likely pragma at the end" # pylint: disable=some-pylint-check
" of it."
)

x = (
"........................................................................"
" \N{LAO KO LA}"
)

x = (
"..........................................................................."
" \N{LAO KO LA}"
)

x = (
"............................................................................"
" \N{LAO KO LA}"
)
8 changes: 8 additions & 0 deletions tests/data/long_strings__regression.py
Expand Up @@ -514,6 +514,10 @@ async def foo(self):

x = F"This is a long string which contains an f-expr that should not split {{{[i for i in range(5)]}}}."

x = (
"\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}"
)


# output

Expand Down Expand Up @@ -1142,3 +1146,7 @@ async def foo(self):
"This is a long string which contains an f-expr that should not split"
f" {{{[i for i in range(5)]}}}."
)

x = (
"\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}"
)