diff --git a/cement/__init__.py b/cement/__init__.py index ae058080..912b3a73 100644 --- a/cement/__init__.py +++ b/cement/__init__.py @@ -1,4 +1,4 @@ """Cement top level module""" __import__('pkg_resources').declare_namespace(__name__) -from cement.core.configuration import config, hooks, commands \ No newline at end of file +from cement.core.configuration import config, hooks, commands, options \ No newline at end of file diff --git a/cement/core/app_setup.py b/cement/core/app_setup.py index 9cf647ce..6cbb0852 100644 --- a/cement/core/app_setup.py +++ b/cement/core/app_setup.py @@ -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 diff --git a/cement/core/configuration.py b/cement/core/configuration.py index 8d5de4ff..1551f567 100644 --- a/cement/core/configuration.py +++ b/cement/core/configuration.py @@ -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 diff --git a/cement/core/options.py b/cement/core/options.py index 62eb5eff..4d607788 100644 --- a/cement/core/options.py +++ b/cement/core/options.py @@ -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() diff --git a/examples/CementExample/cement_example/core.py b/examples/CementExample/cement_example/core.py index 7dc8fa25..48531ebb 100644 --- a/examples/CementExample/cement_example/core.py +++ b/examples/CementExample/cement_example/core.py @@ -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) diff --git a/plugins/cement-plugins-clibasic/cement/plugins/clibasic.py b/plugins/cement-plugins-clibasic/cement/plugins/clibasic.py index 71649a74..05955a65 100644 --- a/plugins/cement-plugins-clibasic/cement/plugins/clibasic.py +++ b/plugins/cement-plugins-clibasic/cement/plugins/clibasic.py @@ -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 \ No newline at end of file