diff --git a/sqlparse/filters/others.py b/sqlparse/filters/others.py index 95bc436c..109d44c2 100644 --- a/sqlparse/filters/others.py +++ b/sqlparse/filters/others.py @@ -112,11 +112,16 @@ def _stripws_identifierlist(self, tlist): return self._stripws_default(tlist) def _stripws_parenthesis(self, tlist): - while tlist.tokens[1].is_whitespace: + # A well-formed parenthesis has an opening and closing token plus the + # content in between, so tokens[1]/tokens[-2] address the inner edges. + # Degenerate groups can hold fewer tokens (e.g. "(::)" is grouped as a + # single Identifier), leaving nothing to strip; guard against indexing + # past the ends in that case. + while len(tlist.tokens) > 2 and tlist.tokens[1].is_whitespace: tlist.tokens.pop(1) - while tlist.tokens[-2].is_whitespace: + while len(tlist.tokens) > 2 and tlist.tokens[-2].is_whitespace: tlist.tokens.pop(-2) - if tlist.tokens[-2].is_group: + if len(tlist.tokens) > 2 and tlist.tokens[-2].is_group: # save to remove the last whitespace while tlist.tokens[-2].tokens[-1].is_whitespace: tlist.tokens[-2].tokens.pop(-1) diff --git a/tests/test_format.py b/tests/test_format.py index 0cdbcf88..52e57df0 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -784,3 +784,14 @@ def test_strip_ws_removes_trailing_ws_in_groups(): # issue782 strip_whitespace=True) expected = '(where foo = bar) from' assert formatted == expected + + +def test_strip_ws_degenerate_parenthesis_group(): + # "(::)" is grouped as a single Identifier, so the enclosing Parenthesis + # holds too few tokens for the whitespace stripper to index its inner + # edges. Formatting must not raise IndexError. + assert sqlparse.format('(::)', strip_whitespace=True) == '(::)' + assert sqlparse.format('( :: )', reindent=True) == '( :: )' + assert sqlparse.format('count(::)', strip_whitespace=True) == 'count(::)' + # Normal parentheses still have their inner whitespace stripped. + assert sqlparse.format('( a )', strip_whitespace=True) == '(a)'