diff options
Diffstat (limited to 'base/base.py')
-rw-r--r-- | base/base.py | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/base/base.py b/base/base.py new file mode 100644 index 0000000..214d1d5 --- /dev/null +++ b/base/base.py @@ -0,0 +1,216 @@ +import logging +import os + +from inspect import isclass +from collections import deque +from os.path import join + + +from module import Module +from vars import __description__, __version__, header, prompt + + +log = logging.getLogger('devshell') +modules = {} + +def print_docstrings(module=None): + """ + Print out the docstrings for all methods in the specified module. + If no module is specified, then display docstrings for all modules. + + Arguments: + module: an object subclassing Module (optional) + """ + def _print_docstrings(name, module): + log.info(header(name)) + for prop in filter(lambda x: x[0] != '_', dir(module)): + if callable(getattr(module, prop)) and hasattr(module, '__doc__') \ + and getattr(module, prop).__doc__: + log.info(" |- [%s] %s" % (prop, + getattr(module, prop).__doc__.strip())) + if not module: + for name, module in modules.items(): + _print_docstrings(name, module) + else: + _print_docstrings(str(module.__class__).split('.')[-1], module) + +def load_modules(): + global modules + log.debug("Loading modules") + #TODO: better way to find modules + for f in os.listdir(os.path.abspath('modules')): + module_name, ext = os.path.splitext(f) + if ext == '.py': + exec "from modules import %s as module" % module_name + for item in dir(module): + obj = getattr(module, item) + if item[0] != '_' and isclass(obj): + log.debug('%s is subclass of Module: %s' %\ + (obj, issubclass(obj, Module))) + if item[0] != '_' and isclass(obj) and issubclass(obj, Module) \ + and obj is not Module: + modules[item.lower()] = obj + log.info(" * %s" % item) + del module + +def load_module(name, data=[]): + top = None + try: + log.debug("Loading %s.__init__(%s)" % (name, data)) + top = modules[name](*data) + except TypeError, e: + log.debug('got type error') + log.error("%s: %s" % (name, e)) + raise ModuleError('probably bad call to __init__' , e) + return top + +def shell(): + while True: + try: + data = raw_input('/'.join(shell.prompt) + '> ') + except (EOFError, KeyboardInterrupt): + print + break + if not data: continue + if data in ('quit', 'exit'): break + keyword = data.split()[0] + args = data.split()[1:] + + # Show the docstrings to all methods for all loaded modules + if keyword == 'help': + print_docstrings() + + # Go up a module in our stack + elif keyword in ('up', 'cd..', 'cd ..'): + shell.stack = shell.stack[:-1] + shell.prompt = shell.prompt[:-1] + + # Show the docstrings for all methods in our current module + elif keyword in ('ls', 'help', '?'): + if len(shell.stack): + print_docstrings(shell.stack[-1]) + else: + print_docstrings() + + # Flush our module stack + elif keyword == 'cd': + shell.stack = [] + shell.prompt = prompt + + # iPython. Never leave home without it. + elif keyword in ('py', 'python'): + os.system("ipython -noconfirm_exit") + else: + #figure out if there is a top + if len(shell.stack): + top = shell.stack[-1] + else: + top = None + output, top, params = do_command(data.split(), top) + if top: + shell.stack += [top] + shell.prompt += [top.__class__.__name__] + params +shell.stack = [] +shell.prompt = prompt + +def do_command(data, top=None): + + # Do some intelligent handling of unknown input. + # For the given input 'foo bar', we first check if we have the + # module 'foo' loaded, then we push it on the top of our module + # stack and check if it has the 'bar' attribute. If so, we call + # it with any remaining arguments + data = deque(data) + log.debug(data) + params = [] + + module = None + while len(data): + if not top: + log.debug('not top') + mod = data.popleft() + try: + module = modules[mod] + except KeyError, e: + log.error('%s is not a known module' % e.message) + return None, None, None + log.debug('mod is %s' % mod) + params = [] + while len(data): + log.debug('params so far %s' % params) + log.debug('inner loop %s' % data) + param = data.popleft() + if hasattr(module, param): + data.appendleft(param) + break + params += [param] + try: + top = module = load_module(mod, params) + mod_params = params + loaded_module = True + except ModuleError, e: + log.debug(e.reason) + break + else: + log.debug('is top') + params = [] + log.debug('params so far %s' % params) + param = data.popleft() + if hasattr(top, param): + log.debug("next property %s found in %s" % (param, top)) + top = getattr(top, param) + else: + log.debug("adding argument: %s" % param) + params += [param] + output = None + if len(params): + output = top(*params) + + if module: + return output, module, mod_params + else: + return output, None, None + +def setup_options(): + '''Sets up an options parser for all command line utilities + + Arguements: + none: none! + + Return Values: + opts: an object containing the options specified + args: remaining arguments + ''' + from optparse import OptionParser + parser = OptionParser(usage="%prog [options]", + version="%s %s" % ('%prog', __version__), + description=__description__) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', + help='Display verbose debugging output') + return parser.parse_args() + +def setup_logger(opts): + '''Configures the logger system to the environment + + Arguments: + opts: options from an OptionParser + + Return Values: + none: none! + ''' + global log + # Setup our logger + sh = logging.StreamHandler() + if opts.verbose: + log.setLevel(logging.DEBUG) + sh.setLevel(logging.DEBUG) + format = logging.Formatter("[%(levelname)s] %(message)s") + else: + log.setLevel(logging.INFO) + sh.setLevel(logging.INFO) + format = logging.Formatter("%(message)s") + sh.setFormatter(format) + log.addHandler(sh) + +__all__ = ['do_command', 'log', 'setup_options', 'load_module', 'shell', + 'print_docstrings', 'modules', 'setup_logger', 'load_modules']
\ No newline at end of file |