From 03d77d1eeef335263ac4fe4ccceee32aa0f93f1d 2012-05-26 03:28:25 From: Fernando Perez Date: 2012-05-26 03:28:25 Subject: [PATCH] Add a few more fixes to cell/line input code, switch approaches. The pure regexp approach failed in more complex cases, so I've gone for a hybrid with some manual string logic and also regex. There are a lot more tests now that pass, so I'm starting to trust this code. --- diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index 4750bab..74833a0 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -141,8 +141,6 @@ def num_ini_spaces(s): else: return 0 -last_blank_re = re.compile(r'^.*\n\s+$', re.MULTILINE) - def last_blank(src): """Determine if the input source ends in a blank. @@ -153,11 +151,13 @@ def last_blank(src): src : string A single or multiline string. """ - return src == '\n' or bool(last_blank_re.match(src)) + if not src: return False + ll = src.splitlines()[-1] + return (ll == '') or ll.isspace() -last_two_blanks_re = re.compile(r'^\n\s*\n\s*$', re.MULTILINE) -last_two_blanks_re2 = re.compile(r'^.+\n\s*\n\s+$', re.MULTILINE) +last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE) +last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE) def last_two_blanks(src): """Determine if the input source ends in two blanks. @@ -169,8 +169,17 @@ def last_two_blanks(src): src : string A single or multiline string. """ - return (bool(last_two_blanks_re.match(src)) or - bool(last_two_blanks_re2.match(src)) ) + if not src: return False + # The logic here is tricky: I couldn't get a regexp to work and pass all + # the tests, so I took a different approach: split the source by lines, + # grab the last two and prepend '###\n' as a stand-in for whatever was in + # the body before the last two lines. Then, with that structure, it's + # possible to analyze with two regexps. Not the most elegant solution, but + # it works. If anyone tries to change this logic, make sure to validate + # the whole test suite first! + new_src = '\n'.join(['###\n'] + src.splitlines()[-2:]) + return (bool(last_two_blanks_re.match(new_src)) or + bool(last_two_blanks_re2.match(new_src)) ) def remove_comments(src): @@ -786,6 +795,7 @@ class IPythonInputSplitter(InputSplitter): return False if self.cell_magic_mode: + #print('c2 lines', repr(lines)) # dbg # Find out if the last stored block has a whitespace line as its # last line and also this line is whitespace, case in which we're # done (two contiguous blank lines signal termination). Note that @@ -842,7 +852,7 @@ class IPythonInputSplitter(InputSplitter): This means that we get the entire cell with each call. Between resets, the calls simply add more text to the input.""" - + print('lines', repr(lines)) # dbg if lines.startswith('%%'): # Cell magics bypass all further transformations self.cell_magic_mode = True @@ -859,6 +869,7 @@ class IPythonInputSplitter(InputSplitter): self._store(tlines) self._store(lines, self._buffer_raw, 'source_raw') self._is_complete = last_two_blanks(lines) + print('IC', self._is_complete) # dbg return self._is_complete lines_list = lines.splitlines() diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index e369289..0fd1b11 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -609,6 +609,9 @@ def test_last_blank(): nt.assert_true(isp.last_blank('\n ')) nt.assert_true(isp.last_blank('abc\n ')) nt.assert_true(isp.last_blank('abc\n\n')) + nt.assert_true(isp.last_blank('abc\nd\n\n')) + nt.assert_true(isp.last_blank('abc\nd\ne\n\n')) + nt.assert_true(isp.last_blank('abc \n \n \n\n')) def test_last_two_blanks(): @@ -627,6 +630,8 @@ def test_last_two_blanks(): nt.assert_true(isp.last_two_blanks('abc\n\n \n')) nt.assert_true(isp.last_two_blanks('abc\n\n \n ')) nt.assert_true(isp.last_two_blanks('abc\n\n \n \n')) + nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n')) + nt.assert_true(isp.last_two_blanks('abc\nd\ns\nds\n\n\n')) def test_cell_magics_line_mode():