Source code for cli2.node

import inspect
import importlib
import types


[docs]class Node: """ A Python node, used to browse python module programatically for callables. """ def __init__(self, name, target, module=None): self.name = name self.target = target if module: self.module = module elif isinstance(target, types.ModuleType): self.module = target elif hasattr(target, '__module__'): self.module = target.__module__ else: self.module = None def __eq__(self, other): if isinstance(other, type(self)): return self.target == other.target return self.target == other def __str__(self): return self.name def __repr__(self): return f'Node({self.name})' @property def doc(self): return inspect.getdoc(self.target) @property def type(self): if isinstance(self.target, types.ModuleType): return 'module' if isinstance(self.target, types.FunctionType): return 'function' @property def callables(self): """Return the list of callables in this Node.""" results = [] builtins = (types.BuiltinFunctionType, types.BuiltinMethodType) for name, member in inspect.getmembers(self.target): if not callable(member): continue if name.startswith('__') or isinstance(member, builtins): continue # skip builtins results.append(Node(name, member)) return results
[docs] @classmethod def factory(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 = None parts = name.split('.') for i, part in reversed(list(enumerate(parts))): modname = '.'.join(parts[:i + 1]) if not modname: break try: module = importlib.import_module(modname) except ImportError: continue else: break if module: ret = module for part in parts[i + 1:]: if isinstance(ret, dict) and part in ret: ret = ret.get(part) elif isinstance(ret, list) and part.isnumeric(): ret = ret[int(part)] else: ret = getattr(ret, part, None) else: ret = None return cls(name, ret, module)