Skip to content

Commit

Permalink
Merge branch 'repr_fixes'
Browse files Browse the repository at this point in the history
* repr_fixes:
  BUG: config.is_one_of_factory is broken
  TST: add test for config.is_one_of_factory
  DOC: RELEASE.rst mention new options disp.height/width and deprecated line_width
  BUG: fix validators for config options
  ENH: default dimensions for ip zmq frontends derive from disp.height/width defaults
  ENH: let display.notebook_repr_html control HTML repr in qtconsole again.
  ENH: fix df.repr() for scripts (keep GH1611 away), accept None for max_cols/rows
  BUG: rework get_console_size to handle cases properly
  ENH: optimize _repr_fits_horizontal_, to_string only on slice of interest
  BUG: for numerical option, sentry should be another Type, not 0
  TST: fix test_repr_non_interactive passing by accident
  TST: adjust tests to accommodate new defaults for display options
  ENH: Adjust height/width/max_rows defaults to match informal survey results
  CLN: convert test utilizing disp.line_width to disp.width
  DOC: Add comments, tweak option descriptions.
  BUG: Always Ignore auto-detect terminal size for qtc/+ipnb
  ENH: add helper for detecting any IPython zmq frontend, make in_qtconsole robuster
  ENH: add internal get_default_val() function to core.config
  BUG: fix df repr troubles GH3337
  • Loading branch information
y-p committed Apr 22, 2013
2 parents 0ea3965 + 8b782c2 commit b9fa04a
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 108 deletions.
11 changes: 10 additions & 1 deletion RELEASE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,23 @@ pandas 0.11.0
when invalid shapes are passed
- Don't suppress TypeError in GroupBy.agg (GH3238_)
- Methods return None when inplace=True (GH1893_)

- HTML repr output for dataframs is once again controlled by the option
`display.notebook_repr_html`, and on by default.
- ``HDFStore``

- added the method ``select_column`` to select a single column from a table as a Series.
- deprecated the ``unique`` method, can be replicated by ``select_column(key,column).unique()``
- ``min_itemsize`` parameter will now automatically create data_columns for passed keys

- Downcast on pivot if possible (GH3283_), adds argument ``downcast`` to ``fillna``
- Introduced options `display.height/width` for explicitly specifying terminal
height/width in characters. Deprecated display.line_width, now replaced by display.width.
These defaults are in effect for scripts as well, so unless disabled, previously
very wide output will now be output as "expand_repr" style wrapped output.
- Various defaults for options (including display.max_rows) have been revised,
after a brief survey concluded they were wrong for everyone. Now at w=80,h=60.
- HTML repr output for dataframes is once again controlled by the option
`display.notebook_repr_html`, and on by default.

**Bug Fixes**

Expand Down
16 changes: 15 additions & 1 deletion pandas/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1782,11 +1782,25 @@ def in_qtconsole():
"""
try:
ip = get_ipython()
if ip.config['KernelApp']['parent_appname'] == 'ipython-qtconsole':
front_end = (ip.config.get('KernelApp',{}).get('parent_appname',"") or
ip.config.get('IPKernelApp',{}).get('parent_appname',""))
if 'qtconsole' in front_end.lower():
return True
except:
return False

def in_ipnb_frontend():
"""
check if we're inside an an IPython zmq frontend
"""
try:
ip = get_ipython()
return 'zmq' in str(type(ip)).lower()
except:
pass

return False

# Unicode consolidation
# ---------------------
#
Expand Down
5 changes: 4 additions & 1 deletion pandas/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ def _reset_option(pat):
for k in keys:
_set_option(k, _registered_options[k].defval)

def get_default_val(pat):
key = _get_single_key(pat, silent=True)
return _get_registered_option(key).defval

class DictWrapper(object):
""" provide attribute-style access to a nested dict
Expand Down Expand Up @@ -706,7 +709,7 @@ def inner(x):
def is_one_of_factory(legal_values):
def inner(x):
from pandas.core.common import pprint_thing as pp
if not x in legal_values:
if not type(x) in legal_values:
pp_values = map(pp, legal_values)
raise ValueError("Value must be one of %s" % pp("|".join(pp_values)))

Expand Down
40 changes: 24 additions & 16 deletions pandas/core/config_init.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pandas.core.config as cf
from pandas.core.config import (is_int, is_bool, is_text, is_float,
is_instance_factory,is_one_of_factory)
is_instance_factory,is_one_of_factory,get_default_val)
from pandas.core.format import detect_console_encoding

"""
Expand Down Expand Up @@ -34,7 +34,8 @@
: int
This sets the maximum number of rows pandas should output when printing
out various output. For example, this value determines whether the repr()
for a dataframe prints out fully or just an summary repr.
for a dataframe prints out fully or just a summary repr.
'None' value means unlimited.
"""

pc_max_cols_doc = """
Expand All @@ -46,6 +47,7 @@
format in case all columns would not fit vertically. The IPython notebook,
IPython qtconsole, or IDLE do not run in a terminal and hence it is not
possible to do correct auto-detection.
'None' value means unlimited.
"""

pc_max_info_cols_doc = """
Expand Down Expand Up @@ -127,18 +129,19 @@

pc_width_doc = """
: int
Width of the display. In case python/IPython is running in a terminal this
can be set to 0 and pandas will correctly auto-detect the width. Note that
the IPython notebook, IPython qtconsole, or IDLE do not run in a terminal
and hence it is not possible to correctly detect the width.
Width of the display in characters. In case python/IPython is running in
a terminal this can be set to None and pandas will correctly auto-detect the
width.
Note that the IPython notebook, IPython qtconsole, or IDLE do not run in a
terminal and hence it is not possible to correctly detect the width.
"""

pc_height_doc = """
: int
Height of the display. In case python/IPython is running in a terminal this
can be set to 0 and pandas will auto-detect the width. Note that the
IPython notebook, IPython qtconsole, or IDLE do not run in a terminal,
and hence it is not possible to correctly detect the height.
Height of the display in lines. In case python/IPython is running in a
terminal this can be set to None and pandas will auto-detect the width.
Note that the IPython notebook, IPython qtconsole, or IDLE do not run
in a terminal, and hence it is not possible to correctly detect the height.
"""

pc_chop_threshold_doc = """
Expand Down Expand Up @@ -208,9 +211,11 @@ def mpl_style_cb(key):
cf.register_option('column_space', 12, validator=is_int)
cf.register_option('max_info_rows', 1690785, pc_max_info_rows_doc,
validator=is_instance_factory((int, type(None))))
cf.register_option('max_rows', 100, pc_max_rows_doc, validator=is_int)
cf.register_option('max_rows', 60, pc_max_rows_doc,
validator=is_one_of_factory([type(None), int]))
cf.register_option('max_colwidth', 50, max_colwidth_doc, validator=is_int)
cf.register_option('max_columns', 20, pc_max_cols_doc, validator=is_int)
cf.register_option('max_columns', 20, pc_max_cols_doc,
validator=is_one_of_factory([type(None), int]))
cf.register_option('max_info_columns', 100, pc_max_info_cols_doc,
validator=is_int)
cf.register_option('colheader_justify', 'right', colheader_justify_doc,
Expand All @@ -228,14 +233,17 @@ def mpl_style_cb(key):
cf.register_option('encoding', detect_console_encoding(), pc_encoding_doc,
validator=is_text)
cf.register_option('expand_frame_repr', True, pc_expand_repr_doc)
cf.register_option('line_width', 80, pc_line_width_doc)
cf.register_option('chop_threshold', None, pc_chop_threshold_doc)
cf.register_option('max_seq_items', None, pc_max_seq_items)
cf.register_option('mpl_style', None, pc_mpl_style_doc,
validator=is_one_of_factory([None, False, 'default']),
validator=is_one_of_factory([type(None), False, 'default']),
cb=mpl_style_cb)
cf.register_option('height', 100, pc_height_doc, validator=is_int)
cf.register_option('width',80, pc_width_doc, validator=is_int)
cf.register_option('height', 60, pc_height_doc,
validator=is_one_of_factory([type(None), int]))
cf.register_option('width',80, pc_width_doc,
validator=is_one_of_factory([type(None), int]))
# redirected to width, make defval identical
cf.register_option('line_width', get_default_val('display.width'), pc_line_width_doc)
cf.deprecate_option('display.line_width',
msg=pc_line_width_deprecation_warning,
rkey='display.width')
Expand Down
42 changes: 40 additions & 2 deletions pandas/core/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from pandas.core.index import Index, MultiIndex, _ensure_index
from pandas.util import py3compat
from pandas.util.compat import OrderedDict
from pandas.util.terminal import get_terminal_size
from pandas.core.config import get_option, set_option, reset_option
import pandas.core.common as com
import pandas.lib as lib
Expand Down Expand Up @@ -356,7 +357,7 @@ def get_col_type(dtype):
return 'r'
else:
return 'l'

import warnings
if force_unicode is not None: # pragma: no cover
warnings.warn(
Expand All @@ -372,7 +373,7 @@ def get_col_type(dtype):
strcols = [[info_line]]
else:
strcols = self._to_str_columns()

if column_format is None:
dtypes = self.frame.dtypes.values
column_format = 'l%s' % ''.join(map(get_col_type, dtypes))
Expand Down Expand Up @@ -1673,6 +1674,43 @@ def detect_console_encoding():
return encoding


def get_console_size():
"""Return console size as tuple = (width, height).
May return (None,None) in some cases.
"""
display_width = get_option('display.width')
display_height = get_option('display.height')

# Consider
# interactive shell terminal, can detect term size
# interactive non-shell terminal (ipnb/ipqtconsole), cannot detect term size
# non-interactive script, should disregard term size

# in addition
# width,height have default values, but setting to 'None' signals
# should use Auto-Detection, But only in interactive shell-terminal.
# Simple. yeah.

if com.in_interactive_session():
if com.in_ipnb_frontend():
# sane defaults for interactive non-shell terminal
# match default for width,height in config_init
from pandas.core.config import get_default_val
terminal_width = get_default_val('display.width')
terminal_height = get_default_val('display.height')
else:
# pure terminal
terminal_width, terminal_height = get_terminal_size()
else:
terminal_width, terminal_height = None,None

# Note if the User sets width/Height to None (auto-detection)
# and we're in a script (non-inter), this will return (None,None)
# caller needs to deal.
return (display_width or terminal_width, display_height or terminal_height)


class EngFormatter(object):
"""
Formats float values according to engineering format.
Expand Down
80 changes: 51 additions & 29 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,42 +602,56 @@ def __nonzero__(self):
def _repr_fits_vertical_(self):
"""
Check if full repr fits in vertical boundaries imposed by the display
options height and max_columns. In case off non-interactive session,
options height and max_rows. In case of non-interactive session,
no boundaries apply.
"""
if not com.in_interactive_session():
return True
width, height = fmt.get_console_size()
max_rows = get_option("display.max_rows")

terminal_width, terminal_height = get_terminal_size()
if height is None and max_rows is None:
return True

# excluding column axis area
max_rows = get_option("display.max_rows") or terminal_height
display_height = get_option("display.height") or terminal_height
return len(self.index) <= min(max_rows, display_height)
else:
# min of two, where one may be None
height = height or max_rows +1
max_rows = max_rows or height +1
return len(self) <= min(max_rows, height)

def _repr_fits_horizontal_(self):
"""
Check if full repr fits in horizontal boundaries imposed by the display
options width and max_columns. In case off non-interactive session, no
boundaries apply.
"""
if not com.in_interactive_session():
return True

terminal_width, terminal_height = get_terminal_size()

width, height = fmt.get_console_size()
max_columns = get_option("display.max_columns")
display_width = get_option("display.width") or terminal_width
nb_columns = len(self.columns)

# exceed max columns
if ((max_columns and nb_columns > max_columns) or
(nb_columns > (display_width // 2))):
(width and nb_columns > (width // 2))):
return False

if width is None:
# no sense finding width of repr if no width set
return True

buf = StringIO()
self.to_string(buf=buf)

# only care about the stuff we'll actually print out
# and to_string on entire frame may be expensive
d = self
max_rows = get_option("display.max_rows")
if not (height is None and max_rows is None):
# min of two, where one may be None
height = height or max_rows +1
max_rows = max_rows or height +1
d=d.iloc[:min(max_rows, height,len(d))]

d.to_string(buf=buf)
value = buf.getvalue()
repr_width = max([len(l) for l in value.split('\n')])
return repr_width <= display_width
return repr_width <= width

def __str__(self):
"""
Expand Down Expand Up @@ -670,19 +684,24 @@ def __unicode__(self):
"""
buf = StringIO(u"")
fits_vertical = self._repr_fits_vertical_()
fits_horizontal = self._repr_fits_horizontal_()
fits_horizontal = False
if fits_vertical:
# This needs to compute the entire repr
# so don't do it unless rownum is bounded
fits_horizontal = self._repr_fits_horizontal_()

if fits_vertical and fits_horizontal:
self.to_string(buf=buf)
else:
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.
width, height = fmt.get_console_size()
max_rows = get_option("display.max_rows") or height
# expand_repr basically takes the extrac columns that don't
# fit the width, and creates a new page, which increases
# the effective row count. check number of cols agaibst
# max rows to catch wrapping. that would exceed max_rows.
if (get_option("display.expand_frame_repr") and fits_vertical and
len(self.columns) < max_rows):
line_width = get_option("display.width") or terminal_width
self.to_string(buf=buf, line_width=line_width)
self.to_string(buf=buf, line_width=width)
else:
max_info_rows = get_option('display.max_info_rows')
verbose = (max_info_rows is None or
Expand All @@ -707,11 +726,14 @@ def _repr_html_(self):
Return a html representation for a particular DataFrame.
Mainly for IPython notebook.
"""
if com.in_qtconsole():
raise ValueError('Disable HTML output in QtConsole')

if get_option("display.notebook_repr_html"):
if self._repr_fits_horizontal_() and self._repr_fits_vertical_():
fits_vertical = self._repr_fits_vertical_()
fits_horizontal = False
if fits_vertical:
fits_horizontal = self._repr_fits_horizontal_()

if fits_horizontal and fits_vertical:
return ('<div style="max-height:1000px;'
'max-width:1500px;overflow:auto;">\n' +
self.to_html() + '\n</div>')
Expand Down Expand Up @@ -1580,7 +1602,7 @@ def info(self, verbose=True, buf=None, max_cols=None):

# hack
if max_cols is None:
max_cols = get_option('display.max_info_columns')
max_cols = get_option('display.max_info_columns',len(self.columns)+1)

if verbose and len(self.columns) <= max_cols:
lines.append('Data columns (total %d columns):' % len(self.columns))
Expand Down
7 changes: 7 additions & 0 deletions pandas/tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ def test_api(self):
self.assertTrue(hasattr(pd, 'reset_option'))
self.assertTrue(hasattr(pd, 'describe_option'))

def test_is_one_of_factory(self):
v = self.cf.is_one_of_factory([type(None),int])

v(12)
v(None)
self.assertRaises(ValueError,v,1.1)

def test_register_option(self):
self.cf.register_option('a', 1, 'doc')

Expand Down
Loading

0 comments on commit b9fa04a

Please sign in to comment.