From 16c20a2045aa0759088ae57c7d8d3e3671ab5a3d 2021-03-14 00:21:54 From: Blazej Michalik Date: 2021-03-14 00:21:54 Subject: [PATCH] Bail on singleline cells with sunken brackets Sunken bracket is a closing bracket character that is put before its opening counterpart. In IPython, this leads to unnecessary annoyances, where a multiline prompt is opened for seemingly no reason at all, e.g. if a line contains a ']]' mistyped brackets. --- diff --git a/IPython/core/inputtransformer2.py b/IPython/core/inputtransformer2.py index 44b44de..a5229ae 100644 --- a/IPython/core/inputtransformer2.py +++ b/IPython/core/inputtransformer2.py @@ -508,6 +508,20 @@ def make_tokens_by_line(lines:List[str]): return tokens_by_line + +def has_sunken_brackets(tokens: List[tokenize.TokenInfo]): + """Check if the depth of brackets in the list of tokens drops below 0""" + parenlev = 0 + for token in tokens: + if token.string in {"(", "[", "{"}: + parenlev += 1 + elif token.string in {")", "]", "}"}: + parenlev -= 1 + if parenlev < 0: + return True + return False + + def show_linewise_tokens(s: str): """For investigation and debugging""" if not s.endswith('\n'): @@ -662,6 +676,11 @@ class TransformerManager: tokens_by_line = make_tokens_by_line(lines) + # Bail if we got one line and there are more closing parentheses than + # the opening ones + if len(lines) == 1 and has_sunken_brackets(tokens_by_line[0]): + return "invalid", None + if not tokens_by_line: return 'incomplete', find_last_indent(lines) diff --git a/IPython/core/tests/test_inputtransformer2.py b/IPython/core/tests/test_inputtransformer2.py index 61f183c..f5d72b8 100644 --- a/IPython/core/tests/test_inputtransformer2.py +++ b/IPython/core/tests/test_inputtransformer2.py @@ -297,6 +297,24 @@ def test_check_complete_II(): nt.assert_equal(cc('''def foo():\n """'''), ('incomplete', 4)) +def test_check_complete_invalidates_sunken_brackets(): + """ + Test that a single line with more closing brackets than the opening ones is + interpretted as invalid + """ + cc = ipt2.TransformerManager().check_complete + nt.assert_equal(cc(")"), ("invalid", None)) + nt.assert_equal(cc("]"), ("invalid", None)) + nt.assert_equal(cc("}"), ("invalid", None)) + nt.assert_equal(cc(")("), ("invalid", None)) + nt.assert_equal(cc("]["), ("invalid", None)) + nt.assert_equal(cc("}{"), ("invalid", None)) + nt.assert_equal(cc("[()("), ("invalid", None)) + nt.assert_equal(cc("())("), ("invalid", None)) + nt.assert_equal(cc(")[]("), ("invalid", None)) + nt.assert_equal(cc("()]("), ("invalid", None)) + + def test_null_cleanup_transformer(): manager = ipt2.TransformerManager() manager.cleanup_transforms.insert(0, null_cleanup_transformer)