Skip to content

Commit b9fa04a

Browse files
author
y-p
committed
Merge branch 'repr_fixes'
* 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
2 parents 0ea3965 + 8b782c2 commit b9fa04a

File tree

11 files changed

+251
-108
lines changed

11 files changed

+251
-108
lines changed

RELEASE.rst

+10-1
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,23 @@ pandas 0.11.0
173173
when invalid shapes are passed
174174
- Don't suppress TypeError in GroupBy.agg (GH3238_)
175175
- Methods return None when inplace=True (GH1893_)
176-
176+
- HTML repr output for dataframs is once again controlled by the option
177+
`display.notebook_repr_html`, and on by default.
177178
- ``HDFStore``
178179

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

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

185194
**Bug Fixes**
186195

pandas/core/common.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -1782,11 +1782,25 @@ def in_qtconsole():
17821782
"""
17831783
try:
17841784
ip = get_ipython()
1785-
if ip.config['KernelApp']['parent_appname'] == 'ipython-qtconsole':
1785+
front_end = (ip.config.get('KernelApp',{}).get('parent_appname',"") or
1786+
ip.config.get('IPKernelApp',{}).get('parent_appname',""))
1787+
if 'qtconsole' in front_end.lower():
17861788
return True
17871789
except:
17881790
return False
17891791

1792+
def in_ipnb_frontend():
1793+
"""
1794+
check if we're inside an an IPython zmq frontend
1795+
"""
1796+
try:
1797+
ip = get_ipython()
1798+
return 'zmq' in str(type(ip)).lower()
1799+
except:
1800+
pass
1801+
1802+
return False
1803+
17901804
# Unicode consolidation
17911805
# ---------------------
17921806
#

pandas/core/config.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ def _reset_option(pat):
140140
for k in keys:
141141
_set_option(k, _registered_options[k].defval)
142142

143+
def get_default_val(pat):
144+
key = _get_single_key(pat, silent=True)
145+
return _get_registered_option(key).defval
143146

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

pandas/core/config_init.py

+24-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pandas.core.config as cf
22
from pandas.core.config import (is_int, is_bool, is_text, is_float,
3-
is_instance_factory,is_one_of_factory)
3+
is_instance_factory,is_one_of_factory,get_default_val)
44
from pandas.core.format import detect_console_encoding
55

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

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

5153
pc_max_info_cols_doc = """
@@ -127,18 +129,19 @@
127129

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

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

144147
pc_chop_threshold_doc = """
@@ -208,9 +211,11 @@ def mpl_style_cb(key):
208211
cf.register_option('column_space', 12, validator=is_int)
209212
cf.register_option('max_info_rows', 1690785, pc_max_info_rows_doc,
210213
validator=is_instance_factory((int, type(None))))
211-
cf.register_option('max_rows', 100, pc_max_rows_doc, validator=is_int)
214+
cf.register_option('max_rows', 60, pc_max_rows_doc,
215+
validator=is_one_of_factory([type(None), int]))
212216
cf.register_option('max_colwidth', 50, max_colwidth_doc, validator=is_int)
213-
cf.register_option('max_columns', 20, pc_max_cols_doc, validator=is_int)
217+
cf.register_option('max_columns', 20, pc_max_cols_doc,
218+
validator=is_one_of_factory([type(None), int]))
214219
cf.register_option('max_info_columns', 100, pc_max_info_cols_doc,
215220
validator=is_int)
216221
cf.register_option('colheader_justify', 'right', colheader_justify_doc,
@@ -228,14 +233,17 @@ def mpl_style_cb(key):
228233
cf.register_option('encoding', detect_console_encoding(), pc_encoding_doc,
229234
validator=is_text)
230235
cf.register_option('expand_frame_repr', True, pc_expand_repr_doc)
231-
cf.register_option('line_width', 80, pc_line_width_doc)
232236
cf.register_option('chop_threshold', None, pc_chop_threshold_doc)
233237
cf.register_option('max_seq_items', None, pc_max_seq_items)
234238
cf.register_option('mpl_style', None, pc_mpl_style_doc,
235-
validator=is_one_of_factory([None, False, 'default']),
239+
validator=is_one_of_factory([type(None), False, 'default']),
236240
cb=mpl_style_cb)
237-
cf.register_option('height', 100, pc_height_doc, validator=is_int)
238-
cf.register_option('width',80, pc_width_doc, validator=is_int)
241+
cf.register_option('height', 60, pc_height_doc,
242+
validator=is_one_of_factory([type(None), int]))
243+
cf.register_option('width',80, pc_width_doc,
244+
validator=is_one_of_factory([type(None), int]))
245+
# redirected to width, make defval identical
246+
cf.register_option('line_width', get_default_val('display.width'), pc_line_width_doc)
239247
cf.deprecate_option('display.line_width',
240248
msg=pc_line_width_deprecation_warning,
241249
rkey='display.width')

pandas/core/format.py

+40-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from pandas.core.index import Index, MultiIndex, _ensure_index
1313
from pandas.util import py3compat
1414
from pandas.util.compat import OrderedDict
15+
from pandas.util.terminal import get_terminal_size
1516
from pandas.core.config import get_option, set_option, reset_option
1617
import pandas.core.common as com
1718
import pandas.lib as lib
@@ -356,7 +357,7 @@ def get_col_type(dtype):
356357
return 'r'
357358
else:
358359
return 'l'
359-
360+
360361
import warnings
361362
if force_unicode is not None: # pragma: no cover
362363
warnings.warn(
@@ -372,7 +373,7 @@ def get_col_type(dtype):
372373
strcols = [[info_line]]
373374
else:
374375
strcols = self._to_str_columns()
375-
376+
376377
if column_format is None:
377378
dtypes = self.frame.dtypes.values
378379
column_format = 'l%s' % ''.join(map(get_col_type, dtypes))
@@ -1673,6 +1674,43 @@ def detect_console_encoding():
16731674
return encoding
16741675

16751676

1677+
def get_console_size():
1678+
"""Return console size as tuple = (width, height).
1679+
1680+
May return (None,None) in some cases.
1681+
"""
1682+
display_width = get_option('display.width')
1683+
display_height = get_option('display.height')
1684+
1685+
# Consider
1686+
# interactive shell terminal, can detect term size
1687+
# interactive non-shell terminal (ipnb/ipqtconsole), cannot detect term size
1688+
# non-interactive script, should disregard term size
1689+
1690+
# in addition
1691+
# width,height have default values, but setting to 'None' signals
1692+
# should use Auto-Detection, But only in interactive shell-terminal.
1693+
# Simple. yeah.
1694+
1695+
if com.in_interactive_session():
1696+
if com.in_ipnb_frontend():
1697+
# sane defaults for interactive non-shell terminal
1698+
# match default for width,height in config_init
1699+
from pandas.core.config import get_default_val
1700+
terminal_width = get_default_val('display.width')
1701+
terminal_height = get_default_val('display.height')
1702+
else:
1703+
# pure terminal
1704+
terminal_width, terminal_height = get_terminal_size()
1705+
else:
1706+
terminal_width, terminal_height = None,None
1707+
1708+
# Note if the User sets width/Height to None (auto-detection)
1709+
# and we're in a script (non-inter), this will return (None,None)
1710+
# caller needs to deal.
1711+
return (display_width or terminal_width, display_height or terminal_height)
1712+
1713+
16761714
class EngFormatter(object):
16771715
"""
16781716
Formats float values according to engineering format.

pandas/core/frame.py

+51-29
Original file line numberDiff line numberDiff line change
@@ -602,42 +602,56 @@ def __nonzero__(self):
602602
def _repr_fits_vertical_(self):
603603
"""
604604
Check if full repr fits in vertical boundaries imposed by the display
605-
options height and max_columns. In case off non-interactive session,
605+
options height and max_rows. In case of non-interactive session,
606606
no boundaries apply.
607607
"""
608-
if not com.in_interactive_session():
609-
return True
608+
width, height = fmt.get_console_size()
609+
max_rows = get_option("display.max_rows")
610610

611-
terminal_width, terminal_height = get_terminal_size()
611+
if height is None and max_rows is None:
612+
return True
612613

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

618620
def _repr_fits_horizontal_(self):
619621
"""
620622
Check if full repr fits in horizontal boundaries imposed by the display
621623
options width and max_columns. In case off non-interactive session, no
622624
boundaries apply.
623625
"""
624-
if not com.in_interactive_session():
625-
return True
626-
627-
terminal_width, terminal_height = get_terminal_size()
628-
626+
width, height = fmt.get_console_size()
629627
max_columns = get_option("display.max_columns")
630-
display_width = get_option("display.width") or terminal_width
631628
nb_columns = len(self.columns)
629+
630+
# exceed max columns
632631
if ((max_columns and nb_columns > max_columns) or
633-
(nb_columns > (display_width // 2))):
632+
(width and nb_columns > (width // 2))):
634633
return False
635634

635+
if width is None:
636+
# no sense finding width of repr if no width set
637+
return True
638+
636639
buf = StringIO()
637-
self.to_string(buf=buf)
640+
641+
# only care about the stuff we'll actually print out
642+
# and to_string on entire frame may be expensive
643+
d = self
644+
max_rows = get_option("display.max_rows")
645+
if not (height is None and max_rows is None):
646+
# min of two, where one may be None
647+
height = height or max_rows +1
648+
max_rows = max_rows or height +1
649+
d=d.iloc[:min(max_rows, height,len(d))]
650+
651+
d.to_string(buf=buf)
638652
value = buf.getvalue()
639653
repr_width = max([len(l) for l in value.split('\n')])
640-
return repr_width <= display_width
654+
return repr_width <= width
641655

642656
def __str__(self):
643657
"""
@@ -670,19 +684,24 @@ def __unicode__(self):
670684
"""
671685
buf = StringIO(u"")
672686
fits_vertical = self._repr_fits_vertical_()
673-
fits_horizontal = self._repr_fits_horizontal_()
687+
fits_horizontal = False
688+
if fits_vertical:
689+
# This needs to compute the entire repr
690+
# so don't do it unless rownum is bounded
691+
fits_horizontal = self._repr_fits_horizontal_()
692+
674693
if fits_vertical and fits_horizontal:
675694
self.to_string(buf=buf)
676695
else:
677-
terminal_width, terminal_height = get_terminal_size()
678-
max_rows = get_option("display.max_rows") or terminal_height
679-
# Expand or info? Decide based on option display.expand_frame_repr
680-
# and keep it sane for the number of display rows used by the
681-
# expanded repr.
696+
width, height = fmt.get_console_size()
697+
max_rows = get_option("display.max_rows") or height
698+
# expand_repr basically takes the extrac columns that don't
699+
# fit the width, and creates a new page, which increases
700+
# the effective row count. check number of cols agaibst
701+
# max rows to catch wrapping. that would exceed max_rows.
682702
if (get_option("display.expand_frame_repr") and fits_vertical and
683703
len(self.columns) < max_rows):
684-
line_width = get_option("display.width") or terminal_width
685-
self.to_string(buf=buf, line_width=line_width)
704+
self.to_string(buf=buf, line_width=width)
686705
else:
687706
max_info_rows = get_option('display.max_info_rows')
688707
verbose = (max_info_rows is None or
@@ -707,11 +726,14 @@ def _repr_html_(self):
707726
Return a html representation for a particular DataFrame.
708727
Mainly for IPython notebook.
709728
"""
710-
if com.in_qtconsole():
711-
raise ValueError('Disable HTML output in QtConsole')
712729

713730
if get_option("display.notebook_repr_html"):
714-
if self._repr_fits_horizontal_() and self._repr_fits_vertical_():
731+
fits_vertical = self._repr_fits_vertical_()
732+
fits_horizontal = False
733+
if fits_vertical:
734+
fits_horizontal = self._repr_fits_horizontal_()
735+
736+
if fits_horizontal and fits_vertical:
715737
return ('<div style="max-height:1000px;'
716738
'max-width:1500px;overflow:auto;">\n' +
717739
self.to_html() + '\n</div>')
@@ -1580,7 +1602,7 @@ def info(self, verbose=True, buf=None, max_cols=None):
15801602

15811603
# hack
15821604
if max_cols is None:
1583-
max_cols = get_option('display.max_info_columns')
1605+
max_cols = get_option('display.max_info_columns',len(self.columns)+1)
15841606

15851607
if verbose and len(self.columns) <= max_cols:
15861608
lines.append('Data columns (total %d columns):' % len(self.columns))

pandas/tests/test_config.py

+7
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ def test_api(self):
3939
self.assertTrue(hasattr(pd, 'reset_option'))
4040
self.assertTrue(hasattr(pd, 'describe_option'))
4141

42+
def test_is_one_of_factory(self):
43+
v = self.cf.is_one_of_factory([type(None),int])
44+
45+
v(12)
46+
v(None)
47+
self.assertRaises(ValueError,v,1.1)
48+
4249
def test_register_option(self):
4350
self.cf.register_option('a', 1, 'doc')
4451

0 commit comments

Comments
 (0)