Repository: ipython/ipython

Issue:
Problem with indentation in the `run_cell` method
Hi,

When using the InteractiveShell method `run_cell`, there is a problem when the string to execute begins with a line that contains only 4 spaces. It then does not handle properly  the indentations of the subsequent loops.
![image](https://user-images.githubusercontent.com/3619520/52297665-21969400-2936-11e9-800e-95262a98436d.png)


Tested on JupyterNotebook, JupyterLab and the Ipython interactive shell in the terminal.

Config:
- jupyter --version 4.4.0
- ipython --version 7.0.1
- python3 --version Python 3.6.7
- Ubuntu 18.04.1 LTS


Discussion:
Yes, the IPython machinery is attempting to detect leading indent and remove them. This is to handle text that might be copy-pasted from other source (like the web) that may include leading indent. 

It will also automatically strip leading `>>>`, `...`, `... :` and `In [x]` prompt. 
How about trimming based on the line with the least amount of leading whitespace? Or at least ignoring whitespace-only lines? Seems like that would be less error-prone.
There are a lot of historical reason for the current behavior; one of them is that often when copy-pasting from the web the first line does not have the same amount of whitespace than the others. So looking at the line with the least WS may not work. 

I believe it make sens to ignore the first line if it's only whitespace – ignoring all line with only WS may work as well. I'd be happy to look at a PR that does that. 

Resolving patch:
diff --git a/IPython/core/inputtransformer2.py b/IPython/core/inputtransformer2.py
--- a/IPython/core/inputtransformer2.py
+++ b/IPython/core/inputtransformer2.py
@@ -18,6 +18,19 @@
 
 _indent_re = re.compile(r'^[ \t]+')
 
+def leading_empty_lines(lines):
+    """Remove leading empty lines
+
+    If the leading lines are empty or contain only whitespace, they will be
+    removed.
+    """
+    if not lines:
+        return lines
+    for i, line in enumerate(lines):
+        if line and not line.isspace():
+            return lines[i:]
+    return lines
+
 def leading_indent(lines):
     """Remove leading indentation.
 
@@ -510,6 +523,7 @@ class TransformerManager:
     """
     def __init__(self):
         self.cleanup_transforms = [
+            leading_empty_lines,
             leading_indent,
             classic_prompt,
             ipython_prompt,


Test patch:
diff --git a/IPython/core/tests/test_inputtransformer2_line.py b/IPython/core/tests/test_inputtransformer2_line.py
--- a/IPython/core/tests/test_inputtransformer2_line.py
+++ b/IPython/core/tests/test_inputtransformer2_line.py
@@ -86,3 +86,31 @@ def test_leading_indent():
     for sample, expected in [INDENT_SPACES, INDENT_TABS]:
         nt.assert_equal(ipt2.leading_indent(sample.splitlines(keepends=True)),
                         expected.splitlines(keepends=True))
+
+LEADING_EMPTY_LINES = ("""\
+    \t
+
+if True:
+    a = 3
+
+b = 4
+""", """\
+if True:
+    a = 3
+
+b = 4
+""")
+
+ONLY_EMPTY_LINES = ("""\
+    \t
+
+""", """\
+    \t
+
+""")
+
+def test_leading_empty_lines():
+    for sample, expected in [LEADING_EMPTY_LINES, ONLY_EMPTY_LINES]:
+        nt.assert_equal(
+                ipt2.leading_empty_lines(sample.splitlines(keepends=True)),
+                expected.splitlines(keepends=True))