Repository: pandas-dev/pandas

Issue:
max_columns=0 no longer switches over to summary view
``` python
In [1]: import pandas as pd

In [2]: import numpy as np

In [3]: pd.set_option('line_width', 250)

In [4]: pd.set_option('max_columns', 0)

In [5]: pd.describe_option('max_columns')
display.max_columns: 
: int
        max_rows and max_columns are used in __repr__() methods to decide if
        to_string() or info() is used to render an object to a string.
        Either one, or both can be set to 0 (experimental). Pandas will figure
        out how big the terminal is and will not display more rows or/and
        columns that can fit on it.


In [6]: pd.util.terminal.get_terminal_size()
Out[6]: (138, 43)

In [7]: df = pd.DataFrame(np.random.randn(2,50))
```

df wraps it columns, but no switch over to summary view

```
In [9]: pd.__version__
Out[9]: '0.11.0.dev-dad367e'
```

see also [mailing list](https://groups.google.com/forum/?fromgroups=#!topic/pydata/FNcnHz-PskI)



Discussion:
Just making sure you're aware of the shiny `pd.options` shortcut with tab completion.

@y-p thanks for the tip!


Resolving patch:
diff --git a/pandas/core/config_init.py b/pandas/core/config_init.py
--- a/pandas/core/config_init.py
+++ b/pandas/core/config_init.py
@@ -212,6 +212,9 @@ def mpl_style_cb(key):
     cf.register_option('mpl_style', None, pc_mpl_style_doc,
                        validator=is_one_of_factory([None, False, 'default']),
                        cb=mpl_style_cb)
+    cf.register_option('height', 100, 'TODO', validator=is_int)
+    cf.register_option('width',80, 'TODO', validator=is_int)
+cf.deprecate_option('display.line_width', msg='TODO', rkey='display.width')
 
 tc_sim_interactive_doc = """
 : boolean
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -599,44 +599,39 @@ def empty(self):
     def __nonzero__(self):
         raise ValueError("Cannot call bool() on DataFrame.")
 
-    def _need_info_repr_(self):
+    def _repr_fits_boundaries_(self):
         """
-        Check if it is needed to use info/summary view to represent a
-        particular DataFrame.
+        Check if repr fits in boundaries imposed by the following sets of
+        display options:
+            * width, height
+            * max_rows, max_columns
+        In case off non-interactive session, no boundaries apply.
         """
+        if not com.in_interactive_session():
+            return True
 
-        if com.in_qtconsole():
-            terminal_width, terminal_height = 100, 100
-        else:
-            terminal_width, terminal_height = get_terminal_size()
-        max_rows = (terminal_height if get_option("display.max_rows") == 0
-                    else get_option("display.max_rows"))
-        max_columns = get_option("display.max_columns")
-        expand_repr = get_option("display.expand_frame_repr")
+        terminal_width, terminal_height = get_terminal_size()
 
-        if max_columns > 0:
-            if (len(self.index) <= max_rows and
-                    (len(self.columns) <= max_columns and expand_repr)):
-                return False
-            else:
-                return True
-        else:
-            # save us
-            if (len(self.index) > max_rows or
-                (com.in_interactive_session() and
-                 len(self.columns) > terminal_width // 2 and
-                 not expand_repr)):
-                return True
-            else:
-                buf = StringIO()
-                self.to_string(buf=buf)
-                value = buf.getvalue()
-                if (max([len(l) for l in value.split('\n')]) > terminal_width
-                    and com.in_interactive_session()
-                        and not expand_repr):
-                    return True
-                else:
-                    return False
+        # check vertical boundaries (excluding column axis area)
+        max_rows = get_option("display.max_rows") or terminal_height
+        display_height = get_option("display.height") or terminal_height
+        if len(self.index) > min(max_rows, display_height):
+            return False
+
+        # check horizontal boundaries (including index axis area)
+        max_columns = get_option("display.max_columns")
+        display_width = get_option("display.width") or terminal_width
+        nb_columns = len(self.columns)
+        if max_columns and nb_columns > max_columns:
+            return False
+        if nb_columns > (display_width // 2):
+            return False
+
+        buf = StringIO()
+        self.to_string(buf=buf)
+        value = buf.getvalue()
+        repr_width = max([len(l) for l in value.split('\n')])
+        return repr_width <= display_width
 
     def __str__(self):
         """
@@ -668,26 +663,29 @@ def __unicode__(self):
         py2/py3.
         """
         buf = StringIO(u"")
-        if self._need_info_repr_():
-            max_info_rows = get_option('display.max_info_rows')
-            verbose = max_info_rows is None or self.shape[0] <= max_info_rows
-            self.info(buf=buf, verbose=verbose)
+        if self._repr_fits_boundaries_():
+            self.to_string(buf=buf)
         else:
-            is_wide = self._need_wide_repr()
-            line_width = None
-            if is_wide:
-                line_width = get_option('display.line_width')
-            self.to_string(buf=buf, line_width=line_width)
+            terminal_width, terminal_height = get_terminal_size()
+            max_rows = get_option("display.max_rows") or terminal_height
+            # Expand or info? Decide based on option display.expand_frame_repr
+            # and keep it sane for the number of display rows used by the
+            # expanded repr.
+            if (get_option("display.expand_frame_repr") and
+                len(self.columns) < max_rows):
+                line_width = get_option("display.width") or terminal_width
+                self.to_string(buf=buf, line_width=line_width)
+            else:
+                max_info_rows = get_option('display.max_info_rows')
+                verbose = (max_info_rows is None or
+                           self.shape[0] <= max_info_rows)
+                self.info(buf=buf, verbose=verbose)
 
         value = buf.getvalue()
         assert type(value) == unicode
 
         return value
 
-    def _need_wide_repr(self):
-        return (get_option("display.expand_frame_repr")
-                and com.in_interactive_session())
-
     def __repr__(self):
         """
         Return a string representation for a particular DataFrame
@@ -705,12 +703,18 @@ def _repr_html_(self):
             raise ValueError('Disable HTML output in QtConsole')
 
         if get_option("display.notebook_repr_html"):
-            if self._need_info_repr_():
-                return None
-            else:
+            if self._repr_fits_boundaries_():
                 return ('<div style="max-height:1000px;'
                         'max-width:1500px;overflow:auto;">\n' +
                         self.to_html() + '\n</div>')
+            else:
+                buf = StringIO(u"")
+                max_info_rows = get_option('display.max_info_rows')
+                verbose = (max_info_rows is None or
+                           self.shape[0] <= max_info_rows)
+                self.info(buf=buf, verbose=verbose)
+                info = buf.getvalue().replace('<', '&lt').replace('>', '&gt')
+                return ('<pre>\n' + info + '\n</pre>')
         else:
             return None
 


Test patch:
diff --git a/pandas/tests/test_format.py b/pandas/tests/test_format.py
--- a/pandas/tests/test_format.py
+++ b/pandas/tests/test_format.py
@@ -19,6 +19,7 @@
 from pandas.util.py3compat import lzip
 import pandas.core.format as fmt
 import pandas.util.testing as tm
+from pandas.util.terminal import get_terminal_size
 import pandas
 import pandas as pd
 from pandas.core.config import (set_option, get_option,
@@ -31,6 +32,17 @@ def curpath():
     pth, _ = os.path.split(os.path.abspath(__file__))
     return pth
 
+def has_info_repr(df):
+    r = repr(df)
+    return r.split('\n')[0].startswith("<class")
+
+def has_expanded_repr(df):
+    r = repr(df)
+    for line in r.split('\n'):
+        if line.endswith('\\'):
+            return True
+    return False
+
 
 class TestDataFrameFormatting(unittest.TestCase):
     _multiprocess_can_split_ = True
@@ -144,6 +156,57 @@ def test_repr_no_backslash(self):
             df = DataFrame(np.random.randn(10, 4))
             self.assertTrue('\\' not in repr(df))
 
+    def test_expand_frame_repr(self):
+        df_small = DataFrame('hello', [0], [0])
+        df_wide = DataFrame('hello', [0], range(10))
+
+        with option_context('mode.sim_interactive', True):
+            with option_context('display.width', 50):
+                with option_context('display.expand_frame_repr', True):
+                    self.assertFalse(has_info_repr(df_small))
+                    self.assertFalse(has_expanded_repr(df_small))
+                    self.assertFalse(has_info_repr(df_wide))
+                    self.assertTrue(has_expanded_repr(df_wide))
+
+                with option_context('display.expand_frame_repr', False):
+                    self.assertFalse(has_info_repr(df_small))
+                    self.assertFalse(has_expanded_repr(df_small))
+                    self.assertTrue(has_info_repr(df_wide))
+                    self.assertFalse(has_expanded_repr(df_wide))
+
+    def test_repr_max_columns_max_rows(self):
+        term_width, term_height = get_terminal_size()
+        if term_width < 10 or term_height < 10:
+            raise nose.SkipTest
+
+        def mkframe(n):
+            index = ['%05d' % i for i in range(n)]
+            return DataFrame(0, index, index)
+
+        with option_context('mode.sim_interactive', True):
+            with option_context('display.width', term_width * 2):
+                with option_context('display.max_rows', 5,
+                                    'display.max_columns', 5):
+                    self.assertFalse(has_expanded_repr(mkframe(4)))
+                    self.assertFalse(has_expanded_repr(mkframe(5)))
+                    self.assertFalse(has_expanded_repr(mkframe(6)))
+                    self.assertTrue(has_info_repr(mkframe(6)))
+
+                with option_context('display.max_rows', 20,
+                                    'display.max_columns', 5):
+                    # Out off max_columns boundary, but no extending
+                    # occurs ... can improve?
+                    self.assertFalse(has_expanded_repr(mkframe(6)))
+                    self.assertFalse(has_info_repr(mkframe(6)))
+
+            with option_context('display.max_columns', 0,
+                                'display.max_rows', term_width * 20,
+                                'display.width', 0):
+                df = mkframe((term_width // 7) - 2)
+                self.assertFalse(has_expanded_repr(df))
+                df = mkframe((term_width // 7) + 2)
+                self.assertTrue(has_expanded_repr(df))
+
     def test_to_string_repr_unicode(self):
         buf = StringIO()
 
@@ -1176,8 +1239,8 @@ def get_ipython():
         self.assert_(repstr is not None)
 
         fmt.set_printoptions(max_rows=5, max_columns=2)
-
-        self.assert_(self.frame._repr_html_() is None)
+        repstr = self.frame._repr_html_()
+        self.assert_('class' in repstr)  # info fallback
 
         fmt.reset_printoptions()