Little Q: how to print a variable's name, not its value?

Duncan Booth duncan.booth at invalid.invalid
Wed Mar 30 03:43:17 EST 2005


Ron_Adam wrote:

> I've been playing around with a way to explore name spaces, but once
> you drop into class's, and functions, the references can lead you into
> an endless loops.  
> 
Here is a rough attempt at printing the names of a variable. It will pick
up several names where appropriate, but deliberately doesn't attempt to
get all possible names (as you say, that could result in endless loops).
In particular, for the Fred=5/John=8/Winner=8 example it will only find
one of John or Winner since it only picks at most one match from each dict 
or list. It doesn't yet manage to correctly lookup attributes (e.g. slots) 
when they aren't stored in a __dict__, nor does the output distinguish
between dictionary keys and values (so encodings.cp437.encoding_map[8]
below actually refers to the key not the value).

If you want to track down a memory leak with this code save a weak 
reference to the variable you expect to be saved and then you may be able 
to work out why it hasn't been freed later on. I originally wrote a version
of this code because I was having problems with xml.dom.ext.reader.HtmlLib 
(its parser holds onto the DOM from a C extension object which doesn't 
support garbage collection). In that situation you get variable 'names' 
beginning with '...' since there is no named variable to start from, but 
the attributes make it easier to track down which objects are involved and 
where the circular loops are.

Python 2.4 (#60, Nov 30 2004, 11:49:19) [MSC v.1310 32 bit (Intel)] on
win32 Type "help", "copyright", "credits" or "license" for more
information. 
>>> Fred = 5
>>> John = 8
>>> Winner = John
>>> import varname
>>> for s in varname.object_info(Winner, False):
...    print s
...
sre_parse.ESCAPES['\\b'][1]
tokenize.tok_name[8]
token.tok_name[8]
sre_parse.OPCODES['call']
sre_parse.FLAGS['m']
sre_parse.CHCODES['category_loc_word']
sre_parse.ATCODES['at_loc_boundary']
sre_constants.OPCODES['call']
sre_constants.CHCODES['category_loc_word']
sre_constants.ATCODES['at_loc_boundary']
sre_compile.OPCODES['call']
sre_compile.CHCODES['category_loc_word']
sre_compile.ATCODES['at_loc_boundary']
encodings.cp437.encoding_map[8]
encodings.cp437.decoding_map[8]
encodings.cp1252.encoding_map[8]
encodings.cp1252.decoding_map[8]
string.expandtabs[0]
tokenize.RPAR
token.RPAR
stat.ST_MTIME
sre_parse.SRE_FLAG_MULTILINE
sre_constants.SRE_FLAG_MULTILINE
sre_compile.SRE_FLAG_MULTILINE
sre.M
signal.SIGFPE
re.M
os.O_APPEND
nt.O_APPEND
inspect.CO_VARKEYWORDS
imp.PY_CODERESOURCE
gc.DEBUG_INSTANCES
__main__.Winner
>>>
>>> class C:
...     def foo(self):
...        x = 'Hello '+'world'
...        for s in varname.object_info(x):
...            print s
...
>>> C().foo()
__main__.C.foo()x
>>>

---------- varname.py ------------
import gc, inspect, types, operator

def locate_keyorvalue(obj, container):
    if isinstance(container, dict):
        for k, v in container.iteritems():
            if v is obj:
                return 'value', k
            if k is obj:
                return 'key', k
    else:
        for i, x in enumerate(container):
            if x is obj:
                return 'index', i

    return '???', ''

def object_info(obj, anonymous=True):
    gc.collect()

    tree = {}
    ignored = {}
    def ignore(obj):
        ignored[id(obj)] = 1
    def unignore(obj):
        del ignored[id(obj)]

    def safeshow(o):
        if isinstance(o, (list, dict)):
            return type(o)
        return o
        
    def buildTree(obj):
        '''Build a tree of referrers to obj such that
        tree[id(obj)] -> list of referrers
        '''
        ignore(inspect.currentframe())

        objects = [obj]
        while objects:
            current = objects.pop()
            #print "current", type(current), hex(id(current))

            if isinstance(current, (types.ModuleType, )):
                refs = [] # Don't extend references to modules
            else:
                refs = [ o for o in gc.get_referrers(current) if not id(o) in ignored ]
                ignore(refs)

            tree[id(current)] = refs

            modules = [ r for r in refs if isinstance(r, (types.ModuleType,)) ]
            if modules:
                objects.extend(modules)
                tree[id(current)] = modules
            else:
                # Not yet found a path from a module
                for r in refs:
                    if not id(r) in tree:
                        objects.append(r)

    ignore(inspect.currentframe())
    ignore(tree)

    buildTree(obj)
    
    def findPath(obj, ignore):
        '''Find a path from 'obj' back as far as it will go in the tree'''
        ignore[id(obj)] = 1

        referrers = tree[id(obj)]
        if referrers:
            for t in referrers:
                if id(t) in ignore:
                    yield ['...', t, obj]
                else:
                    for path in findPath(t, ignore):
                        yield path + [obj]
        else:
            yield [obj]

        del ignore[id(obj)]


    ignores = {}
    SCORES = {
        types.ModuleType: 100,
        type: 70,
        types.ClassType: 60,
        types.FrameType: 20,
        list: 10, dict:10
        }
    results = []
    for p in findPath(obj, ignores):
        score = []
        result = []
        while p:
            this = p.pop(0)
            #print type(this)
            if hasattr(this, '__dict__') and p[0]==this.__dict__:
                next = p.pop(0)
                name = locate_keyorvalue(p[0], next)
                if not result:
                    result.append(this.__name__)
                result.append('.')
                result.append(name[1])

            elif isinstance(this, types.FrameType):
                style, name = locate_keyorvalue(p[0], this.f_locals)
                code = this.f_code
                result.append("%s()" % object_info(this.f_code).next())
                result.append(name)

            elif isinstance(this, types.GeneratorType):
                break
            else:
                if isinstance(this, (tuple, list, dict)) and len(p):
                    style, name = locate_keyorvalue(p[0], this)
                    if not result:
                        if not anonymous:
                            break
                        result.append('<anonymous %s@%s>' % (type(this).__name__,id(this)))
                    result.append('[%r]' % name)
                else:
                    if not result:
                        if not anonymous:
                            break
                        result.append('<%s>' % (this,))
                        #print this
                        break
            score.append(SCORES.get(type(this), 0))

        if result:
            results.append((score, str.join('', result)))
            #yield str.join('', result), score
            result = []

    for r in sorted(results, reverse=True):
        yield r[1]
    return

if __name__=='__main__':
    def test():
        v1 = 'abc'+'def'
        print "Info for",repr(v1)
        for s in object_info(v1):
            print s

    class Fred(object):
        __classvar = 'xyzzy1'
        def foo(self, v):
            print "Info for", repr(v)
            for s in object_info(v):
                print s
            print

    myvar = Fred()
    myvar.abc = 'xyzzy1'
    pqr = (0, myvar.abc, 1)

    aDict = {}
    aDict[myvar.abc] = 'xyzzy1'

    def test1(x):
        print "Info for", repr(x)
        for s in object_info(x, False):
            print s
        print
    test1('xyzzy1')

    myvar.foo('pqr'+'stu')
    test()

    #test1(None)
----------------------------------



More information about the Python-list mailing list