Skip to content

Instantly share code, notes, and snippets.

@lebedov
Last active August 29, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lebedov/4da3552c1d2c9da6fb58 to your computer and use it in GitHub Desktop.
Save lebedov/4da3552c1d2c9da6fb58 to your computer and use it in GitHub Desktop.
How to find globals accessed by a Python object.
#!/usr/bin/env python
"""
How to find globals accessed by a Python object.
"""
import inspect
import numpy as np
def allglobalvars(x):
"""
Find all globals accessed by an object.
"""
# Define internal recursively called function to enable tracking of the
# recursion level:
def recursive(x, seen=set(), level=0):
# Get locals of scope in which allglobalvars() was invoked; the
# following code ensures that the `locals_dict` contains the locals from
# the top scope regardless of the recursion level:
local_dict = inspect.currentframe(level+2).f_locals
level += 1
results = {}
if inspect.isbuiltin(x) or type(x) == np.ufunc:
return {x.__name__: x}
elif inspect.isroutine(x):
# Get symbols and globals accessed by function or method; we need to
# check co_freevars for defined symbols that are not in co_names:
if inspect.isfunction(x):
name_list = x.func_code.co_names+x.func_code.co_freevars
global_dict = x.func_globals
elif inspect.ismethod(x):
name_list = x.im_func.func_code.co_names+x.im_func.func_code.co_freevars
global_dict = x.im_func.func_globals
else:
raise ValueError('invalid input')
for name in name_list:
if name in seen:
pass
elif name in global_dict:
# If an accessed symbol is a global that can be imported as-is,
# include it in the dict of globals; if it is a method of a class,
# recurse into it:
results[name] = global_dict[name]
if not inspect.ismodule(global_dict[name]) and \
hasattr(global_dict[name], '__module__') and \
global_dict[name].__module__ == '__main__':
results.update(recursive(global_dict[name],
set([name]).union(seen), level))
elif name in local_dict:
results[name] = local_dict[name]
if not inspect.ismodule(local_dict[name]) and \
hasattr(local_dict[name], '__module__') and \
local_dict[name].__module__ == '__main__':
results.update(recursive(local_dict[name],
set([name]).union(seen), level))
else:
# Check if symbol is the name is an attribute of a global (i.e.,
# can be imported):
for r in results.keys():
if hasattr(results[r], name) and \
inspect.ismodule(getattr(results[r], name)):
results[r+'.'+name] = getattr(results[r], name)
else:
# Include globals of a class' parents:
if inspect.isclass(x):
# Parent classes other than object should be included in a class'
# globals:
for b in x.__bases__:
if b is not object:
results[b.__name__] = b
results.update(recursive(b, set([b.__name__]).union(seen), level))
# Recurse into class/object methods:
for f in inspect.getmembers(x, predicate=inspect.ismethod):
results.update(recursive(f[1], set([f[1].__name__]).union(seen), level))
return results
return recursive(x)
if __name__ == '__main__':
# Demo of usage:
import numpy as np
class Foo(object):
def bar(self, x):
return np.linalg.svd(x)
print allglobalvars(Foo)
print '---'
import math
f = lambda x: math.lgamma(x)
print allglobalvars(f)
print '---'
import pandas
print allglobalvars(pandas.DataFrame)
from allglobalvars import allglobalvars
import inspect
import random
from unittest import main, TestCase
def func1(x):
return x+random.random()
from numbers import Integral
def func2(x):
return isinstance(x, Integral)
class test_allglobalvars(TestCase):
def test_func(self):
r = allglobalvars(func1)
assert r.has_key('random') and inspect.ismodule(r['random'])
def test_func_imported(self):
r = allglobalvars(func2)
assert len(r.keys()) == 1 and \
r.has_key('Integral') and inspect.isclass(r['Integral'])
def test_class_member(self):
class Foo(object):
def foo(self, x):
return random.random()+x
r = allglobalvars(Foo)
assert len(r.keys()) == 1 and \
r.has_key('random') and inspect.ismodule(r['random'])
def test_class_instance_member(self):
class Foo(object):
def foo(self, x):
return random.random()+x
r = allglobalvars(Foo())
assert len(r.keys()) == 1 and \
r.has_key('random') and inspect.ismodule(r['random'])
def test_class_member_calls_func(self):
class Foo(object):
def foo(self, x):
return func1(x)
r = allglobalvars(Foo)
assert len(r.keys()) == 2 and \
r.has_key('random') and inspect.ismodule(r['random']) and \
r.has_key('func1') and inspect.isfunction(r['func1'])
def test_class_child(self):
class Foo(object):
def foo(self, x):
return random.random()+x
class Bar(Foo):
def foo(self, x):
return super(Bar, self).foo(x)
r = allglobalvars(Bar)
assert len(r.keys()) == 3 and \
r.has_key('random') and inspect.ismodule(r['random']) and \
r.has_key('Foo') and inspect.isclass(r['Foo']) and \
r.has_key('Bar') and inspect.isclass(r['Bar'])
def test_class_in_func(self):
class Foo(object):
def foo(self, x):
return random.random()+x
def bar(x):
return Foo().foo(x)
r = allglobalvars(bar)
assert len(r.keys()) == 2 and \
r.has_key('random') and inspect.ismodule(r['random'])
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment