Repository: ipython/ipython

Issue:
%paste still bugs about IndentationError and says to use %paste
``` py
In [8]: %paste
 sum([1, 2,
3, 4])
## -- End pasted text --
IndentationError: unexpected indent

If you want to paste code into IPython, try the %paste and %cpaste magic functions.
```

The IndentationError is legitimate, but it shouldn't tell me to use %paste when I already am.



Discussion:
The answer here would be to restore the original InteractiveShell.showindentationerror around `run_cell` in `magic_*paste`.

Hi,

Do you think this might be a good intro issue? Where do you think I should start?

Hi @mrshu . Yes, this is probably a decent thing to start with.

The main shell class is InteractiveShell from [IPython.core.interactiveshell](https://github.com/ipython/ipython/blob/master/IPython/core/interactiveshell.py). That's subclassed by TerminalInteractiveShell from [IPython.frontend.terminal.interactiveshell](https://github.com/ipython/ipython/blob/master/IPython/frontend/terminal/interactiveshell.py), which adds the `%paste` and `%cpaste` magics, and overrides `showindentationerror` to display the message about pasting input. As @minrk suggests, we should fall back to the default implementation when code is run through the paste magics.

Have a look at the relevant code in those two files, and feel free to ask questions here or on the mailing list. When you've got something that seems to work, submit a pull request to get feedback on it.

@takluyver Thanks for your response!

I have already looked at the code but kind of failed to find a way of detecting when is the code run through the paste magics. Is there any?

It sounds like you're thinking of something cleverer than we need ;-) . The magic functions are defined starting from [this line](https://github.com/ipython/ipython/blob/master/IPython/frontend/terminal/interactiveshell.py#L184). You can just make the substitution before they run the code - perhaps in the `store_or_execute` function that they both use.

Could be, that's what I'm famous for =)

One last question before actually posting some code: how should the actual substitution look like? I have to monkey patch things.

Thanks!

Well, I can see two ways to do it:
- Simply set the `showindentationerror` attribute on the shell to the parent method (you may need to fiddle with super()). We do a similar thing for `shell.system` [here](https://github.com/ipython/ipython/blob/master/IPython/frontend/terminal/interactiveshell.py#L363).
- Have a boolean flag, which the implementation of `showindentationerror` checks to decide whether to print its extra message. Then `store_or_execute` can toggle the flag before & after running code.

They have different benefits. I marginally prefer the second way - I think it will be easier to see what's going on.


Resolving patch:
diff --git a/IPython/frontend/terminal/interactiveshell.py b/IPython/frontend/terminal/interactiveshell.py
--- a/IPython/frontend/terminal/interactiveshell.py
+++ b/IPython/frontend/terminal/interactiveshell.py
@@ -150,6 +150,7 @@ def cleanup_input(self, block):
     def store_or_execute(self, block, name):
         """ Execute a block, or store it in a variable, per the user's request.
         """
+
         b = self.cleanup_input(block)
         if name:
             # If storing it for further editing
@@ -157,7 +158,11 @@ def store_or_execute(self, block, name):
             print("Block assigned to '%s'" % name)
         else:
             self.shell.user_ns['pasted_block'] = b
-            self.shell.run_cell(b)
+            self.shell.using_paste_magics = True
+            try:
+                self.shell.run_cell(b)
+            finally:
+                self.shell.using_paste_magics = False
 
     def rerun_pasted(self, name='pasted_block'):
         """ Rerun a previously pasted command.
@@ -347,6 +352,10 @@ class TerminalInteractiveShell(InteractiveShell):
     term_title = CBool(False, config=True,
         help="Enable auto setting the terminal title."
     )
+    
+    # This `using_paste_magics` is used to detect whether the code is being
+    # executed via paste magics functions
+    using_paste_magics = CBool(False)
 
     # In the terminal, GUI control is done via PyOS_InputHook
     from IPython.lib.inputhook import enable_gui
@@ -721,8 +730,9 @@ def init_magics(self):
 
     def showindentationerror(self):
         super(TerminalInteractiveShell, self).showindentationerror()
-        print("If you want to paste code into IPython, try the "
-              "%paste and %cpaste magic functions.")
+        if not self.using_paste_magics:
+            print("If you want to paste code into IPython, try the "
+                "%paste and %cpaste magic functions.")
 
 
 InteractiveShellABC.register(TerminalInteractiveShell)


Test patch:
diff --git a/IPython/frontend/terminal/tests/test_interactivshell.py b/IPython/frontend/terminal/tests/test_interactivshell.py
--- a/IPython/frontend/terminal/tests/test_interactivshell.py
+++ b/IPython/frontend/terminal/tests/test_interactivshell.py
@@ -21,6 +21,7 @@
 
 from IPython.testing.decorators import skipif
 from IPython.utils import py3compat
+from IPython.testing import tools as tt
 
 class InteractiveShellTestCase(unittest.TestCase):
     def rl_hist_entries(self, rl, n):
@@ -169,3 +170,20 @@ def test_replace_multiline_hist_replaces_empty_line(self):
         enc = sys.stdin.encoding or "utf-8"
         expected = [ py3compat.unicode_to_str(e, enc) for e in expected ]
         self.assertEqual(hist, expected)
+    
+    def test_paste_magics_message(self):
+        """Test that an IndentationError while using paste magics doesn't
+        trigger a message about paste magics and also the opposite."""
+
+        ip = get_ipython()
+        s = ''' sum([1, 2,
+3, 4])'''
+
+        tm = ip.magics_manager.registry['TerminalMagics']
+        with tt.AssertPrints("If you want to paste code into IPython, try the "
+                "%paste and %cpaste magic functions."):
+            ip.run_cell(s)
+
+        with tt.AssertNotPrints("If you want to paste code into IPython, try the "
+                "%paste and %cpaste magic functions."):
+            tm.store_or_execute(s, name=None)