diff --git a/LICENSE b/LICENSE index 4e9c3c4f..a0fc3f67 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -MODIFIED PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 -------------------------------------------- 1. This LICENSE AGREEMENT is between William J. Dierkes (a.k.a "BJ Dierkes"), diff --git a/README b/README index 3f2220ff..1557f1f8 100644 --- a/README +++ b/README @@ -6,7 +6,7 @@ DESCRIPTION: Cement is an advanced CLI Application Framework for Python. It promotes code re-use by way of plugins and helper libraries that can be shared between -any application built on cement. The MVC and overall framework design is +any application built on Cement. The MVC and overall framework design is very much inspired by the TurboGears2 web framework. Its goal is to introduce a standard, and feature-full platform for both simple and complex command line applications as well as support rapid development needs without sacrificing @@ -35,11 +35,11 @@ plugin system is designed to allow portability of re-usable code, and it is encouraged to contribute any plugins back to the project to extend the functionality of Cement. -The Cement CLI Application Framework for Python is Open Source and is -distributed under the Python Software Foundation License. When creating -plugins specifically for re-use within the community, please be sure to -follow the standard naming convention "HelloWorld Plugin for Cement" as an -example. The actual module name should be 'cement.plugins.helloworld'. +The Cement CLI Application Framework is Open Source and is distributed under +the Python Software Foundation License. When creating plugins specifically +for re-use within the community, please be sure to follow the standard naming +convention "HelloWorld Plugin for Cement" as an example. The actual module +name should be 'cement.plugins.helloworld'. GETTING STARTED: diff --git a/cement/core/app_setup.py b/cement/core/app_setup.py index cab5b3f2..ea0c211e 100644 --- a/cement/core/app_setup.py +++ b/cement/core/app_setup.py @@ -14,38 +14,35 @@ from cement.core.hook import register_hook, define_hook, run_hooks log = get_logger(__name__) def register_default_hooks(): - """Register Cement framework hooks.""" + """Registers Cement framework hooks.""" define_hook('options_hook') define_hook('post_options_hook') define_hook('validate_config_hook') define_hook('pre_plugins_hook') define_hook('post_plugins_hook') -def lay_cement(default_app_config=None, version_banner=None): +def lay_cement(config=None, banner=None): """ Primary method to setup an application for Cement. - Arguments: - - config => dict containing application config. - version_banner => Option txt displayed for --version + Keyword arguments: + config -- Dict containing application config. + banner -- Optional text to display for --version """ - vb = version_banner - if not version_banner: - vb = """%s version %s""" % ( - default_app_config['app_name'], - get_distribution(default_app_config['app_egg_name']).version - ) + if not banner: + banner = "%s version %s" % ( + config['app_name'], + get_distribution(config['app_egg_name']).version) namespace = CementNamespace( - label = 'global', - version = get_distribution(default_app_config['app_egg_name']).version, - required_api = CEMENT_API, - config = get_default_config(), - version_banner = vb, + label='global', + version=get_distribution(config['app_egg_name']).version, + required_api=CEMENT_API, + config=get_default_config(), + banner=banner, ) define_namespace('global', namespace) - namespaces['global'].config.update(default_app_config) + namespaces['global'].config.update(config) register_default_hooks() diff --git a/cement/core/command.py b/cement/core/command.py index 28397223..a7b611ad 100644 --- a/cement/core/command.py +++ b/cement/core/command.py @@ -12,18 +12,24 @@ from cement.core.exc import CementArgumentError log = get_logger(__name__) # FIXME: This method is so effing ugly. -def run_command(command_name): +def run_command(cmd_name=None): """ - Run the command or namespace-subcommand. + Run the command or namespace-subcommand as defined by the 'expose()' + decorator used on a Controller function. + + Keyword arguments: + cmd_name -- The command name as store in the global 'namespaces'. For + example, namespaces['global'].commands['cmd_name']. + """ - log.debug("processing passed command '%s'", command_name) - command_name = command_name.rstrip('*') - if command_name in namespaces.keys(): - namespace = command_name + log.debug("processing passed command '%s'", cmd_name) + cmd_name = cmd_name.rstrip('*') + if cmd_name in namespaces.keys(): + namespace = cmd_name else: namespace = 'global' - m = re.match('(.*)-help', command_name) + m = re.match('(.*)-help', cmd_name) if m and m.group(1) in namespaces.keys(): namespace = m.group(1) raise CementArgumentError, \ @@ -46,7 +52,7 @@ def run_command(command_name): pass # doesn't expect a result if namespace == 'global': - actual_cmd = command_name + actual_cmd = cmd_name else: try: actual_cmd = cli_args[1] @@ -62,4 +68,4 @@ def run_command(command_name): log.debug("executing command '%s'" % actual_cmd) func(cli_opts, cli_args) else: - raise CementArgumentError, "Unknown command, see --help?" \ No newline at end of file + raise CementArgumentError, "Unknown command '%s', see --help?" % actual_cmd \ No newline at end of file diff --git a/cement/core/namespace.py b/cement/core/namespace.py index e970140a..5ded4ae2 100644 --- a/cement/core/namespace.py +++ b/cement/core/namespace.py @@ -7,9 +7,26 @@ from cement.core.opt import init_parser class CementNamespace(object): """ - Class that handles plugins and namespaces (commands, options, hooks). + Class that handles plugins and namespaces. """ def __init__(self, label, version, required_api, **kw): + """ + Initialize CementNamespace class. + + Required arguments: + label -- Namespace label. Class is stored in the global + 'namespaces' dict as namespaces['label']. + version -- The version of the application. + required_api-- The required Cement API the application was built on. + + Optional keyword arguments: + description -- Description of the plugin/namespace (default: '') + commands -- A dict of command functions (default: {}) + is_hidden -- Boolean, whether command should display in --help + output (default: False) + config -- A config dict (default: None) + banner -- A version banner to display for --version (default: '') + """ self.label = label self.version = version self.required_api = required_api @@ -21,20 +38,20 @@ class CementNamespace(object): if kw.get('config', None): self.config.update(kw['config']) - if not kw.get('version_banner'): - vb = "%s version %s" % (self.label, self.version) + if not kw.get('banner'): + banner = "%s version %s" % (self.label, self.version) else: - vb = kw.get('version_banner') - self.options = kw.get('options', init_parser(version_banner=vb)) + banner = kw.get('banner') + self.options = kw.get('options', init_parser(banner=banner)) def define_namespace(namespace, namespace_obj): """ Define a namespace for commands, options, configuration, etc. - Arguments: - - namespace => label of the namespace - namespace_obj => CementNamespace object + Keyword arguments: + namespace -- Label of the namespace + namespace_obj -- CementNamespace object. Stored in global 'namespaces' + dict as namespaces['namespace'] """ if namespaces.has_key(namespace): raise CementRuntimeError, "Namespace '%s' already defined!" % namespace diff --git a/cement/core/opt.py b/cement/core/opt.py index c24d785c..cf743548 100644 --- a/cement/core/opt.py +++ b/cement/core/opt.py @@ -38,12 +38,18 @@ class Options(object): self.parser = OptionParser(formatter=fmt, version=version_banner) -def init_parser(version_banner=None): - """Create an OptionParser object and returns its parser member.""" +def init_parser(banner=None): + """ + Create an OptionParser object and returns its parser member. + + Keyword arguments: + banner -- Optional version banner to display for --version + + """ fmt = IndentedHelpFormatter( indent_increment=4, max_help_position=32, width=77, short_first=1 ) - parser = OptionParser(formatter=fmt, version=version_banner) + parser = OptionParser(formatter=fmt, version=banner) return parser diff --git a/cement/core/plugin.py b/cement/core/plugin.py index 0c2dc61c..25349621 100644 --- a/cement/core/plugin.py +++ b/cement/core/plugin.py @@ -132,7 +132,6 @@ def load_all_plugins(): elif opt.get_opt_string() == '--json': pass else: - print opt namespaces[namespace].options.add_option(opt) for res in run_hooks('post_plugins_hook'): diff --git a/cement/paste/templates/cementapp/+package+/appmain.py_tmpl b/cement/paste/templates/cementapp/+package+/appmain.py_tmpl index d1626cef..ed930ecb 100644 --- a/cement/paste/templates/cementapp/+package+/appmain.py_tmpl +++ b/cement/paste/templates/cementapp/+package+/appmain.py_tmpl @@ -29,7 +29,7 @@ def main(): # Warning: You shouldn't modify below this point unless you know what # you're doing. - lay_cement(default_config, version_banner=BANNER) + lay_cement(config=default_config, banner=BANNER) log = get_logger(__name__) log.debug("Cement Framework Initialized!") diff --git a/cement/paste/templates/cementapp/+package+/plugins/+package+_core.py_tmpl b/cement/paste/templates/cementapp/+package+/plugins/+package+_core.py_tmpl index d14120a0..2cf3cbbc 100644 --- a/cement/paste/templates/cementapp/+package+/plugins/+package+_core.py_tmpl +++ b/cement/paste/templates/cementapp/+package+/plugins/+package+_core.py_tmpl @@ -21,11 +21,11 @@ class {{package}}Plugin(CementPlugin): def __init__(self): CementPlugin.__init__(self, - label = '{{package}}', - version = VERSION, - description = 'Core plugin for {{package}}', - required_api = REQUIRED_CEMENT_API, - version_banner = BANNER, + label='{{package}}', + version=VERSION, + description='Core plugin for {{package}}', + required_api=REQUIRED_CEMENT_API, + banner=BANNER, is_hidden=True, ) diff --git a/cement/paste/templates/cementapp/+package+/plugins/example.py_tmpl b/cement/paste/templates/cementapp/+package+/plugins/example.py_tmpl index 49d428ee..bd55bcdb 100644 --- a/cement/paste/templates/cementapp/+package+/plugins/example.py_tmpl +++ b/cement/paste/templates/cementapp/+package+/plugins/example.py_tmpl @@ -25,11 +25,11 @@ class ExamplePlugin(CementPlugin): def __init__(self): CementPlugin.__init__(self, - label = 'example', - version = VERSION, - description = 'Example plugin for {{package}}', - required_api = REQUIRED_CEMENT_API, - version_banner=BANNER, + label='example', + version=VERSION, + description='Example plugin for {{package}}', + required_api=REQUIRED_CEMENT_API, + banner=BANNER, ) # plugin configurations can be setup this way diff --git a/cement/paste/templates/cementplugin/+project+/plugins/+plugin+.py_tmpl b/cement/paste/templates/cementplugin/+project+/plugins/+plugin+.py_tmpl index 34f4df0f..b4572a2b 100644 --- a/cement/paste/templates/cementplugin/+project+/plugins/+plugin+.py_tmpl +++ b/cement/paste/templates/cementplugin/+project+/plugins/+plugin+.py_tmpl @@ -27,11 +27,11 @@ class {{plugin}}Plugin(CementPlugin): def __init__(self): CementPlugin.__init__(self, - label = '{{plugin}}', - version = VERSION, - description = '{{plugin}} plugin for {{project}}', - required_api = REQUIRED_CEMENT_API, - version_banner=BANNER + label='{{plugin}}', + version=VERSION, + description='{{plugin}} plugin for {{project}}', + required_api=REQUIRED_CEMENT_API, + banner=BANNER ) # diff --git a/examples/helloworld/etc/helloworld/helloworld.conf b/examples/helloworld/etc/helloworld/helloworld.conf index b81dfdb8..e68d14b9 100644 --- a/examples/helloworld/etc/helloworld/helloworld.conf +++ b/examples/helloworld/etc/helloworld/helloworld.conf @@ -24,7 +24,7 @@ plugin_dir = ./var/lib/helloworld/plugins.d # show_plugin_load = true # toggle debug output... can be true, false, yes, no, 1, 0 -debug = true +debug = false # toggle the log level... can be info, warn, error, fatal, debug log_level = warn @@ -34,4 +34,4 @@ log_to_console = true # add any config options you'd like here # -# myoption = this is my option +# myoption = this is my option \ No newline at end of file diff --git a/examples/helloworld/helloworld/appmain.py b/examples/helloworld/helloworld/appmain.py index 79c56880..6525a48d 100644 --- a/examples/helloworld/helloworld/appmain.py +++ b/examples/helloworld/helloworld/appmain.py @@ -29,7 +29,7 @@ def main(): # Warning: You shouldn't modify below this point unless you know what # you're doing. - lay_cement(default_config, version_banner=BANNER) + lay_cement(config=default_config, banner=BANNER) log = get_logger(__name__) log.debug("Cement Framework Initialized!") diff --git a/examples/helloworld/helloworld/plugins/example.py b/examples/helloworld/helloworld/plugins/example.py index 4e8f686d..efd0cf76 100644 --- a/examples/helloworld/helloworld/plugins/example.py +++ b/examples/helloworld/helloworld/plugins/example.py @@ -25,11 +25,11 @@ class ExamplePlugin(CementPlugin): def __init__(self): CementPlugin.__init__(self, - label = 'example', - version = VERSION, - description = 'Example plugin for helloworld', - required_api = REQUIRED_CEMENT_API, - version_banner=BANNER, + label='example', + version=VERSION, + description='Example plugin for helloworld', + required_api=REQUIRED_CEMENT_API, + banner=BANNER, ) # plugin configurations can be setup this way diff --git a/examples/helloworld/helloworld/plugins/helloworld_core.py b/examples/helloworld/helloworld/plugins/helloworld_core.py index 73c58b1a..bf521973 100644 --- a/examples/helloworld/helloworld/plugins/helloworld_core.py +++ b/examples/helloworld/helloworld/plugins/helloworld_core.py @@ -21,11 +21,11 @@ class helloworldPlugin(CementPlugin): def __init__(self): CementPlugin.__init__(self, - label = 'helloworld', - version = VERSION, - description = 'Core plugin for helloworld', - required_api = REQUIRED_CEMENT_API, - version_banner = BANNER, + label='helloworld', + version=VERSION, + description='Core plugin for helloworld', + required_api=REQUIRED_CEMENT_API, + banner=BANNER, is_hidden=True, ) diff --git a/examples/helloworld/plugins/helloworld-plugins-sayhi/helloworld/plugins/sayhi.py b/examples/helloworld/plugins/helloworld-plugins-sayhi/helloworld/plugins/sayhi.py index 50cc5610..f19383f5 100644 --- a/examples/helloworld/plugins/helloworld-plugins-sayhi/helloworld/plugins/sayhi.py +++ b/examples/helloworld/plugins/helloworld-plugins-sayhi/helloworld/plugins/sayhi.py @@ -27,11 +27,11 @@ class sayhiPlugin(CementPlugin): def __init__(self): CementPlugin.__init__(self, - label = 'sayhi', - version = VERSION, - description = 'sayhi plugin for helloworld', - required_api = REQUIRED_CEMENT_API, - version_banner=BANNER + label='sayhi', + version=VERSION, + description='sayhi plugin for helloworld', + required_api=REQUIRED_CEMENT_API, + banner=BANNER ) # diff --git a/setup.py b/setup.py index de520e7d..2b8d2c8f 100644 --- a/setup.py +++ b/setup.py @@ -4,34 +4,42 @@ import sys, os version = '0.5.1' LONG = """ -Cement is a CLI Application Framework for Python. It promotes code re-use by -way of plugins and helper libraries that can be maintained internally, or -shared with the community. The MVC and overall framework design is very much -inspired by the TurboGears2 web framework. +Cement is an advanced CLI Application Framework for Python. It promotes code +re-use by way of plugins and helper libraries that can be shared between +any application built on Cement. The MVC and overall framework design is +very much inspired by the TurboGears2 web framework. Its goal is to introduce +a standard, and feature-full platform for both simple and complex command line +applications as well as support rapid development needs without sacrificing +quality. -At a minimum, Cement easily sets up the following: - - * Configuration file parsing [using ConfigObj] - * Command line arguments and option parsing [using OptParse] - * Logging [using Logger] - * Plugin support [partially using setuptools] - * Basic "hook" support - * Full MVC support for advanced application design - * Text output rendering with Genshi templates +At a minimum, Cement configures the following features for every application: +:: + * Multiple Configuration file parsing (default: /etc, ~/) + * Command line argument and option parsing + * Dual Console/File Logging Support + * Full Internal and External (3rd Party) Plugin support + * Basic "hook" support + * Full MVC support for advanced application design + * Text output rendering with Genshi templates + * Json output rendering allows other programs to access your CLI-API -These pieces are important for any command line application.. -Normally to accomplish what's listed above would require hundreds of lines of -code before you even begin working on your application logic. With Cement, the -above is configured with more or less a single command (via paste). +The above provides any level developer with a solid, and fully functional +cli application from the very start with more or less a single command via +the paster utility. Cement brings an end to the 'hack it out, and [maybe] +clean it up later' routine that we all find ourselves in under deadlines. -Cement is most generally used as a starting point from which to begin -developing a command line type application. That said, applications using -cement can also share plugins with either cement or other applications using -cement. +Any application can utilize existing plugins from the CementPlugins +project, or from other 3rd party resources to extend functionality. The +plugin system is designed to allow portability of re-usable code, and it +is encouraged to contribute any plugins back to the project to extend the +functionality of Cement. The Cement CLI Application Framework for Python is Open Source and is -distributed under the Python Software Foundation License. +distributed under the Python Software Foundation License. When creating +plugins specifically for re-use within the community, please be sure to +follow the standard naming convention "HelloWorld Plugin for Cement" as an +example. The actual module name should be 'cement.plugins.helloworld'. GETTING STARTED: