Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a44dc3d

Browse files
authored
Exclude string type annotations from ESP (psf#3462)
1 parent 1e8217f commit a44dc3d

5 files changed

Lines changed: 98 additions & 14 deletions

File tree

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
- Long values in dict literals are now wrapped in parentheses; correspondingly
2727
unnecessary parentheses around short values in dict literals are now removed; long
2828
string lambda values are now wrapped in parentheses (#3440)
29+
- Exclude string type annotations from improved string processing; fix crash when the
30+
return type annotation is stringified and spans across multiple lines (#3462)
2931

3032
### Configuration
3133

src/black/brackets.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,12 @@ def mark(self, leaf: Leaf) -> None:
8080
within brackets a given leaf is. 0 means there are no enclosing brackets
8181
that started on this line.
8282
83-
If a leaf is itself a closing bracket, it receives an `opening_bracket`
84-
field that it forms a pair with. This is a one-directional link to
85-
avoid reference cycles.
83+
If a leaf is itself a closing bracket and there is a matching opening
84+
bracket earlier, it receives an `opening_bracket` field with which it forms a
85+
pair. This is a one-directional link to avoid reference cycles. Closing
86+
bracket without opening happens on lines continued from previous
87+
breaks, e.g. `) -> "ReturnType":` as part of a funcdef where we place
88+
the return type annotation on its own line of the previous closing RPAR.
8689
8790
If a leaf is a delimiter (a token on which Black can split the line if
8891
needed) and it's on depth 0, its `id()` is stored in the tracker's
@@ -91,6 +94,13 @@ def mark(self, leaf: Leaf) -> None:
9194
if leaf.type == token.COMMENT:
9295
return
9396

97+
if (
98+
self.depth == 0
99+
and leaf.type in CLOSING_BRACKETS
100+
and (self.depth, leaf.type) not in self.bracket_match
101+
):
102+
return
103+
94104
self.maybe_decrement_after_for_loop_variable(leaf)
95105
self.maybe_decrement_after_lambda_arguments(leaf)
96106
if leaf.type in CLOSING_BRACKETS:

src/black/nodes.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,3 +848,15 @@ def is_string_token(nl: NL) -> TypeGuard[Leaf]:
848848

849849
def is_number_token(nl: NL) -> TypeGuard[Leaf]:
850850
return nl.type == token.NUMBER
851+
852+
853+
def is_part_of_annotation(leaf: Leaf) -> bool:
854+
"""Returns whether this leaf is part of type annotations."""
855+
ancestor = leaf.parent
856+
while ancestor is not None:
857+
if ancestor.prev_sibling and ancestor.prev_sibling.type == token.RARROW:
858+
return True
859+
if ancestor.parent and ancestor.parent.type == syms.tname:
860+
return True
861+
ancestor = ancestor.parent
862+
return False

src/black/trans.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030

3131
from mypy_extensions import trait
3232

33-
from black.brackets import BracketMatchError
3433
from black.comments import contains_pragma_comment
3534
from black.lines import Line, append_leaves
3635
from black.mode import Feature
@@ -41,6 +40,7 @@
4140
is_empty_lpar,
4241
is_empty_par,
4342
is_empty_rpar,
43+
is_part_of_annotation,
4444
parent_type,
4545
replace_child,
4646
syms,
@@ -351,7 +351,7 @@ class StringMerger(StringTransformer, CustomSplitMapMixin):
351351
352352
Requirements:
353353
(A) The line contains adjacent strings such that ALL of the validation checks
354-
listed in StringMerger.__validate_msg(...)'s docstring pass.
354+
listed in StringMerger._validate_msg(...)'s docstring pass.
355355
OR
356356
(B) The line contains a string which uses line continuation backslashes.
357357
@@ -377,6 +377,8 @@ def do_match(self, line: Line) -> TMatchResult:
377377
and is_valid_index(i + 1)
378378
and LL[i + 1].type == token.STRING
379379
):
380+
if is_part_of_annotation(leaf):
381+
return TErr("String is part of type annotation.")
380382
return Ok(i)
381383

382384
if leaf.type == token.STRING and "\\\n" in leaf.value:
@@ -454,7 +456,7 @@ def _merge_string_group(self, line: Line, string_idx: int) -> TResult[Line]:
454456
455457
Returns:
456458
Ok(new_line), if ALL of the validation checks found in
457-
__validate_msg(...) pass.
459+
_validate_msg(...) pass.
458460
OR
459461
Err(CannotTransform), otherwise.
460462
"""
@@ -608,7 +610,7 @@ def make_naked(string: str, string_prefix: str) -> str:
608610
def _validate_msg(line: Line, string_idx: int) -> TResult[None]:
609611
"""Validate (M)erge (S)tring (G)roup
610612
611-
Transform-time string validation logic for __merge_string_group(...).
613+
Transform-time string validation logic for _merge_string_group(...).
612614
613615
Returns:
614616
* Ok(None), if ALL validation checks (listed below) pass.
@@ -622,6 +624,11 @@ def _validate_msg(line: Line, string_idx: int) -> TResult[None]:
622624
- The set of all string prefixes in the string group is of
623625
length greater than one and is not equal to {"", "f"}.
624626
- The string group consists of raw strings.
627+
- The string group is stringified type annotations. We don't want to
628+
process stringified type annotations since pyright doesn't support
629+
them spanning multiple string values. (NOTE: mypy, pytype, pyre do
630+
support them, so we can change if pyright also gains support in the
631+
future. See https://github.com/microsoft/pyright/issues/4359.)
625632
"""
626633
# We first check for "inner" stand-alone comments (i.e. stand-alone
627634
# comments that have a string leaf before them AND after them).
@@ -812,13 +819,7 @@ def do_transform(self, line: Line, string_idx: int) -> Iterator[TResult[Line]]:
812819

813820
new_line = line.clone()
814821
new_line.comments = line.comments.copy()
815-
try:
816-
append_leaves(new_line, line, LL[: string_idx - 1])
817-
except BracketMatchError:
818-
# HACK: I believe there is currently a bug somewhere in
819-
# right_hand_split() that is causing brackets to not be tracked
820-
# properly by a shared BracketTracker.
821-
append_leaves(new_line, line, LL[: string_idx - 1], preformatted=True)
822+
append_leaves(new_line, line, LL[: string_idx - 1])
822823

823824
string_leaf = Leaf(token.STRING, LL[string_idx].value)
824825
LL[string_idx - 1].remove()
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
def func(
2+
arg1,
3+
arg2,
4+
) -> Set["this_is_a_very_long_module_name.AndAVeryLongClasName"
5+
".WithAVeryVeryVeryVeryVeryLongSubClassName"]:
6+
pass
7+
8+
9+
def func(
10+
argument: (
11+
"VeryLongClassNameWithAwkwardGenericSubtype[int] |"
12+
"VeryLongClassNameWithAwkwardGenericSubtype[str]"
13+
),
14+
) -> (
15+
"VeryLongClassNameWithAwkwardGenericSubtype[int] |"
16+
"VeryLongClassNameWithAwkwardGenericSubtype[str]"
17+
):
18+
pass
19+
20+
21+
def func(
22+
argument: (
23+
"int |"
24+
"str"
25+
),
26+
) -> Set["int |"
27+
" str"]:
28+
pass
29+
30+
31+
# output
32+
33+
34+
def func(
35+
arg1,
36+
arg2,
37+
) -> Set[
38+
"this_is_a_very_long_module_name.AndAVeryLongClasName"
39+
".WithAVeryVeryVeryVeryVeryLongSubClassName"
40+
]:
41+
pass
42+
43+
44+
def func(
45+
argument: (
46+
"VeryLongClassNameWithAwkwardGenericSubtype[int] |"
47+
"VeryLongClassNameWithAwkwardGenericSubtype[str]"
48+
),
49+
) -> (
50+
"VeryLongClassNameWithAwkwardGenericSubtype[int] |"
51+
"VeryLongClassNameWithAwkwardGenericSubtype[str]"
52+
):
53+
pass
54+
55+
56+
def func(
57+
argument: ("int |" "str"),
58+
) -> Set["int |" " str"]:
59+
pass

0 commit comments

Comments
 (0)