diff --git a/src/black/__init__.py b/src/black/__init__.py index 64a18655905..f0920b5a26c 100644 --- a/src/black/__init__.py +++ b/src/black/__init__.py @@ -112,6 +112,10 @@ class InvalidInput(ValueError): """Raised when input source code fails all parse attempts.""" +class BracketMatchError(KeyError): + """Raised when an opening bracket is unable to be matched to a closing bracket.""" + + T = TypeVar("T") E = TypeVar("E", bound=Exception) @@ -1308,7 +1312,13 @@ def mark(self, leaf: Leaf) -> None: self.maybe_decrement_after_lambda_arguments(leaf) if leaf.type in CLOSING_BRACKETS: self.depth -= 1 - opening_bracket = self.bracket_match.pop((self.depth, leaf.type)) + try: + opening_bracket = self.bracket_match.pop((self.depth, leaf.type)) + except KeyError as e: + raise BracketMatchError( + "Unable to match a closing bracket to the following opening" + f" bracket: {leaf}" + ) from e leaf.opening_bracket = opening_bracket if not leaf.value: self.invisible.append(leaf) @@ -3306,10 +3316,26 @@ def do_transform(self, line: Line, string_idx: int) -> Iterator[TResult[Line]]: yield TErr( "Will not strip parentheses which have comments attached to them." ) + return new_line = line.clone() new_line.comments = line.comments.copy() - append_leaves(new_line, line, LL[: string_idx - 1]) + try: + append_leaves(new_line, line, LL[: string_idx - 1]) + except BracketMatchError as e: + # HACK: I believe there is currently a bug somewhere in + # right_hand_split() that is causing brackets to not be tracked + # properly by a shared BracketTracker. It is probably fine to just + # ignore this event using the `preformatted=True` keyword argument, + # but to be safe we abort this transformer when this happens. + cant_transform = CannotTransform( + "While attempting to strip parens, we were unable to match one of the" + " other brackets (i.e. NOT one of the target parens) in this line to" + " its corresponding opening/closing bracket." + ) + cant_transform.__cause__ = e + yield Err(cant_transform) + return string_leaf = Leaf(token.STRING, LL[string_idx].value) LL[string_idx - 1].remove() diff --git a/tests/data/long_strings__regression.py b/tests/data/long_strings__regression.py index 044bb4a5deb..823879c729d 100644 --- a/tests/data/long_strings__regression.py +++ b/tests/data/long_strings__regression.py @@ -310,6 +310,25 @@ def who(self): passenger_association=passenger_association, ) +def A(): + def B(): + def C(): + def D(): + def E(): + def F(): + def G(): + assert ( + c_float(val[0][0] / val[0][1]).value + == c_float(value[0][0] / value[0][1]).value + ), "%s didn't roundtrip" % tag + +class xxxxxxxxxxxxxxxxxxxxx(xxxx.xxxxxxxxxxxxx): + def xxxxxxx_xxxxxx(xxxx): + assert xxxxxxx_xxxx in [ + x.xxxxx.xxxxxx.xxxxx.xxxxxx, + x.xxxxx.xxxxxx.xxxxx.xxxx, + ], ("xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx) + # output @@ -702,3 +721,26 @@ def who(self): passenger_association=passenger_association, ) ) + + +def A(): + def B(): + def C(): + def D(): + def E(): + def F(): + def G(): + assert ( + c_float(val[0][0] / val[0][1]).value + == c_float(value[0][0] / value[0][1]).value + ), ("%s didn't roundtrip" % tag) + + +class xxxxxxxxxxxxxxxxxxxxx(xxxx.xxxxxxxxxxxxx): + def xxxxxxx_xxxxxx(xxxx): + assert xxxxxxx_xxxx in [ + x.xxxxx.xxxxxx.xxxxx.xxxxxx, + x.xxxxx.xxxxxx.xxxxx.xxxx, + ], ( + "xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx + )