Skip to content

BUG: honor repr() of proper subclasses of dict/ODict in pprint_thing #3253

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 42 additions & 10 deletions pandas/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import pandas.tslib as tslib

from pandas.util import py3compat
from pandas.util import compat
import codecs
import csv

Expand Down Expand Up @@ -763,7 +764,7 @@ def changeit():
# our type is wrong here, need to upcast
r, fill_value = _maybe_upcast(result, fill_value=other, dtype=dtype, copy=True)
np.putmask(r, mask, other)

# we need to actually change the dtype here
if change is not None:
change.dtype = r.dtype
Expand All @@ -778,7 +779,7 @@ def changeit():
new_dtype, fill_value = _maybe_promote(result.dtype,other)
if new_dtype != result.dtype:

# we have a scalar or len 0 ndarray
# we have a scalar or len 0 ndarray
# and its nan and we are changing some values
if np.isscalar(other) or (isinstance(other,np.ndarray) and other.ndim < 1):
if isnull(other):
Expand Down Expand Up @@ -1471,9 +1472,31 @@ def is_list_like(arg):

def _is_sequence(x):
try:
iter(x)
if isinstance(x, basestring):
return False
iter(x) # it's iterable
len(x) # it has a length
return not isinstance(x, basestring) and True

return True
except Exception:
return False

def _is_pprint_sequence(x):
try:
if isinstance(x, basestring):
return False

# proper subclass of stdlib dicts, let them format themselves
if (isinstance(x, dict) and
(not type(x) == dict) and
(not type(x) == compat.OrderedDict)):
return False

iter(x) # it's iterable
len(x) # it has a length

return True

except Exception:
return False

Expand Down Expand Up @@ -1818,9 +1841,16 @@ def _pprint_dict(seq, _nest_lvl=0):
pairs = []

pfmt = u"%s: %s"
for k, v in seq.items():
pairs.append(pfmt % (repr(k), repr(v)))
return fmt % ", ".join(pairs)

nitems = get_option("max_seq_items") or len(seq)

for k, v in seq.items()[:nitems]:
pairs.append(pfmt % (pprint_thing(k,_nest_lvl+1), pprint_thing(v,_nest_lvl+1)))

if nitems < len(seq):
return fmt % (", ".join(pairs) + ", ...")
else:
return fmt % ", ".join(pairs)


def pprint_thing(thing, _nest_lvl=0, escape_chars=None, default_escapes=False):
Expand Down Expand Up @@ -1849,15 +1879,17 @@ def pprint_thing(thing, _nest_lvl=0, escape_chars=None, default_escapes=False):

"""


if thing is None:
result = ''
elif (py3compat.PY3 and hasattr(thing, '__next__')) or \
hasattr(thing, 'next'):
return unicode(thing)
elif (isinstance(thing, dict) and
elif ((type(thing) == dict) or
(type(thing) == compat.OrderedDict) and
_nest_lvl < get_option("display.pprint_nest_depth")):
result = _pprint_dict(thing, _nest_lvl)
elif _is_sequence(thing) and _nest_lvl < \
elif _is_pprint_sequence(thing) and _nest_lvl < \
get_option("display.pprint_nest_depth"):
result = _pprint_seq(thing, _nest_lvl, escape_chars=escape_chars)
else:
Expand All @@ -1876,7 +1908,7 @@ def pprint_thing(thing, _nest_lvl=0, escape_chars=None, default_escapes=False):
result = str(thing).decode('utf-8', "replace")

translate = {'\t': r'\t',
'\n': r'\n',
'\n': r'\n',
'\r': r'\r',
}
if isinstance(escape_chars, dict):
Expand Down
18 changes: 17 additions & 1 deletion pandas/tests/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@ def test_ensure_platform_int():


def test_pprint_thing():
from pandas.util.compat import OrderedDict

if py3compat.PY3:
raise nose.SkipTest

Expand All @@ -309,6 +311,20 @@ def test_pprint_thing():
# GH #2038
assert not "\t" in pp_t("a\tb", escape_chars=("\t",))

assert u'{a: 1}' == pp_t(dict(a=1))
assert u'{a: 1}' == pp_t(OrderedDict(a=1))

# allow proper subclasses of dict/OrderedDict to format themselves
class SubDict(dict):
def __unicode__(self):
return "kagles"

class SubODict(OrderedDict):
def __unicode__(self):
return "kagles"

assert u'kagles' == pp_t(SubDict())
assert u'kagles' == pp_t(SubODict())

class TestTake(unittest.TestCase):

Expand Down Expand Up @@ -684,7 +700,7 @@ def test_2d_float32(self):
expected = arr.take(indexer, axis=1)
expected[:, [2, 4]] = np.nan
tm.assert_almost_equal(result, expected)

def test_2d_datetime64(self):
# 2005/01/01 - 2006/01/01
arr = np.random.randint(11045376L, 11360736L, (5,3))*100000000000
Expand Down
5 changes: 3 additions & 2 deletions pandas/tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -1192,8 +1192,9 @@ def test_dict_entries(self):
df = DataFrame({'A': [{'a': 1, 'b': 2}]})

val = df.to_string()
self.assertTrue("'a': 1" in val)
self.assertTrue("'b': 2" in val)
# to be fixed ot 'a': 1 when #3038 comes to town
self.assertTrue("a: 1" in val)
self.assertTrue("b: 2" in val)

def test_to_latex(self):
# it works!
Expand Down