Repository: ipython/ipython

Issue:
Show line number for IndentationErrors
This is a feature request.

When I execute a script with an IndentationError python and ipython show me the line where the problem is.
Would it be possible to also give show the line where the IndentationError occurs when I execute a multi-line code snippet in ipython via a notebook or qtconsole cell or on the command line via %paste?

I just copy & pasted and executed a 20-line code snippet and got a SyntaxError and couldn't figure out in the notebook where the problem was.

Here's an example:

```
$ cat test.py 
a = 42
if a == 42:
    #print 'Yes'
else:
    print 'No'
```

Python shows the line number and text from the correct line:

```
$ python test.py 
  File "test.py", line 4
    else:
       ^
IndentationError: expected an indented block
```

ipython mentions the line number, but the text it shows me is useless:

```
In [1]: run test.py
---------------------------------------------------------------------------
IndentationError                          Traceback (most recent call last)
/Users/deil/Library/Python/2.7/lib/python/site-packages/IPython/utils/py3compat.pyc in execfile(fname, *where)
    177             else:
    178                 filename = fname
--> 179             __builtin__.execfile(filename, *where)

IndentationError: expected an indented block (test.py, line 4)
```

Copy and paste that code in a notebook cell and execute is and you'll get no line info.
Executing a cell with this content

```
a = 42
if a == 42:
    1 / 0
else:
    print 'No'
```

gives a ZeroDivisionError in the notebook with correct line number info:

```
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-33-09fdeff38d22> in <module>()
      1 a = 42
      2 if a == 42:
----> 3     1 / 0
      4 else:
      5     print 'No'

ZeroDivisionError: integer division or modulo by zero
```

So is it possible to give equally good line number info and show the text context info for SyntaxErrors?



Discussion:
General SyntaxErrors already show the relevant context, at least in my tests. I don't know why `IndentationError` is behaving differently. I'll look into it.


Resolving patch:
diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py
--- a/IPython/core/interactiveshell.py
+++ b/IPython/core/interactiveshell.py
@@ -1716,7 +1716,7 @@ def showtraceback(self,exc_tuple = None,filename=None,tb_offset=None,
                 self.write_err('No traceback available to show.\n')
                 return
             
-            if etype is SyntaxError:
+            if issubclass(etype, SyntaxError):
                 # Though this won't be called by syntax errors in the input
                 # line, there may be SyntaxError cases with imported code.
                 self.showsyntaxerror(filename)
@@ -1769,7 +1769,7 @@ def showsyntaxerror(self, filename=None):
         """
         etype, value, last_traceback = self._get_exc_info()
 
-        if filename and etype is SyntaxError:
+        if filename and issubclass(etype, SyntaxError):
             try:
                 value.filename = filename
             except:
diff --git a/IPython/core/ultratb.py b/IPython/core/ultratb.py
--- a/IPython/core/ultratb.py
+++ b/IPython/core/ultratb.py
@@ -552,7 +552,7 @@ def _format_exception_only(self, etype, value):
             # Not sure if this can still happen in Python 2.6 and above
             list.append( py3compat.cast_unicode(stype) + '\n')
         else:
-            if etype is SyntaxError:
+            if issubclass(etype, SyntaxError):
                 have_filedata = True
                 #print 'filename is',filename  # dbg
                 if not value.filename: value.filename = "<string>"


Test patch:
diff --git a/IPython/core/tests/test_ultratb.py b/IPython/core/tests/test_ultratb.py
--- a/IPython/core/tests/test_ultratb.py
+++ b/IPython/core/tests/test_ultratb.py
@@ -73,3 +73,23 @@ def test_iso8859_5(self):
             with tt.AssertPrints("ZeroDivisionError"):
                 with tt.AssertPrints(u'дбИЖ', suppress=False):
                     ip.run_cell('fail()')
+
+indentationerror_file = """if True:
+zoon()
+"""
+
+class IndentationErrorTest(unittest.TestCase):
+    def test_indentationerror_shows_line(self):
+        # See issue gh-2398
+        with tt.AssertPrints("IndentationError"):
+            with tt.AssertPrints("zoon()", suppress=False):
+                ip.run_cell(indentationerror_file)
+        
+        with TemporaryDirectory() as td:
+            fname = os.path.join(td, "foo.py")
+            with open(fname, "w") as f:
+                f.write(indentationerror_file)
+            
+            with tt.AssertPrints("IndentationError"):
+                with tt.AssertPrints("zoon()", suppress=False):
+                    ip.magic('run %s' % fname)