Skip to content

inspect / vars function #116

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)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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