import importlib
import inspect
from fancytools.fcollections.NestedOrderedDict import NestedOrderedDict
[docs]class GetCallablesInPackage(NestedOrderedDict):
'''
Return a NestedOrderedDict of all functions and classes within a given package.
excluding 'private' modules beginning with '_'
========= ======================================
Arguments Description
========= ======================================
package package or string of path to package
to get all callables from
========= ======================================
===================== ======= ===============================
Optional Default Description
===================== ======= ===============================
modules_in_structure False include the modules of the
callables in the output
include_classes True include classes in the output
include_functions True include functions in the output
min_level 0 minimum depth level in the
package structure
max_level None maximum depth level in the
package structure
exclude_empty_pck True exclude sub-packages without
any callables from the output
===================== ======= ===============================
'''
def __init__(self, package, modules_in_structure=False,
include_classes=True, include_functions=False,
min_level=0, max_level=None, exclude_empty_pck=True):
'''
'''
NestedOrderedDict.__init__(self)
if isinstance(package, basestring):
package = importlib.import_module(package)
self._package = package
self._include_classes = include_classes
self._include_functions = include_functions
self._modules_in_structure = modules_in_structure
self._min_level = min_level
self._max_level = max_level
self._objects = [package]
self._buildRecursive(package, self, 0)
if exclude_empty_pck:
self._cleanRecursive(self)
def _cleanRecursive(self, subSelf):
'''
Delete all NestedOrderedDict that haven't any entries.
'''
for key, item in subSelf.iteritems():
if self.isNestedDict(item):
if not item:
subSelf.pop(key)
else:
self._cleanRecursive(item)
def _buildRecursive(self, module, subSelf, level):
for (name, obj) in inspect.getmembers(module):
# take only 'public' modules
if name[0] == '_':
continue
#print name, level
if inspect.ismodule(obj):
#don't handle modules thats have been used before:
if obj in self._objects:
continue
self._objects.append(obj)
if obj.__name__.startswith(self._package.__name__):
#either use module for submenus or check whether module is actually a package:
if self._modules_in_structure or obj.__file__.endswith('__init__.pyc'):
#limit max recursion level
if not self._max_level or level <= self._max_level:
#if self._max_level:
l = subSelf[name] = NestedOrderedDict()
self._buildRecursive(obj, l, level+1)
else:
#use same menu for all classes of all files on one folder/module
self._buildRecursive(obj, subSelf, level)
elif level >= self._min_level:
# if object is a class
if (
( self._include_classes and inspect.isclass(obj) ) or #( not self._include_classes or ( self._include_classes and inspect.isclass(obj) ) or
# and/or a function
( self._include_functions and inspect.isfunction(obj) ) #( not self._include_functions or ( self._include_functions and inspect.isfunction(obj)) )
):
# if object belongs to its parent module (mod is not imported)
# OR module paths are the same (in case of from mod import cls and cls.name == mod.name)
try:
if self.belongsToModule(obj, module):
subSelf[name] = obj
except AttributeError:
pass # obj has no __module__ is e.g. a string
@staticmethod
[docs] def belongsToModule(obj, module):
'''Returns True is an object belongs to a module.'''
return obj.__module__ == module.__name__ or obj.__module__.startswith(module.__name__)
[docs] def buildHirarchy(self, horizontal_operation=None, vertical_operation=None):
'''Walk through the nested dict structure and executes
horizontal_operation(name, callable) resp.
vertical_operation(name, callable) if defined.
'''
def buildRecursive(pkey, pval):
if self.isNestedDict(pval):
if vertical_operation:
vertical_operation(pkey, pval)
for key, val in pval.iteritems():
#if not isinstance(val, NestedOrderedDict):
buildRecursive(key, val)
else:
if horizontal_operation:
horizontal_operation(pkey, pval)
buildRecursive(self._package.__name__, self)
@staticmethod
[docs] def isNestedDict(instance):
'''convenience function for
>>> isinstance(instance, NestedOrderedDict)'''
return isinstance(instance, NestedOrderedDict)
if __name__ == '__main__':
import sys
import numpy
g = GetCallablesInPackage(numpy, include_functions=True)
print g
class printClassStructure(object):
def __init__(self):
self.indent = ''
self._hor_as_last_called = True
def hor(self, key, item):
sys.stdout.write("%s%s - " %(self.indent,key) )
self.indent = ''
self._hor_as_last_called = True
def vert(self, key, item):
if self._hor_as_last_called:
print ''
print "%s-> %s" %(self.indent,key)
self.indent += '\t'
self._hor_as_last_called = False
p = printClassStructure()
g.buildHirarchy(horizontal_operation=p.hor,
vertical_operation=p.vert)