Skip to content

inspect / vars function #116

Open
Open
@samuelcolvin

Description

@samuelcolvin

(I thought this was already an issue, but I checked and it seems it only existed in my head)

I want a way to pretty-print pertinent attributes of an object, like vars(...) but more intelligent - in particular it should try to inspect __slots__ if __dict__ is not available.

This might be as simple as sugar around the following POC, but there are probably edge cases I haven't thought about.

I guess we should expose this via debug.inspect().

Rough demo code of what I mean
from __future__ import annotations
from devtools import debug

ignored_attributes = {
    '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
    '__init__', '__init_subclass__', '__le__', '__lt__', '__module__',
    '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
    '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '__slots__',
}


class DebugInspect:
    __slots__ = ('obj',)

    def __init__(self, obj: Any):
        self.obj = obj

    def __pretty__(self, fmt: Callable[[Any], Any], **kwargs: Any) -> Generator[Any, None, None]:
        yield self.obj.__class__.__name__ + '('
        yield 1
        # use __dict__ if possible to maintain order, also should be slightly faster
        obj_dict = getattr(self.obj, '__dict__', None)
        if obj_dict is not None:
            for name, value in obj_dict.items():
                yield name + '='
                yield fmt(value)
                yield ','
                yield 0
        else:
            for name in dir(self.obj):
                if name not in ignored_attributes:
                    yield name + '='
                    yield fmt(getattr(self.obj, name))
                    yield ','
                    yield 0
        yield -1
        # closing bracket to keep it looking a bit like python
        yield ')'


class Foo:
    def __init__(self):
        self.x = 1
        self.y = 2
        self._private = 3
        self.__custom_dunder__ = 4


class Bar:
    __slots__ = 'x', 'y', '_private', '__custom_dunder__'

    def __init__(self):
        self.x = 1
        self.y = 2
        self._private = 3
        self.__custom_dunder__ = 4


f = Foo()
debug(DebugInspect(f))

b = Bar()
debug(DebugInspect(b))

prints:

foobar.py:61 <module>
    DebugInspect(f): Foo(
        x=1,
        y=2,
        _private=3,
        __custom_dunder__=4,
    ) (DebugInspect)
foobar.py:64 <module>
    DebugInspect(b): Bar(
        __custom_dunder__=4,
        _private=3,
        x=1,
        y=2,
    ) (DebugInspect)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions