tommikaikkonen / prettyprinter

Syntax-highlighting, declarative and composable pretty printer for Python 3.5+
https://prettyprinter.readthedocs.io
MIT License
336 stars 20 forks source link

Counter prettyprinter should sort entries by count #48

Open anntzer opened 5 years ago

anntzer commented 5 years ago

Description

PrettyPrinter does not sort Counter()s entries by count, unlike Counter.__str__ which explicitly does so.

What I Did

In [1]: prettyprinter.pformat(collections.Counter({"a": 1, "b": 200}))
Out[1]: "collections.Counter({'a': 1, 'b': 200})"

In [2]: str(collections.Counter({"a": 1, "b": 200}))                                                                
Out[2]: "Counter({'b': 200, 'a': 1})"

I think the sorted form is clearly more useful (well, at least, it matches the intent of whoever wrote the stdlib's Counter.__str__).

On Py3.6+ I think it's just a matter of

diff --git i/prettyprinter/pretty_stdlib.py w/prettyprinter/pretty_stdlib.py
index 273ee0e..9c66171 100644
--- i/prettyprinter/pretty_stdlib.py
+++ w/prettyprinter/pretty_stdlib.py
@@ -296,7 +296,7 @@ def pretty_ordereddict(d, ctx):

 @register_pretty(Counter)
 def pretty_counter(counter, ctx):
-    return pretty_call_alt(ctx, type(counter), args=(dict(counter), ))
+    return pretty_call_alt(ctx, type(counter), args=(dict(counter.most_common()), ))

 @register_pretty('enum.Enum')

(well the stdlib's version has additionally some handling when most_common() fails due to unorderable values, but you get the idea) but I'm not sure how to make this work on Py3.5.

tommikaikkonen commented 5 years ago

I think this sounds good. I believe we'll need to extract logic from pretty_dict to a utility function that takes an iterable of key-value pairs instead of a dict, called something like pretty_dict_from_pairs so that we can use that function both here and in the implementation of pretty_dict. This will ensure Python 3.5 can correctly render the output in the most common order. Then the actual pretty printer for Counter can be implemented as

build_fncall(
    ctx,
    type(counter),
    argdocs=(
        pretty_dict_from_pairs(ctx, counter.most_common()),
    ),
    hug_sole_arg=True
)