Repository: ipython/ipython

Issue:
crash of ipython when alias is used with %s and echo
This bug is present in alias of magic commands
In [1]: alias parts echo first %s second %s
In [2]: parts A
  After executing the first line of command when we give command as parts A 
instead of printing
                  "Incorrect number of arguments: 2 expected." ipython automatically crashes.



Discussion:
Replicated in trunk


Resolving patch:
diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py
--- a/IPython/core/interactiveshell.py
+++ b/IPython/core/interactiveshell.py
@@ -38,7 +38,7 @@
 from IPython.core import prefilter
 from IPython.core import shadowns
 from IPython.core import ultratb
-from IPython.core.alias import AliasManager
+from IPython.core.alias import AliasManager, AliasError
 from IPython.core.autocall import ExitAutocall
 from IPython.core.builtin_trap import BuiltinTrap
 from IPython.core.compilerop import CachingCompiler
@@ -2129,9 +2129,18 @@ def run_cell(self, raw_cell, store_history=True):
         cell = self.input_splitter.source_reset()
         
         with self.builtin_trap:
+            prefilter_failed = False
             if len(cell.splitlines()) == 1:
-                cell = self.prefilter_manager.prefilter_lines(cell)
-            
+                try:
+                    cell = self.prefilter_manager.prefilter_lines(cell)
+                except AliasError as e:
+                    error(e)
+                    prefilter_failed=True
+                except Exception:
+                    # don't allow prefilter errors to crash IPython
+                    self.showtraceback()
+                    prefilter_failed = True
+                
             # Store raw and processed history
             if store_history:
                 self.history_manager.store_inputs(self.execution_count, 
@@ -2139,30 +2148,32 @@ def run_cell(self, raw_cell, store_history=True):
 
             self.logger.log(cell, raw_cell)
             
-            cell_name = self.compile.cache(cell, self.execution_count)
+            if not prefilter_failed:
+                # don't run if prefilter failed
+                cell_name = self.compile.cache(cell, self.execution_count)
             
-            with self.display_trap:
-                try:
-                    code_ast = ast.parse(cell, filename=cell_name)
-                except (OverflowError, SyntaxError, ValueError, TypeError,
-                        MemoryError):
-                    self.showsyntaxerror()
-                    self.execution_count += 1
-                    return None
-
-                self.run_ast_nodes(code_ast.body, cell_name,
-                                                    interactivity="last_expr")
-
-                # Execute any registered post-execution functions.
-                for func, status in self._post_execute.iteritems():
-                    if not status:
-                        continue
+                with self.display_trap:
                     try:
-                        func()
-                    except:
-                        self.showtraceback()
-                        # Deactivate failing function
-                        self._post_execute[func] = False
+                        code_ast = ast.parse(cell, filename=cell_name)
+                    except (OverflowError, SyntaxError, ValueError, TypeError,
+                            MemoryError):
+                        self.showsyntaxerror()
+                        self.execution_count += 1
+                        return None
+            
+                    self.run_ast_nodes(code_ast.body, cell_name,
+                                                        interactivity="last_expr")
+            
+                    # Execute any registered post-execution functions.
+                    for func, status in self._post_execute.iteritems():
+                        if not status:
+                            continue
+                        try:
+                            func()
+                        except:
+                            self.showtraceback()
+                            # Deactivate failing function
+                            self._post_execute[func] = False
                 
         if store_history:
             # Write output to the database. Does nothing unless


Test patch:
diff --git a/IPython/core/tests/test_interactiveshell.py b/IPython/core/tests/test_interactiveshell.py
--- a/IPython/core/tests/test_interactiveshell.py
+++ b/IPython/core/tests/test_interactiveshell.py
@@ -20,7 +20,10 @@
 #-----------------------------------------------------------------------------
 # stdlib
 import unittest
+from cStringIO import StringIO
+
 from IPython.testing import decorators as dec
+from IPython.utils import io
 
 #-----------------------------------------------------------------------------
 # Tests
@@ -91,3 +94,16 @@ def test_magic_names_in_string(self):
         ip = get_ipython()
         ip.run_cell('a = """\n%exit\n"""')
         self.assertEquals(ip.user_ns['a'], '\n%exit\n')
+    
+    def test_alias_crash(self):
+        """Errors in prefilter can't crash IPython"""
+        ip = get_ipython()
+        ip.run_cell('%alias parts echo first %s second %s')
+        # capture stderr:
+        save_err = io.stderr
+        io.stderr = StringIO()
+        ip.run_cell('parts 1')
+        err = io.stderr.getvalue()
+        io.stderr = save_err
+        self.assertEquals(err.split(':')[0], 'ERROR')
+