[docs]classNode:""" A Python node, used to browse python module programatically for callables. """def__init__(self,name,target,module=None):self.name=nameself.target=targetifmodule:self.module=moduleelifisinstance(target,types.ModuleType):self.module=targetelifhasattr(target,'__module__'):self.module=target.__module__else:self.module=Nonedef__eq__(self,other):ifisinstance(other,type(self)):returnself.target==other.targetreturnself.target==otherdef__str__(self):returnself.namedef__repr__(self):returnf'Node({self.name})'@propertydefdoc(self):returninspect.getdoc(self.target)@propertydeftype(self):ifisinstance(self.target,types.ModuleType):return'module'ifisinstance(self.target,types.FunctionType):return'function'@propertydefcallables(self):"""Return the list of callables in this Node."""results=[]builtins=(types.BuiltinFunctionType,types.BuiltinMethodType)forname,memberininspect.getmembers(self.target):ifnotcallable(member):continueifname.startswith('__')orisinstance(member,builtins):continue# skip builtinsresults.append(Node(name,member))returnresults
[docs]@classmethoddeffactory(cls,name):""" Return a Node based on a string dotted python path. Examples:: Node.factory('your.module') Node.factory('your.module.your_object') Node.factory('your.module.your_object.some_attribute') """module=Noneparts=name.split('.')fori,partinreversed(list(enumerate(parts))):modname='.'.join(parts[:i+1])ifnotmodname:breaktry:module=importlib.import_module(modname)exceptImportError:continueelse:breakifmodule:ret=moduleforpartinparts[i+1:]:ifisinstance(ret,dict)andpartinret:ret=ret.get(part)elifisinstance(ret,list)andpart.isnumeric():ret=ret[int(part)]else:ret=getattr(ret,part,None)else:ret=Nonereturncls(name,ret,module)