diff --git a/IPython/core/tests/test_application.py b/IPython/core/tests/test_application.py index 8ef97f3..dea26ea 100644 --- a/IPython/core/tests/test_application.py +++ b/IPython/core/tests/test_application.py @@ -3,9 +3,13 @@ import os import tempfile +import shutil + +import nose.tools as nt from IPython.core.application import BaseIPythonApplication from IPython.testing import decorators as dec +from IPython.testing.tools import make_tempfile, ipexec from IPython.utils import py3compat @dec.onlyif_unicode_paths @@ -48,3 +52,46 @@ def test_unicode_ipdir(): os.environ["IPYTHONDIR"] = old_ipdir1 if old_ipdir2: os.environ["IPYTHONDIR"] = old_ipdir2 + + + +TEST_SYNTAX_ERROR_CMDS = """ +from IPython.core.inputtransformer import InputTransformer + +%cpaste +class SyntaxErrorTransformer(InputTransformer): + + def push(self, line): + if 'syntaxerror' in line: + raise SyntaxError('in input '+line) + return line + + def reset(self): + pass +-- + +ip = get_ipython() +transformer = SyntaxErrorTransformer() +ip.input_splitter.python_line_transforms.append(transformer) +ip.input_transformer_manager.python_line_transforms.append(transformer) + +# now the actual commands +1234 +2345 # syntaxerror <- triggered here +3456 +""" + +def test_syntax_error(): + """Check that IPython does not abort if a SyntaxError is raised in an InputTransformer""" + try: + tmp = tempfile.mkdtemp() + filename = os.path.join(tmp, 'test_syntax_error.py') + with open(filename, 'w') as f: + f.write(TEST_SYNTAX_ERROR_CMDS) + out, err = ipexec(filename, pipe=True) + nt.assert_equal(err, '') + nt.assert_in('1234', out) + nt.assert_in('SyntaxError: in input 2345 # syntaxerror <- triggered here', out) + nt.assert_in('3456', out) + finally: + shutil.rmtree(tmp) diff --git a/IPython/terminal/interactiveshell.py b/IPython/terminal/interactiveshell.py index f281dc2..21398af 100644 --- a/IPython/terminal/interactiveshell.py +++ b/IPython/terminal/interactiveshell.py @@ -541,8 +541,13 @@ class TerminalInteractiveShell(InteractiveShell): # asynchronously by signal handlers, for example. self.showtraceback() else: - self.input_splitter.push(line) - more = self.input_splitter.push_accepts_more() + try: + self.input_splitter.push(line) + more = self.input_splitter.push_accepts_more() + except SyntaxError: + self.showsyntaxerror() + self.input_splitter.reset() + more = False if (self.SyntaxTB.last_syntax_error and self.autoedit_syntax): self.edit_syntax_error() diff --git a/IPython/testing/tools.py b/IPython/testing/tools.py index d17ce42..aa63b5f 100644 --- a/IPython/testing/tools.py +++ b/IPython/testing/tools.py @@ -173,7 +173,7 @@ def get_ipython_cmd(as_string=False): return ipython_cmd -def ipexec(fname, options=None): +def ipexec(fname, options=None, pipe=False): """Utility to call 'ipython filename'. Starts IPython with a minimal and safe configuration to make startup as fast @@ -189,6 +189,9 @@ def ipexec(fname, options=None): options : optional, list Extra command-line flags to be passed to IPython. + pipe : optional, boolean + Pipe fname into IPython as stdin instead of calling it as external file + Returns ------- (stdout, stderr) of ipython subprocess. @@ -208,8 +211,12 @@ def ipexec(fname, options=None): ipython_cmd = get_ipython_cmd() # Absolute path for filename full_fname = os.path.join(test_dir, fname) - full_cmd = ipython_cmd + cmdargs + [full_fname] - p = Popen(full_cmd, stdout=PIPE, stderr=PIPE) + if pipe: + full_cmd = ipython_cmd + cmdargs + p = Popen(full_cmd, stdin=open(full_fname), stdout=PIPE, stderr=PIPE) + else: + full_cmd = ipython_cmd + cmdargs + [full_fname] + p = Popen(full_cmd, stdout=PIPE, stderr=PIPE) out, err = p.communicate() out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err) # `import readline` causes 'ESC[?1034h' to be output sometimes,