Welcome, guest | Sign In | My Account | Store | Cart

Here's a text-based object browser that is helpful when working with big complicated data structures.

Python, 134 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
class ObjectDescender:
    def __init__(self, maxdepth, outf=sys.stderr):
        self.already = [ ]
        self.maxdepth = maxdepth
        self.outf = outf

    def exclude(self, attr, obj):
        return False
    def showThis(self, attr, obj):
        return True

    def prefix(self, depth, pn):
        return ((depth * "\t") + ".".join(pn) + ": ")

    def handleLeaf(self, v, depth, pn):
        def trepr(v):
            if v == None:
                return "None"
            elif type(v) == types.InstanceType:
                r = v.__class__.__name__
            else:
                r = repr(type(v))
            return "%s at %x" % (r, id(v))
        if type(v) in (types.ListType, types.TupleType):
            self.outf.write(self.prefix(depth, pn) + trepr(v))
            if len(v) == 0:
                self.outf.write(" (empty)")
            self.outf.write("\n")
        elif type(v) in (types.StringType, types.IntType,
                         types.FloatType, types.ComplexType):
            self.outf.write(self.prefix(depth, pn) + repr(v) + "\n")
        else:
            self.outf.write(self.prefix(depth, pn) + trepr(v) + "\n")

    def getAttributes(self, obj):
        lst = dir(obj)
        if hasattr(obj, "__dict__"):
            for x in obj.__dict__.keys():
                if x not in lst:
                    lst.append(x)
        lst.sort()
        def filt(x):
            return x not in ("__doc__",)
        return filter(filt, lst)

    def descend(self, obj, depth=0, pathname=[ ]):
        if obj in self.already:
            return
        self.already.append(obj)
        if depth == 0:
            self.handleLeaf(obj, depth, pathname)
        if depth >= self.maxdepth:
            return
        if type(obj) in (types.ListType, types.TupleType):
            lst = [ ]
            if len(pathname) > 0:
                lastitem = pathname[-1]
                pathname = pathname[:-1]
            else:
                lastitem = ""
            for i in range(len(obj)):
                x = obj[i]
                if not self.exclude(i, x):
                    y = pathname + [ lastitem + ("[%d]" % i) ]
                    lst.append((i, x, y))
            for i, v, pn in lst:
                if self.showThis(i, v):
                    self.handleLeaf(v, depth+1, pn)
            for i, v, pn in lst:
                self.descend(v, depth+1, pn)
        elif type(obj) in (types.DictType,):
            keys = obj.keys()
            lst = [ ]
            if len(pathname) > 0:
                lastitem = pathname[-1]
                pathname = pathname[:-1]
            else:
                lastitem = ""
            for k in keys:
                x = obj[k]
                if not self.exclude(k, x):
                    y = pathname + [ lastitem + ("[%s]" % repr(k)) ]
                    lst.append((k, x, y))
            for k, v, pn in lst:
                if self.showThis(k, v):
                    self.handleLeaf(v, depth+1, pn)
            for k, v, pn in lst:
                self.descend(v, depth+1, pn)
        elif (hasattr(obj, "__class__") or
            type(obj) in (types.InstanceType, types.ClassType,
                          types.ModuleType, types.FunctionType)):
            ckeys = [ ]
            if True:
                # Look at instance variables, ignore class variables and methods
                if hasattr(obj, "__class__"):
                    ckeys = self.getAttributes(obj.__class__)
            else:
                # Look at all variables and methods
                ckeys = ( )
            keys = filter(lambda x: x not in ckeys, self.getAttributes(obj))
            lst = [ ]
            for k in keys:
                x = getattr(obj, k)
                if not self.exclude(k, x):
                    lst.append((k, x, pathname + [ k ]))
            for k, v, pn in lst:
                if self.showThis(k, v):
                    self.handleLeaf(v, depth+1, pn)
            for k, v, pn in lst:
                self.descend(v, depth+1, pn)

def standardExclude(attr, obj):
    # This is specific to the codebase I am currently working in.
    # These classes have a lot of internals that are rarely of interest.
    from MWsemantics import MWsemantics
    from GLPane import GLPane
    return isinstance(obj, MWsemantics) or isinstance(obj, GLPane)

def objectBrowse(obj, maxdepth=5, exclude=standardExclude, showThis=None, outf=sys.stderr):
    od = ObjectDescender(maxdepth=maxdepth, outf=outf)
    if showThis != None:
        od.showThis = showThis
    od.exclude = exclude
    od.descend(obj, pathname=['arg'])

def findChild(obj, showThis, maxdepth=8):
    # Drill down deeper because we're being more selective
    def prefix(depth, pn):
        # no indentation
        return (".".join(pn) + ": ")
    f = Finder(maxdepth=maxdepth)
    f.showThis = showThis
    f.prefix = prefix
    f.descend(obj, pathname=['arg'])

The ObjectDescender class can also be customized for related uses, e.g. searching for a child of the object that meets some criterion. Any child object that has already been found is only explored once.