reworking command/option parsgin

This commit is contained in:
BJ Dierkes 2009-12-11 05:46:32 -06:00
parent e8bb323232
commit 215d62383e
6 changed files with 78 additions and 36 deletions

View File

@ -1,4 +1,4 @@
"""Cement top level module"""
__import__('pkg_resources').declare_namespace(__name__)
from cement.core.configuration import config, hooks, commands
from cement.core.configuration import config, hooks, commands, options

View File

@ -1,14 +1,15 @@
"""Cement methods to setup the framework for applications using it."""
import os
import sys, os
import re
from pkg_resources import get_distribution
from cement import plugins as cement_plugins
from cement import config, hooks, commands
from cement import config, hooks, commands, options
from cement.core.log import setup_logging, get_logger
from cement.core.options import init_parser, parse_options
from cement.core.options import set_config_opts_per_cli_opts
from cement.core.exc import CementConfigError, CementRuntimeError
from cement.core.exc import CementConfigError, CementRuntimeError, \
CementArgumentError
from cement.core.configuration import set_config_opts_per_file, \
validate_config
@ -59,6 +60,7 @@ def define_hook_namespace(namespace):
raise CementRuntimeError, "Hook name '%s' already defined!" % namespace
hooks[namespace] = []
def register_hook(**kwargs):
"""
Decorator function for plugins to register hooks. Used as:
@ -70,12 +72,14 @@ def register_hook(**kwargs):
def decorate(func):
if not hooks.has_key(func.__name__):
raise CementRuntimeError, "Hook name '%s' is not define!" % func.__name__
# (1) is the list of registered hooks in the namespace
hooks[func.__name__].append(
(int(kwargs.get('weight', 0)), func.__name__, func)
)
return func
return decorate
def run_hooks(namespace, *args, **kwargs):
"""
Run all defined hooks in the namespace. Returns a list of return data.
@ -85,8 +89,11 @@ def run_hooks(namespace, *args, **kwargs):
hooks[namespace].sort() # will order based on weight
for hook in hooks[namespace]:
res = hook[2](*args, **kwargs)
# FIXME: Need to validate the return data somehow
yield res
def define_command_namespace(namespace):
"""
Define a command namespace that plugins can register commands in.
@ -96,6 +103,7 @@ def define_command_namespace(namespace):
return
commands[namespace] = {}
def register_command(**kwargs):
"""
Decorator function for plugins to register commands. Used as:
@ -120,25 +128,47 @@ def register_command(**kwargs):
return func
return decorate
def run_command(command_name, cli_args, cli_opts):
def run_command(command_name):
"""
Run all defined hooks in the namespace. Returns a list of return data.
"""
if not commands.has_key(command_name):
CementArgumentError, "Unknown command. See --help?" % command_name
m = re.match('(.*)-help', cli_args[0])
command_name = command_name.lstrip('*')
if command_name in commands.keys():
namespace = command_name
elif command_name.rstrip('-help') in commands.keys():
namespace = command_name.rstrip('-help')
raise CementArgumentError, \
"'%s' is a *namespace, not a command. See '%s --help' instead." % (namespace, namespace)
else:
namespace = 'global'
(cli_opts, cli_args) = parse_options(options, cmd_namespace=namespace)
if namespace == 'global':
actual_cmd = command_name
else:
try:
actual_cmd = cli_args[1]
except IndexError:
raise CementArgumentError, \
"Missing sub-command. See '%s --help?" % (namespace)
m = re.match('(.*)-help', actual_cmd)
if m:
if commands.has_key(m.group(1)):
cmd = commands[m.group(1)](cli_opts, cli_args)
if commands[namespace].has_key(m.group(1)):
cmd = commands[namespace][m.group(1)](cli_opts, cli_args)
cmd.help()
else:
raise CementArgumentError, "Unknown command, see --help?"
raise CementArgumentError, \
"Unknown command '%s'. See --help?" % actual_cmd
elif commands.has_key(cli_args[0]):
cmd = commands[cli_args[0]](cli_opts, cli_args)
elif commands[namespace].has_key(actual_cmd):
cmd = commands[namespace][actual_cmd](cli_opts, cli_args)
cmd.run()
else:
raise CementArgumentError, "Unknown command, see --help?"
@ -152,7 +182,7 @@ def lay_cement(default_app_config=None, version_banner=None):
config => dict containing application config.
version_banner => Option txt displayed for --version
"""
global config
global config, options
config.update(default_app_config)
validate_config(config)
@ -166,12 +196,8 @@ def lay_cement(default_app_config=None, version_banner=None):
set_config_opts_per_file(config, config['app_module'], cf)
options = init_parser(version_banner)
options = load_all_plugins(options)
(cli_opts, cli_args) = parse_options(options)
config = set_config_opts_per_cli_opts(config, cli_opts)
load_all_plugins()
setup_logging()
return (cli_opts, cli_args)
def load_plugin(plugin):
@ -219,11 +245,12 @@ def load_plugin(plugin):
return plugin_cls
def load_all_plugins(options):
def load_all_plugins():
"""
Attempt to load all enabled plugins. Passes the existing config and
options object to each plugin and allows them to add/update each.
"""
global options
for plugin in config['enabled_plugins']:
plugin_cls = load_plugin(plugin)
@ -240,5 +267,4 @@ def load_all_plugins(options):
else:
options.parser.add_option(opt)
return options

View File

@ -20,6 +20,9 @@ hooks = {}
# commands dictionary
commands = {'global' : {}}
# OptParse options object *we set this up later
options = None
def set_config_opts_per_file(tmpconfig, section, config_file):
"""
Parse config file options for into config dict. Will do nothing if the

View File

@ -51,7 +51,7 @@ def parse_options(options_obj, cmd_namespace='global'):
Returns => a tuple of (options, args)
"""
o = options_obj
cmd_txt = ''
line = ' '
if commands:
@ -83,14 +83,20 @@ def parse_options(options_obj, cmd_namespace='global'):
if line != ' ':
cmd_txt += "%s\n" % line
if cmd_namespace != 'global':
namespace_txt = ' %s' % cmd_namespace
cmd_type_txt = 'SUBCOMMAND'
else:
namespace_txt = ''
cmd_type_txt = 'COMMAND'
script = os.path.basename(sys.argv[0])
o.parser.usage = """ %s [COMMAND] --(OPTIONS)
o.parser.usage = """ %s%s [%s] --(OPTIONS)
Commands:
%s
Help? try [COMMAND]-help""" % (script, cmd_txt)
Help? try [%s]-help""" % (script, namespace_txt, cmd_type_txt, cmd_txt, cmd_type_txt)
o.add_default_options()
(opts, args) = o.parser.parse_args()

View File

@ -3,7 +3,7 @@
import sys, os
import re
from cement import config, commands
from cement import config, commands, options
from cement.core.log import get_logger
from cement.core.app_setup import lay_cement, ensure_abi_compat, run_command
from cement.core.exc import CementArgumentError
@ -39,18 +39,17 @@ def main():
# Warning: You shouldn't modify below this point unless you know what
# you're doing.
# tie everything together
(cli_opts, cli_args) = lay_cement(dcf)
lay_cement(dcf)
log = get_logger(__name__)
log.debug("Cement Framework Initialized!")
# react to the passed command.
# react to the passed command. command should be the first arg always
try:
if not len(cli_args) > 0:
if not len(sys.argv) > 0:
raise CementArgumentError, "A command is required. See --help?"
run_command(cli_args[0], cli_args, cli_opts)
run_command(sys.argv[1])
except CementArgumentError, e:
print("CementArgumentError > %s" % e)

View File

@ -86,7 +86,7 @@ class ListPluginsCommand(CementCommand):
)
print
@register_command(name='list-hooks', hidden=True)
@register_command(name='list-hooks', is_hidden=True)
class ListHiddenCommandsCommand(CementCommand):
def run(self):
from cement import hooks
@ -104,8 +104,16 @@ class ListHiddenCommandsCommand(CementCommand):
print
print 'Hidden commands'
print '-' * 77
for cmd in commands:
if commands[cmd].hidden:
print cmd
print "%s-help" % cmd
for nam in commands:
for cmd in commands[nam]:
if commands[nam][cmd].is_hidden:
if nam != 'global':
print '%s %s' % (nam, cmd)
else:
print cmd
if nam == 'global':
print '%s-help' % cmd
else:
print '%s %s-help' % (nam, cmd)
print