Detect and pretty print dictionary views (dict_keys(), dict_values(), and dict_items())
eg detect and pretty print
d = { ... }
ic(d.keys())
ic(d.values())
like
d = { ... }
ic(list(d.keys()))
ic(list(d.values()))
I don't get it.
from icecream import ic
d = {'key': 'value'}
ic(d.keys())
ic(d.values())
Output:
ic| d.keys(): dict_keys(['key'])
ic| d.values(): dict_values(['value'])
Are you saying you want the output to be:
ic| d.keys(): ['key']
ic| d.values(): ['value']
Seems like a marginal improvement.
Example with larger dicts exhibits less-than-optimal formatting:
from icecream import ic
from random import choice
from string import ascii_letters as letters
randomWord = lambda: ''.join(choice(letters) for i in range(10))
d = {randomWord():randomWord() for i in range(10)}
ic(d) # Nicely formatted.
ic(d.items()) # Poorly formatted.
Output:
ic| d: {'CsTAmcmuz': 'rDCAyhFrk',
'TWrUmbiPu': 'nnXNZceYI',
'XeNFOVFdl': 'dXoIbTrYd',
'XhhXFHIFj': 'qpSSOaJwp',
'cCvxMwZYB': 'nGYfUkhGB',
'dHPrOMQZN': 'ysaCBpiUU',
'dZnvdVmAv': 'DuxmrCRyj',
'hAwafTchI': 'CUvXhhqjz',
'lRbAsPfMG': 'cRFFohVXc',
'sIGOEzebe': 'fqotZssCh'}
ic| d.items(): dict_items([('dZnvdVmAv', 'DuxmrCRyj'), ('CsTAmcmuz', 'rDCAyhFrk'), ('cCvxMwZYB', 'nGYfUkhGB'), ('XeNFOVFdl', 'dXoIbTrYd'), ('XhhXFHIFj', 'qpSSOaJwp'), ('TWrUmbiPu', 'nnXNZceYI'), ('hAwafTchI', 'CUvXhhqjz'), ('lRbAsPfMG', 'cRFFohVXc'), ('dHPrOMQZN', 'ysaCBpiUU'), ('sIGOEzebe', 'fqotZssCh')])
I'm not sure how practical it is to generally 'discover' internal dict-like, or list-like, structures that should be formatted as dicts or lists in arbitrary objects. Or, alternatively, whether dictionary view objects (dict.keys(), dict.values(), and dict.items()) are a useful enough corner case to add a specific check for in IceCream.
I'm leaning the latter. For now, at least. Until this problem crops up again with other, arbitrary objects with internal dict-like and/or list-like structures that would benefit from formatting. One problem at a time.
What do you think?
Ah OK, I didn't realise this was about pprint.
This made me look up alternatives to the built in pprint. Here's what I found:
- https://github.com/tommikaikkonen/prettyprinter
- https://github.com/panyanyany/beeprint
- https://github.com/wolever/pprintpp
It turns out none of these handle dict_keys. Maybe that implies a lack of demand?
Either way, if we want to improve formatting in general, maybe using one of these by default could be a good idea? And if we do, then adding support for dict views there might be more worthwhile.
I was once similarly asked to add support for pprintpp in snoop, although I never got around to doing it: https://github.com/alexmojaki/snoop/issues/13
Actually just ran into another organic example just now: ic(os.environ):
import os
from icecream import ic
ic(os.environ)
Output:
ic| os.environ: environ({'PWD': ... a really long, not-pretty-printed, single-line string ... })
Also pretty print is a much better description of the issue than format :). Updated the Issue title.
Yes, this will be an issue with loads of custom classes.
Here is a quick attempt at a general solution for some cases. Don't have time to go into this more.
import ast
import pprint
import re
def clever_pformat(x):
result = pprint.pformat(x)
match = re.match("([\w.]+)\((.+)\)$", result)
if not match:
return result
name, inner = match.groups()
try:
value = ast.literal_eval(inner)
except ValueError:
return result
return "{}({})".format(name, pprint.pformat(value))
import os
print(clever_pformat(os.environ))
print(clever_pformat(dict(os.environ).keys()))
I'll think about this more, too. I created the Issue yesterday so the potential improvement wouldn't get lost.