Printing a Dict() with Rich
Asked Answered
P

2

9

I'm trying to print a dict() using Pythons Rich. From my understanding, this should output the data on different lines etc. A bit like pprint.

But I'm getting:

>>> from rich import print
>>> print(output)

{'GigabitEthernet0/1': {'description': '## Connected to leaf-2 ##', 'type': 'iGbE', 'oper_status': 'up', 
'phys_address': '5000.0009.0001', 'port_speed': 'auto speed', 'mtu': 1500, 'enabled': True, 'bandwidth': 1000000, 
'flow_control': {'receive': False, 'send': False}, 'mac_address': '5000.0009.0001', 'auto_negotiate': True, 
'port_channel': {'port_channel_member': False}, 'duplex_mode': 'auto', 'delay': 10, 'accounting': {'other': {'pkts_in':
0, 'chars_in': 0, 'pkts_out': 431258, 'chars_out': 25875480}, 'ip': {'pkts_in': 513383, 'chars_in': 42910746, 
'pkts_out': 471188, 'chars_out': 45342027}, 'dec mop': {'pkts_in': 0, 'chars_in': 0, 'pkts_out': 7163, 'chars_out': 
551551}, 'arp': {'pkts_in': 3845, 'chars_in': 230700, 'pkts_out': 3846, 'chars_out': 230760}, 'cdp': {'pkts_in': 72010,
'chars_in': 18866620, 'pkts_out': 79879, 'chars_out': 31221768}}, 'ipv4': {'10.1.1.5/30': {'ip': '10.1.1.5',  ...

Any suggestions?

Potamic answered 6/5, 2021 at 21:17 Comment(6)
Rich uses information about your terminal to determine whether or not to print things on multiple lines. What terminal emulator are you running this in? It's possible you're running this in a "dumb" terminal that just doesn't provide enough informationTaiwan
Im using VScode.Potamic
is the output colored?Belfry
@Potamic so, you are running it on teh VScode terminal?Goober
yep, output is color, and yes on the vscode termminal.Potamic
If you don't mind, which module generated the output?Screw
S
9

TL;DR If your dictionary turns out to be not a dict, make an explicit conversion.


From contents of your dictionary I assume your output is from network device configuration like Cisco IOS, I'm dark on these areas and can't quite figure out where you've got your data from.

There's chance that module or script you used to get output may have actually returned a dict looking type called MappingProxyType.

I speculate this is why your text is colored but not prettified.


For example lets see what rich.print does with str.__dict__:

>>> from rich import print
>>> print(str.__dict__)

This will look like this, just as yours.

enter image description here

Do note this is xfce4 Terminal running in WSL2, drawn to X410 X-server. A fully capable terminal.

This indeed looks like plain dict, but lets check what it actually is:

>>> type(str.__dict__)
<class 'mappingproxy'>

>>> from types import MappingProxyType
>>> isinstance(str.__dict__, MappingProxyType)
True

>>> isinstance(str.__dict__, dict)
False

As you see, despite it's output looks like a dictionary, it's not.

types.MappingProxyType Is essentially a read-only dictonary which isn't necessarily a dict. Devs of 3rd party libraries like rich might have forgotten existence of such type. If that's the case, then rich.print will do what built-in print() would do: to call __repr__/__str__ - now just treating it as mere string.

We can confirm this behavior by passing string that looks like __repr__ of a method, and that still gets rich text treatment.

enter image description here

And also by creating instance of MappingProxyType yourself.

>>> from types import MappingProxyType
>>> from rich import print

>>> data = {f"{n}": n for n in range(11)}

>>> print(data)
{
    '0': 0,
    '1': 1,
    '2': 2,
    '3': 3,
    '4': 4,
    '5': 5,
    '6': 6,
    '7': 7,
    '8': 8,
    '9': 9,
    '10': 10
}

>>> print(MappingProxyType(data))
{'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
'10': 10}

From which you can see how type affects rich.print's output.

To fix, simply convert types.MappingProxyType to dict.

>>> from rich import print
>>> print(dict(str.__dict__))

enter image description here

And that's way more pretty than it used to be - excluding __doc__ value which is a single string and can't be helped.

Screw answered 10/5, 2021 at 12:50 Comment(1)
Rich author here. This answer is probably correct. Your output may be dict like object, but not an actual dict. I would suspect a UserDict rather than a MappingProxy. If you could print(type(output)) we would know for sure.Ripuarian
A
0

@jupiterbjy's answer is correct, but I want to expand on it by providing a solution that doesn't require converting to dict on every repr() manually.

If you want your custom object to repr like a dict codebase-wide you can add a __rich_repr__ method.

from types import MappingProxyType

class CustomDictType(MappingProxyType):
    ...

    def __rich_repr__(self):
        return (dict(self),)

For more info see the docs: https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol

For example to do this for all benedict objects you could monkey patch the base class like so:

import benedict
benedict.benedict.__rich_repr__ = lambda self: (dict(self),)

(This should be done with care, as monkey patching can make codebases harder maintain/debug)

Acetic answered 3/10 at 0:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.