Repository: ipython/ipython

Issue:
pretty print list too slow
It seems ipython's behaviour is very different if I say:

```
print range(10**6)       # case 1, fraction of a second, output in ONE line

range(10**6)             # case 2, ten seconds, 10^6 lines
```

and the difference is not due to the actual output, but presumably the formatting of the one-value-per-line output.

As this is a factor of about 10x (not counting the actual output), and growing with list size, there appears to be a problem because this is unexpected by the user. It was only through debugging, then web search, and a stackoverflow comment that I found the %pprint magic which is nowhere documented. So even if you dismiss this as bug or enhancement, this ticket should add helpful documentation.



Discussion:
Blocks http://trac.sagemath.org/ticket/15912

I think we could cap the size for pretty printing of lists, dicts, etc. easily enough.


Resolving patch:
diff --git a/IPython/lib/pretty.py b/IPython/lib/pretty.py
--- a/IPython/lib/pretty.py
+++ b/IPython/lib/pretty.py
@@ -229,10 +229,11 @@ class PrettyPrinter(_PrettyPrinterBase):
     callback method.
     """
 
-    def __init__(self, output, max_width=79, newline='\n'):
+    def __init__(self, output, max_width=79, newline='\n', max_seq_length=1000):
         self.output = output
         self.max_width = max_width
         self.newline = newline
+        self.max_seq_length = max_seq_length
         self.output_width = 0
         self.buffer_width = 0
         self.buffer = deque()
@@ -325,7 +326,17 @@ def begin_group(self, indent=0, open=''):
         self.group_stack.append(group)
         self.group_queue.enq(group)
         self.indentation += indent
-
+    
+    def _enumerate(self, seq):
+        """like enumerate, but with an upper limit on the number of items"""
+        for idx, x in enumerate(seq):
+            if self.max_seq_length and idx >= self.max_seq_length:
+                self.text(',')
+                self.breakable()
+                self.text('...')
+                raise StopIteration
+            yield idx, x
+    
     def end_group(self, dedent=0, close=''):
         """End a group. See `begin_group` for more details."""
         self.indentation -= dedent
@@ -598,7 +609,7 @@ def inner(obj, p, cycle):
             return p.text(start + '...' + end)
         step = len(start)
         p.begin_group(step, start)
-        for idx, x in enumerate(obj):
+        for idx, x in p._enumerate(obj):
             if idx:
                 p.text(',')
                 p.breakable()
@@ -635,7 +646,7 @@ def inner(obj, p, cycle):
             except Exception:
                 # Sometimes the items don't sort.
                 pass
-            for idx, x in enumerate(items):
+            for idx, x in p._enumerate(items):
                 if idx:
                     p.text(',')
                     p.breakable()
@@ -664,7 +675,7 @@ def inner(obj, p, cycle):
         except Exception as e:
             # Sometimes the keys don't sort.
             pass
-        for idx, key in enumerate(keys):
+        for idx, key in p._enumerate(keys):
             if idx:
                 p.text(',')
                 p.breakable()


Test patch:
diff --git a/IPython/lib/tests/test_pretty.py b/IPython/lib/tests/test_pretty.py
--- a/IPython/lib/tests/test_pretty.py
+++ b/IPython/lib/tests/test_pretty.py
@@ -196,4 +196,29 @@ def test_super_repr():
     sb = SB()
     output = pretty.pretty(super(SA, sb))
     nt.assert_in("SA", output)
-    
\ No newline at end of file
+
+
+def test_long_list():
+    lis = list(range(10000))
+    p = pretty.pretty(lis)
+    last2 = p.rsplit('\n', 2)[-2:]
+    nt.assert_equal(last2, [' 999,', ' ...]'])
+
+def test_long_set():
+    s = set(range(10000))
+    p = pretty.pretty(s)
+    last2 = p.rsplit('\n', 2)[-2:]
+    nt.assert_equal(last2, [' 999,', ' ...}'])
+
+def test_long_tuple():
+    tup = tuple(range(10000))
+    p = pretty.pretty(tup)
+    last2 = p.rsplit('\n', 2)[-2:]
+    nt.assert_equal(last2, [' 999,', ' ...)'])
+
+def test_long_dict():
+    d = { n:n for n in range(10000) }
+    p = pretty.pretty(d)
+    last2 = p.rsplit('\n', 2)[-2:]
+    nt.assert_equal(last2, [' 999: 999,', ' ...}'])
+