summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorYaakov Nemoy <loupgaroublond@gmail.com>2008-10-01 19:50:34 -0400
committerYaakov M. Nemoy <loupgaroublond@gmail.com>2008-10-01 19:50:34 -0400
commitcfb2820e1051de15e37c4a3d12be9b5514f81610 (patch)
tree45f5706906db6ee17542da263ebf79b9695a6228 /base
parent7810e072debc0255de3135874e5c46f156f52d4a (diff)
downloadfedora-devshell-cfb2820e1051de15e37c4a3d12be9b5514f81610.tar.gz
fedora-devshell-cfb2820e1051de15e37c4a3d12be9b5514f81610.tar.xz
fedora-devshell-cfb2820e1051de15e37c4a3d12be9b5514f81610.zip
Breaks everything up into seperate files.
I had a problem where having Module in the local namespace was not the same as having Module in the non local namespace (via an import). Somehow breaking it down this way seemed simpler.
Diffstat (limited to 'base')
-rw-r--r--base/__init__.py0
-rw-r--r--base/base.py216
-rw-r--r--base/exceptions.py4
-rw-r--r--base/module.py4
-rw-r--r--base/vars.py10
5 files changed, 234 insertions, 0 deletions
diff --git a/base/__init__.py b/base/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/__init__.py
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
diff --git a/base/exceptions.py b/base/exceptions.py
new file mode 100644
index 0000000..471dd0e
--- /dev/null
+++ b/base/exceptions.py
@@ -0,0 +1,4 @@
+class ModuleError(Exception):
+ def __init__(self, reason, error):
+ self.reason = reason
+ self.error = error
diff --git a/base/module.py b/base/module.py
new file mode 100644
index 0000000..d527ea0
--- /dev/null
+++ b/base/module.py
@@ -0,0 +1,4 @@
+
+class Module(object):
+ """ Our parent class for all command modules """
+ pass
diff --git a/base/vars.py b/base/vars.py
new file mode 100644
index 0000000..232432b
--- /dev/null
+++ b/base/vars.py
@@ -0,0 +1,10 @@
+from os.path import join, expanduser
+
+__version__ = '0.0.1'
+__description__ = 'A shell for hacking on the Fedora project'
+
+FEDORA_DIR = join(expanduser('~'), 'code', 'fedora')
+DEVSHELL_DIR = join(expanduser('~'), '.devshell')
+
+header = lambda x: "%s %s %s" % ('=' * 2, x, '=' * (76 - len(x)))
+prompt = ['\033[34;1mfedora\033[0m'] \ No newline at end of file