From 2d1cdf5d2d050a331c38cfbbe745440cf754a9c3 Mon Sep 17 00:00:00 2001 From: BJ Dierkes Date: Mon, 8 Sep 2014 21:01:58 -0500 Subject: [PATCH] Resolves Issue #275 --- ChangeLog | 2 + doc/source/api/core/arg.rst | 3 +- .../examples/abstract_base_controllers.rst | 163 ++++++++---------- .../{version_option.rst => app_version.rst} | 4 +- ...ents.rst => arbitrary_extra_arguments.rst} | 20 +-- doc/source/examples/bash_auto_completion.rst | 83 ++++----- .../examples/controllers_with_same_label.rst | 90 +++++----- doc/source/examples/index.rst | 4 +- .../examples/load_extensions_via_config.rst | 20 ++- .../examples/multiple_stacked_controllers.rst | 139 +++++++-------- examples/__init__.py | 0 examples/abstract_base_controllers/myapp.py | 104 +++++++++++ examples/app_version/myapp.py | 35 ++++ examples/arbitrary_extra_arguments/myapp.py | 48 ++++++ examples/bash_auto_completion/myapp.py | 85 +++++++++ examples/bash_auto_completion/myapp.rc | 66 +++++++ examples/controllers_with_same_label/myapp.py | 72 ++++++++ .../load_extensions_via_config/myapp.conf | 2 + examples/load_extensions_via_config/myapp.py | 46 +++++ .../multiple_stacked_controllers/myapp.py | 89 ++++++++++ 20 files changed, 793 insertions(+), 282 deletions(-) rename doc/source/examples/{version_option.rst => app_version.rst} (95%) rename doc/source/examples/{handling_extra_arguments.rst => arbitrary_extra_arguments.rst} (81%) create mode 100644 examples/__init__.py create mode 100644 examples/abstract_base_controllers/myapp.py create mode 100644 examples/app_version/myapp.py create mode 100644 examples/arbitrary_extra_arguments/myapp.py create mode 100644 examples/bash_auto_completion/myapp.py create mode 100644 examples/bash_auto_completion/myapp.rc create mode 100644 examples/controllers_with_same_label/myapp.py create mode 100644 examples/load_extensions_via_config/myapp.conf create mode 100644 examples/load_extensions_via_config/myapp.py create mode 100644 examples/multiple_stacked_controllers/myapp.py diff --git a/ChangeLog b/ChangeLog index adfac752..e94187ed 100755 --- a/ChangeLog +++ b/ChangeLog @@ -52,6 +52,8 @@ Features: that code. * :issue:`270` - Add support for multiple template_dirs * :issue:`274` - Add cement.core.interface.list function + * :issue:`775` - Added `examples/` directory with working examples based + on Examples section of the documentation. Incompatible: diff --git a/doc/source/api/core/arg.rst b/doc/source/api/core/arg.rst index aba18cbf..7cd35fb6 100644 --- a/doc/source/api/core/arg.rst +++ b/doc/source/api/core/arg.rst @@ -6,5 +6,4 @@ .. automodule:: cement.core.arg :members: :undoc-members: - :private-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: diff --git a/doc/source/examples/abstract_base_controllers.rst b/doc/source/examples/abstract_base_controllers.rst index ffcfb535..ce56a49c 100644 --- a/doc/source/examples/abstract_base_controllers.rst +++ b/doc/source/examples/abstract_base_controllers.rst @@ -5,168 +5,145 @@ For larger, complex applications it is often very useful to have abstract base controllers that hold shared arguments and commands that a number of other controllers have in common. Note that in the example below, you can not override the Meta.arguments in a sub-class or you overwrite the shared -arguments. That said, you can add any number of additional commands in the -sub-class but still maintain the existing shared commands. +arguments, but it is possible to `append` to them in order to maintain the +defaults while having unique options/arguments for the sub-classed controller. +As well, you can add any number of additional commands in the sub-class but +still maintain the existing shared commands (or override them as necessary). .. code-block:: python - from cement.core import foundation, controller, handler - - class AbstractBaseController(controller.CementBaseController): + from cement.core import foundation, handler + from cement.core.controller import CementBaseController, expose + + class AbstractBaseController(CementBaseController): """ This is an abstract base class that is useless on its own, but used by other classes to sub-class from and to share common commands and - arguments. - + arguments. This should not be confused with the `MyAppBaseController` + used as the ``base_controller`` namespace. + """ class Meta: + stacked_on = 'base' + stacked_type = 'nested' arguments = [ ( ['-f', '--foo'], dict(help='notorious foo option')), ] - + def _setup(self, base_app): - """ - Add a common object that is useful in multiple sub-classed - controllers. - - """ super(AbstractBaseController, self)._setup(base_app) - self.my_shared_obj = dict() - - @controller.expose(hide=True) + + # add a common object that will be used in any sub-class + self.reusable_dict = dict() + + @expose(hide=True) def default(self): """ This command will be shared within all controllers that sub-class from here. It can also be overridden in the sub-class, but for this example we are making it dynamic. - + """ # do something with self.my_shared_obj here? - print(self.my_shared_obj) - + if 'some_key' in self.reusable_dict.keys(): + pass + # or do something with parsed args? if self.app.pargs.foo: - print "Foo option was passed!" - + print "Foo option was passed with value: %s" % self.app.pargs.foo + # or maybe do something dynamically - if self._meta.label == 'controller1': - # do something for controller1 - print("Inside Controller1.default()") - else: - # do something else - print("Inside %s.default()" % self._meta.label.capitalize()) - - class MyBaseController(controller.CementBaseController): + print("Inside %s.default()" % self.__class__.__name__) + + class MyAppBaseController(CementBaseController): """ This is the application base controller, but we don't want to use our abstract base class here. - + """ class Meta: label = 'base' - - @controller.expose(hide=True) + + @expose(hide=True) def default(self): - print("Inside MyBaseController.default()") - + print("Inside MyAppBaseController.default()") + class Controller1(AbstractBaseController): """ This controller sub-classes from the abstract base class as to inherite shared arguments, and commands. - + """ class Meta: label = 'controller1' - description = 'Controller1 Does Amazing Things' - - @controller.expose() + + @expose() def command1(self): print("Inside Controller1.command1()") - + class Controller2(AbstractBaseController): """ - This controller also sub-classes from the abstract base class as to + This controller also sub-classes from the abstract base class as to inherite shared arguments, and commands. - + """ class Meta: label = 'controller2' - description = 'Controller2 Also Does Amazing Things' - - @controller.expose() + + @expose() def command2(self): print("Inside Controller2.command2()") - - app = foundation.CementApp('myapp', base_controller=MyBaseController) - try: - # register non-base controller handlers - handler.register(Controller1) - handler.register(Controller2) - - app.setup() - app.run() - finally: - app.close() - + def main(): + app = foundation.CementApp('myapp') + + try: + # register controllers handlers + handler.register(MyAppBaseController) + handler.register(Controller1) + handler.register(Controller2) + + app.setup() + app.run() + finally: + app.close() + + if __name__ == '__main__': + main() + And: .. code-block:: text - $ python test.py - Inside MyBaseController.default() + $ python myapp.py + Inside MyAppBaseController.default() - - $ python test.py --help - usage: test.py -opt1 --opt2=VAL [arg1] [arg2] ... + $ python myapp.py --help + usage: myapp.py (sub-commands ...) [options ...] {arguments ...} Base Controller commands: controller1 - Controller1 Does Amazing Things + Controller1 Controller controller2 - Controller2 Also Does Amazing Things + Controller2 Controller optional arguments: -h, --help show this help message and exit --debug toggle debug output --quiet suppress all output - - - $ python test.py controller1 --help - usage: test.py controller1 -opt1 --opt2=VAL [arg1] [arg2] ... - - Controller1 Does Amazing Things - - commands: - - command1 - - optional arguments: - -h, --help show this help message and exit - --debug toggle debug output - --quiet suppress all output - -f FOO, --foo FOO notorious foo option - - - $ python test.py controller1 command1 - Inside Controller1.command1() - $ python test.py controller2 command2 - Inside Controller2.command2() - - - $ python test.py controller1 --foo=bar - {} - Foo option was passed! + $ python myapp.py controller1 Inside Controller1.default() + $ python myapp.py controller1 --foo=bar + Foo option was passed with value: bar + Inside Controller1.default() - $ python test.py controller2 --foo=bar - {} - Foo option was passed! + $ python myapp.py controller2 Inside Controller2.default() + diff --git a/doc/source/examples/version_option.rst b/doc/source/examples/app_version.rst similarity index 95% rename from doc/source/examples/version_option.rst rename to doc/source/examples/app_version.rst index 2a92aead..887443be 100644 --- a/doc/source/examples/version_option.rst +++ b/doc/source/examples/app_version.rst @@ -1,5 +1,5 @@ -Adding a --version Option to Your App -===================================== +Adding a Version Option to Your App +=================================== Almost every app out there supports a ``--version`` option of some sort that provides the end user with version information. This is pretty important to diff --git a/doc/source/examples/handling_extra_arguments.rst b/doc/source/examples/arbitrary_extra_arguments.rst similarity index 81% rename from doc/source/examples/handling_extra_arguments.rst rename to doc/source/examples/arbitrary_extra_arguments.rst index ef428365..d6d4e7a3 100644 --- a/doc/source/examples/handling_extra_arguments.rst +++ b/doc/source/examples/arbitrary_extra_arguments.rst @@ -9,7 +9,7 @@ line, rather than option flags. For example: $ myapp some-command some-argument --foo=bar -In the above, `some-command` would be the function under whatever controller +In the above, ``some-command`` would be the function under whatever controller it is exposed from, and `some-argument` would be just an arbtrary argument. In most cases, the argument within the code is generic, but its uses vary. For example: @@ -21,8 +21,8 @@ For example: $ myapp create-group admins -In the above, the sub-commands are `create-user` and `create-group`, and in -this use case they are under the same controller. The `argument` however +In the above, the sub-commands are ``create-user`` and ``create-group``, and +in this use case they are under the same controller. The ``argument`` however differs for each command, though it is passed to the app the same (the first positional argument, that is not a controller/command). @@ -47,16 +47,12 @@ Example label = 'second' stacked_type = 'nested' stacked_on = 'base' + description = 'this is the second controller namespace' arguments = [ - ( - ['-f', '--foo'], - dict(help='Notorious foo option', action='store') - ), - ( - ['extra_arguments'], - dict(action='store', nargs='*') - ), - + (['-f', '--foo'], + dict(help='the notorious foo option', action='store')), + (['extra_arguments'], + dict(action='store', nargs='*')), ] @expose() diff --git a/doc/source/examples/bash_auto_completion.rst b/doc/source/examples/bash_auto_completion.rst index 460440f0..81753d3d 100644 --- a/doc/source/examples/bash_auto_completion.rst +++ b/doc/source/examples/bash_auto_completion.rst @@ -8,7 +8,7 @@ The difficulty is that this auto-completion code must be maintained outside of Cement and your application code, and be implemented in the shell environment generally by use of an "RC" file, or similar means. This then must be updated anytime your application is modified (or atleast any time the -sub-commands/controllers are modified). +sub-commands/controllers/arguments are modified). Note that, in the future, we would love to include some form of "BASH RC Generator" that will do this for you, however in the meantime the @@ -23,8 +23,8 @@ sub-commands, that are implemented via nested-controllers. .. code-block:: python + from cement.core import foundation, handler from cement.core.controller import CementBaseController, expose - from cement.core import foundation, controller, handler class BaseController(CementBaseController): class Meta: @@ -32,56 +32,52 @@ sub-commands, that are implemented via nested-controllers. @expose() def base_cmd1(self): - print("Inside BaseController.cmd1()") - - @expose() - def base_cmd2(self): - print("Inside BaseController.cmd2()") + print("Inside BaseController.base_cmd1()") class EmbeddedController(CementBaseController): class Meta: label = 'embedded' - description = "Embedded with Base Namespace" + description = "embedded with base namespace" stacked_on = 'base' stacked_type = 'embedded' @expose() - def embedded_cmd1(self): - print("Inside EmbeddedController.cmd1()") + def base_cmd2(self): + print("Inside EmbeddedController.base_cmd2()") @expose() - def embedded_cmd2(self): - print("Inside EmbeddedController.cmd2()") + def embedded_cmd3(self): + print("Inside EmbeddedController.embedded_cmd3()") class SecondLevelController(CementBaseController): class Meta: label = 'second' - description = "Second Level Namespace After The Base" + description = '' stacked_on = 'base' stacked_type = 'nested' @expose() - def second_cmd1(self): - print("Inside SecondLevelController.cmd1()") + def second_cmd4(self): + print("Inside SecondLevelController.second_cmd4()") @expose() - def second_cmd2(self): - print("Inside SecondLevelController.cmd2()") + def second_cmd5(self): + print("Inside SecondLevelController.second_cmd5()") class ThirdLevelController(CementBaseController): class Meta: label = 'third' - description = "Third Level Namespace After The Second" + description = '' stacked_on = 'second' stacked_type = 'nested' @expose() - def third_cmd1(self): - print("Inside ThirdLevelController.cmd1()") + def third_cmd6(self): + print("Inside ThirdLevelController.third_cmd6()") @expose() - def third_cmd2(self): - print("Inside ThirdLevelController.cmd2()") + def third_cmd7(self): + print("Inside ThirdLevelController.third_cmd7()") class MyApp(foundation.CementApp): class Meta: @@ -127,42 +123,30 @@ This looks like: base-cmd2 - embedded-cmd1 - - embedded-cmd2 + embedded-cmd3 second - Second Level Namespace After The Base - - optional arguments: - -h, --help show this help message and exit - --debug toggle debug output - --quiet suppress all output $ python myapp.py second --help - Second Level Namespace After The Base - commands: - second-cmd1 + second-cmd4 - second-cmd2 + second-cmd5 third - Third Level Namespace After The Second $ python myapp.py second third --help - Third Level Namespace After The Second - commands: - third-cmd1 + third-cmd6 + + third-cmd7 - third-cmd2 For demonstration purposes, we are going to create a BASH alias here so that @@ -196,6 +180,8 @@ example and is not intended to be copy and pasted: .. code-block:: bash + alias myapp="python ./myapp.py" + _myapp_complete() { local cur prev BASE_LEVEL @@ -207,7 +193,7 @@ example and is not intended to be copy and pasted: # SETUP THE BASE LEVEL (everything after "myapp") if [ $COMP_CWORD -eq 1 ]; then COMPREPLY=( $(compgen \ - -W "base-cmd1 base-cmd2 embedded-cmd1 embedded-cmd2 second" \ + -W "base-cmd1 base-cmd2 embedded-cmd3 second" \ -- $cur) ) @@ -218,7 +204,7 @@ example and is not intended to be copy and pasted: # HANDLE EVERYTHING AFTER THE SECOND LEVEL NAMESPACE "second") COMPREPLY=( $(compgen \ - -W "second-cmd1 second-cmd2 third" \ + -W "second-cmd4 second-cmd5 third" \ -- $cur) ) ;; @@ -240,7 +226,7 @@ example and is not intended to be copy and pasted: # HANDLE EVERYTHING AFTER THE THIRD LEVEL NAMESPACE "third") COMPREPLY=( $(compgen \ - -W "third-cmd1 third-cmd2" \ + -W "third-cmd6 third-cmd7" \ -- $cur) ) ;; @@ -262,6 +248,7 @@ example and is not intended to be copy and pasted: complete -F _myapp_complete myapp + You would then "source" the RC file: .. code-block:: bash @@ -270,7 +257,7 @@ You would then "source" the RC file: In the "real world" you would probably put this in a system wide location -such at `/etc/profile.d` or similar (in a production deployment). +such at ``/etc/profile.d`` or similar (in a production deployment). Finally, this is what it looks like: @@ -278,13 +265,13 @@ Finally, this is what it looks like: # show all sub-commands at the base level $ myapp [tab] [tab] - base-cmd1 base-cmd2 embedded-cmd1 embedded-cmd2 second + base-cmd1 base-cmd2 embedded-cmd3 second # auto-complete a partial matching sub-command $ myapp base [tab] $ myapp base-cmd [tab] [tab] - base-cmd1 base-cmd2 + base-cmd1 base-cmd2 # auto-complete a full matching sub-command $ myapp sec [tab] @@ -293,9 +280,9 @@ Finally, this is what it looks like: # show all sub-commands under the second namespace $ myapp second [tab] [tab] - second-cmd1 second-cmd2 third + second-cmd4 second-cmd5 third # show all sub-commands under the third namespace $ myapp second third [tab] [tab] - third-cmd1 third-cmd2 + third-cmd6 third-cmd7 diff --git a/doc/source/examples/controllers_with_same_label.rst b/doc/source/examples/controllers_with_same_label.rst index 5c49f84f..76651981 100644 --- a/doc/source/examples/controllers_with_same_label.rst +++ b/doc/source/examples/controllers_with_same_label.rst @@ -26,26 +26,29 @@ and a 'hosts' controller and we want to have a 'list' sub-command under both: .. code-block:: python - from cement.core import foundation, controller, handler + from cement.core import foundation, handler + from cement.core.controller import CementBaseController, expose # define application controllers - class MyAppBaseController(controller.CementBaseController): + class MyAppBaseController(CementBaseController): class Meta: label = 'base' - class UsersController(controller.CementBaseController): + class UsersController(CementBaseController): class Meta: label = 'users' + description = "this is the users controller" stacked_on = 'base' stacked_type = 'nested' - class HostsController(controller.CementBaseController): + class HostsController(CementBaseController): class Meta: label = 'hosts' + description = "this is the hosts controller" stacked_on = 'base' stacked_type = 'nested' - class UsersListController(controller.CementBaseController): + class UsersListController(CementBaseController): class Meta: label = 'users_list' description = 'list all available users' @@ -54,56 +57,61 @@ and a 'hosts' controller and we want to have a 'list' sub-command under both: stacked_on = 'users' stacked_type = 'nested' - @controller.expose(hide=True) + @expose(hide=True) def default(self): print "Inside UsersListController.default()" - class HostsListController(controller.CementBaseController): + class HostsListController(CementBaseController): class Meta: label = 'hosts_list' description = 'list all available hosts' aliases = ['list'] aliases_only = True - interface = controller.IController stacked_on = 'hosts' stacked_type = 'nested' - @controller.expose(hide=True) + @expose(hide=True) def default(self): print "Inside HostsListController.default()" - try: - # create the application - app = foundation.CementApp('myapp') + def main(): + try: + # create the application + app = foundation.CementApp('myapp') - # register non-base controllers - handler.register(MyAppBaseController) - handler.register(UsersController) - handler.register(HostsController) - handler.register(UsersListController) - handler.register(HostsListController) + # register non-base controllers + handler.register(MyAppBaseController) + handler.register(UsersController) + handler.register(HostsController) + handler.register(UsersListController) + handler.register(HostsListController) - # setup the application - app.setup() + # setup the application + app.setup() - app.run() - finally: - app.close() + # run it + app.run() + finally: + # close it + app.close() + + if __name__ == '__main__': + main() .. code-block:: text - $ myapp --help - usage: myapp (sub-commands ...) [options ...] {arguments ...} + $ python myapp.py --help + usage: myapp.py (sub-commands ...) [options ...] {arguments ...} Base Controller commands: hosts - Hosts Controller + this is the hosts controller users - Users Controller + this is the users controller optional arguments: -h, --help show this help message and exit @@ -111,10 +119,10 @@ and a 'hosts' controller and we want to have a 'list' sub-command under both: --quiet suppress all output - $ myapp users --help - usage: myapp (sub-commands ...) [options ...] {arguments ...} + $ python myapp.py users --help + usage: myapp.py (sub-commands ...) [options ...] {arguments ...} - Users Controller + this is the users controller commands: @@ -127,25 +135,9 @@ and a 'hosts' controller and we want to have a 'list' sub-command under both: --quiet suppress all output - $ myapp hosts --help - usage: myapp (sub-commands ...) [options ...] {arguments ...} - - Hosts Controller - - commands: - - list - list all available hosts - - optional arguments: - -h, --help show this help message and exit - --debug toggle debug output - --quiet suppress all output - - - $ myapp users list + $ python myapp.py users list Inside UsersListController.default() - - $ myapp hosts list + $ python myapp.py hosts list Inside HostsListController.default() + diff --git a/doc/source/examples/index.rst b/doc/source/examples/index.rst index d58ca261..0d8d66d7 100644 --- a/doc/source/examples/index.rst +++ b/doc/source/examples/index.rst @@ -7,10 +7,10 @@ with Cement. .. toctree:: :maxdepth: 1 - version_option + app_version multiple_stacked_controllers abstract_base_controllers controllers_with_same_label bash_auto_completion - handling_extra_arguments + arbitrary_extra_arguments load_extensions_via_config diff --git a/doc/source/examples/load_extensions_via_config.rst b/doc/source/examples/load_extensions_via_config.rst index a0fd9b76..342cc316 100644 --- a/doc/source/examples/load_extensions_via_config.rst +++ b/doc/source/examples/load_extensions_via_config.rst @@ -23,6 +23,10 @@ before ``app.setup()`` completes (such as in early framework hooks). class Meta: label = 'myapp' + config_files = [ + './myapp.conf', + ] + def validate_config(self): if 'extensions' in self.config.keys('myapp'): exts = self.config.get('myapp', 'extensions') @@ -48,12 +52,16 @@ before ``app.setup()`` completes (such as in early framework hooks). # add to meta data self._meta.extensions.append(ext) - app = MyApp() - try: - app.setup() - app.run() - finally: - app.close() + def main(): + app = MyApp() + try: + app.setup() + app.run() + finally: + app.close() + + if __name__ == '__main__': + main() **myapp.conf** diff --git a/doc/source/examples/multiple_stacked_controllers.rst b/doc/source/examples/multiple_stacked_controllers.rst index 7406a8d0..5c54ede3 100644 --- a/doc/source/examples/multiple_stacked_controllers.rst +++ b/doc/source/examples/multiple_stacked_controllers.rst @@ -6,108 +6,104 @@ Multiple Stacked Controllers .. code-block:: python from cement.core import foundation, controller, handler + from cement.core.controller import CementBaseController, expose # define application controllers - class MyAppBaseController(controller.CementBaseController): + class MyAppBaseController(CementBaseController): class Meta: label = 'base' - interface = controller.IController - description = "My Application Does Amazing Things" + description = "my application does amazing things" arguments = [ (['--base-opt'], dict(help="option under base controller")), ] - @controller.expose(help="base controller default command", hide=True) + @expose(help="base controller default command", hide=True) def default(self): - self.app.args.parse_args(['--help']) print "Inside MyAppBaseController.default()" - @controller.expose(help="another base controller command") + @expose(help="another base controller command") def command1(self): print "Inside MyAppBaseController.command1()" - class SecondController(controller.CementBaseController): - """This controller commands are 'stacked' onto the base controller.""" - + class SecondController(CementBaseController): class Meta: label = 'second_controller' - interface = controller.IController - stacked_on = 'base' + stacked_on = 'base' stacked_type = 'nested' - description = "My Application's Second Controller (stacked)" + description = "this is the second controller (stacked/nested on base)" arguments = [ (['--2nd-opt'], dict(help="another option under base controller")), ] - @controller.expose(help="command under the base namespace", aliases=['asdfas']) + @expose(help="second-controller default command", hide=True) + def default(self): + print "Inside SecondController.default()" + + @expose(help="this is a command under the second-controller namespace") def command2(self): print "Inside SecondController.command2()" - class ThirdController(controller.CementBaseController): - """This controller commands are *not* 'stacked' onto the base controller.""" - + class ThirdController(CementBaseController): class Meta: label = 'third_controller' - interface = controller.IController - stacked_on = 'second_controller' + stacked_on = 'second_controller' stacked_type = 'embedded' - description = "My Application's Third Controller (not-stacked)" + description = "this controller is embedded in the second-controller" arguments = [ (['--3rd-opt'], dict(help="an option only under 3rd controller")), ] - @controller.expose(help="default command for third_controller", hide=True) - def default(self): - print "Inside ThirdController.default()" - - @controller.expose(help="a command only under the 3rd namespace") + @expose(help="another command under the second-controller namespace") def command3(self): print "Inside ThirdController.command3()" - - class FourthController(controller.CementBaseController): - """This controller commands are 'stacked' onto the 3rd controller.""" - + class FourthController(CementBaseController): class Meta: label = 'fourth_controller' - interface = controller.IController - stacked_on = 'third_controller' + stacked_on = 'second_controller' stacked_type = 'nested' - description = "My Application's Fourth Controller (stacked)" + description = "this controller is nested on the second-controller" arguments = [ (['--4th-opt'], dict(help="an option only under 3rd controller")), ] - @controller.expose(help="a command only under the 3rd namespace") + @expose(help="a command only under the fourth-controller namespace") def command4(self): print "Inside FourthController.command4()" - try: - # create the application - app = foundation.CementApp('myapp') + def main(): + try: + # create the application + app = foundation.CementApp('myapp') - # register non-base controllers - handler.register(MyAppBaseController) - handler.register(SecondController) - handler.register(ThirdController) - handler.register(FourthController) + # register controllers + handler.register(MyAppBaseController) + handler.register(SecondController) + handler.register(ThirdController) + handler.register(FourthController) - # setup the application - app.setup() + # setup the application + app.setup() - app.run() - finally: - app.close() + # run the application + app.run() + finally: + # close the application + app.close() -In the `base` controller output of `--help` notice that the + if __name__ == '__main__': + main() + + +In the `base` controller output of `--help` notice that the `second-controller` is listed as a sub-command: .. code-block:: text - $ python example.py --help - usage: example.py (sub-commands ...) [options ...] {arguments ...} + $ python myapp.py --help + usage: myapp.py (sub-commands ...) [options ...] {arguments ...} - My Application Does Amazing Things + my application does amazing things commands: @@ -115,34 +111,40 @@ In the `base` controller output of `--help` notice that the another base controller command second-controller - My Application's Second Controller (stacked) + this is the second controller (stacked/nested on base) optional arguments: -h, --help show this help message and exit --debug toggle debug output --quiet suppress all output --base-opt BASE_OPT option under base controller - - - $ python example.py command1 + + + $ python myapp.py + Inside MyAppBaseController.default() + + + $ python myapp.py command1 Inside MyAppBaseController.command1() + $ python myapp.py second-controller + Inside SecondController.default() - $ python example.py second-controller --help - usage: example.py (sub-commands ...) [options ...] {arguments ...} + $ python myapp.py second-controller --help + usage: myapp.py (sub-commands ...) [options ...] {arguments ...} - My Application's Second Controller (stacked) + this is the second controller (stacked/nested on base) commands: - command2 (aliases: asdfas) - command under the base namespace + command2 + this is a command under the second-controller namespace command3 - a command only under the 3rd namespace + another command under the second-controller namespace fourth-controller - My Application's Fourth Controller (stacked) + this controller is nested on the second-controller optional arguments: -h, --help show this help message and exit @@ -151,27 +153,28 @@ In the `base` controller output of `--help` notice that the --2nd-opt 2ND_OPT another option under base controller --3rd-opt 3RD_OPT an option only under 3rd controller -Under the `second-controller` you can see the commands and options from the + +Under the `second-controller` you can see the commands and options from the second and third controllers. In this example, the `second-controller` is -`nested` on the base controller, and the `third-controller` is `embedded` +`nested` on the base controller, and the `third-controller` is `embedded` on the `second-controller`. Finally, we see that the `fourth-controller` is also `nested` on the `second-controller` creating a sub-sub-command. .. code-block:: text - $ python example.py second-controller command3 + $ python myapp.py second-controller command3 Inside ThirdController.command3() - $ python example.py second-controller fourth-controller --help - usage: example.py (sub-commands ...) [options ...] {arguments ...} + $ python myapp.py second-controller fourth-controller --help + usage: myapp.py (sub-commands ...) [options ...] {arguments ...} - My Application's Fourth Controller (stacked) + this controller is nested on the second-controller commands: command4 - a command only under the 3rd namespace + a command only under the fourth-controller namespace optional arguments: -h, --help show this help message and exit @@ -180,5 +183,5 @@ also `nested` on the `second-controller` creating a sub-sub-command. --4th-opt 4TH_OPT an option only under 3rd controller - $ python example.py second-controller fourth-controller command4 + $ python myapp.py second-controller fourth-controller command4 Inside FourthController.command4() diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/abstract_base_controllers/myapp.py b/examples/abstract_base_controllers/myapp.py new file mode 100644 index 00000000..30a9c040 --- /dev/null +++ b/examples/abstract_base_controllers/myapp.py @@ -0,0 +1,104 @@ + +from cement.core import foundation, handler +from cement.core.controller import CementBaseController, expose + +class AbstractBaseController(CementBaseController): + """ + This is an abstract base class that is useless on its own, but used + by other classes to sub-class from and to share common commands and + arguments. This should not be confused with the `MyAppBaseController` + used as the ``base_controller`` namespace. + + """ + class Meta: + stacked_on = 'base' + stacked_type = 'nested' + arguments = [ + ( ['-f', '--foo'], dict(help='notorious foo option')), + ] + + def _setup(self, base_app): + super(AbstractBaseController, self)._setup(base_app) + + # add a common object that will be used in any sub-class + self.reusable_dict = dict() + + @expose(hide=True) + def default(self): + """ + This command will be shared within all controllers that sub-class + from here. It can also be overridden in the sub-class, but for + this example we are making it dynamic. + + """ + # do something with self.my_shared_obj here? + if 'some_key' in self.reusable_dict.keys(): + pass + + # or do something with parsed args? + if self.app.pargs.foo: + print "Foo option was passed with value: %s" % self.app.pargs.foo + + # or maybe do something dynamically + print("Inside %s.default()" % self.__class__.__name__) + +class MyAppBaseController(CementBaseController): + """ + This is the application base controller, but we don't want to use our + abstract base class here. + + """ + class Meta: + label = 'base' + + @expose(hide=True) + def default(self): + print("Inside MyAppBaseController.default()") + +class Controller1(AbstractBaseController): + """ + This controller sub-classes from the abstract base class as to inherite + shared arguments, and commands. + + """ + class Meta: + label = 'controller1' + + @expose() + def command1(self): + print("Inside Controller1.command1()") + +class Controller2(AbstractBaseController): + """ + This controller also sub-classes from the abstract base class as to + inherite shared arguments, and commands. + + """ + class Meta: + label = 'controller2' + + @expose() + def command2(self): + print("Inside Controller2.command2()") + +def main(): + # create the app + app = foundation.CementApp('myapp') + + try: + # register controllers + handler.register(MyAppBaseController) + handler.register(Controller1) + handler.register(Controller2) + + # setup the app + app.setup() + + # run it + app.run() + finally: + # close it + app.close() + +if __name__ == '__main__': + main() diff --git a/examples/app_version/myapp.py b/examples/app_version/myapp.py new file mode 100644 index 00000000..c925311d --- /dev/null +++ b/examples/app_version/myapp.py @@ -0,0 +1,35 @@ + +from cement.core.foundation import CementApp +from cement.core.controller import CementBaseController + +VERSION = '0.9.1' + +BANNER = """ +My Awesome Application v%s +Copyright (c) 2014 John Doe Enterprises +""" % VERSION + +class MyBaseController(CementBaseController): + class Meta: + label = 'base' + description = 'MyApp Does Amazing Things' + arguments = [ + (['-v', '--version'], dict(action='version', version=BANNER)), + ] + +class MyApp(CementApp): + class Meta: + label = 'myapp' + base_controller = MyBaseController + + +def main(): + app = MyApp() + try: + app.setup() + app.run() + finally: + app.close() + +if __name__ == '__main__': + main() diff --git a/examples/arbitrary_extra_arguments/myapp.py b/examples/arbitrary_extra_arguments/myapp.py new file mode 100644 index 00000000..bb0bfc67 --- /dev/null +++ b/examples/arbitrary_extra_arguments/myapp.py @@ -0,0 +1,48 @@ + +from cement.core.foundation import CementApp +from cement.core.controller import CementBaseController, expose +from cement.core import handler + +class MyBaseController(CementBaseController): + class Meta: + label = 'base' + +class MySecondController(CementBaseController): + class Meta: + label = 'second' + stacked_type = 'nested' + stacked_on = 'base' + description = 'this is the second controller namespace' + arguments = [ + (['-f', '--foo'], + dict(help='the notorious foo option', action='store')), + (['extra_arguments'], + dict(action='store', nargs='*')), + ] + + @expose() + def cmd1(self): + print "Inside MySecondController.cmd1()" + + if self.app.pargs.extra_arguments: + print "Extra Argument 0: %s" % self.app.pargs.extra_arguments[0] + print "Extra Argument 1: %s" % self.app.pargs.extra_arguments[1] + + +class MyApp(CementApp): + class Meta: + label = 'myapp' + base_controller = MyBaseController + +def main(): + app = MyApp() + handler.register(MySecondController) + + try: + app.setup() + app.run() + finally: + app.close() + +if __name__ == '__main__': + main() diff --git a/examples/bash_auto_completion/myapp.py b/examples/bash_auto_completion/myapp.py new file mode 100644 index 00000000..5b03cd7f --- /dev/null +++ b/examples/bash_auto_completion/myapp.py @@ -0,0 +1,85 @@ + +from cement.core import foundation, controller, handler +from cement.core.controller import CementBaseController, expose + +class BaseController(CementBaseController): + class Meta: + label = 'base' + + @expose() + def base_cmd1(self): + print("Inside BaseController.base_cmd1()") + +class EmbeddedController(CementBaseController): + class Meta: + label = 'embedded' + description = "embedded with base namespace" + stacked_on = 'base' + stacked_type = 'embedded' + + @expose() + def base_cmd2(self): + print("Inside EmbeddedController.base_cmd2()") + + @expose() + def embedded_cmd3(self): + print("Inside EmbeddedController.embedded_cmd3()") + +class SecondLevelController(CementBaseController): + class Meta: + label = 'second' + description = '' + stacked_on = 'base' + stacked_type = 'nested' + + @expose() + def second_cmd4(self): + print("Inside SecondLevelController.second_cmd4()") + + @expose() + def second_cmd5(self): + print("Inside SecondLevelController.second_cmd5()") + +class ThirdLevelController(CementBaseController): + class Meta: + label = 'third' + description = '' + stacked_on = 'second' + stacked_type = 'nested' + + @expose() + def third_cmd6(self): + print("Inside ThirdLevelController.third_cmd6()") + + @expose() + def third_cmd7(self): + print("Inside ThirdLevelController.third_cmd7()") + +class MyApp(foundation.CementApp): + class Meta: + label = 'myapp' + + +def main(): + try: + # create the app + app = MyApp() + + # register controllers to the app + handler.register(BaseController) + handler.register(EmbeddedController) + handler.register(SecondLevelController) + handler.register(ThirdLevelController) + + # setup the app + app.setup() + + # run the app + app.run() + + finally: + # close the app + app.close() + +if __name__ == '__main__': + main() diff --git a/examples/bash_auto_completion/myapp.rc b/examples/bash_auto_completion/myapp.rc new file mode 100644 index 00000000..fd1e6b22 --- /dev/null +++ b/examples/bash_auto_completion/myapp.rc @@ -0,0 +1,66 @@ +alias myapp="python ./myapp.py" + +_myapp_complete() +{ + local cur prev BASE_LEVEL + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + # SETUP THE BASE LEVEL (everything after "myapp") + if [ $COMP_CWORD -eq 1 ]; then + COMPREPLY=( $(compgen \ + -W "base-cmd1 base-cmd2 embedded-cmd3 second" \ + -- $cur) ) + + + # SETUP THE SECOND LEVEL (EVERYTHING AFTER "myapp second") + elif [ $COMP_CWORD -eq 2 ]; then + case "$prev" in + + # HANDLE EVERYTHING AFTER THE SECOND LEVEL NAMESPACE + "second") + COMPREPLY=( $(compgen \ + -W "second-cmd4 second-cmd5 third" \ + -- $cur) ) + ;; + + # IF YOU HAD ANOTHER CONTROLLER, YOU'D HANDLE THAT HERE + "some-other-controller") + COMPREPLY=( $(compgen \ + -W "some-other-sub-command" \ + -- $cur) ) + ;; + + # EVERYTHING ELSE + *) + ;; + esac + + # SETUP THE THIRD LEVEL (EVERYTHING AFTER "myapp second third") + elif [ $COMP_CWORD -eq 3 ]; then + case "$prev" in + # HANDLE EVERYTHING AFTER THE THIRD LEVEL NAMESPACE + "third") + COMPREPLY=( $(compgen \ + -W "third-cmd6 third-cmd7" \ + -- $cur) ) + ;; + + # IF YOU HAD ANOTHER CONTROLLER, YOU'D HANDLE THAT HERE + "some-other-controller") + COMPREPLY=( $(compgen \ + -W "some-other-sub-command" \ + -- $cur) ) + ;; + + *) + ;; + esac + fi + + return 0 + +} && +complete -F _myapp_complete myapp diff --git a/examples/controllers_with_same_label/myapp.py b/examples/controllers_with_same_label/myapp.py new file mode 100644 index 00000000..6d556d56 --- /dev/null +++ b/examples/controllers_with_same_label/myapp.py @@ -0,0 +1,72 @@ + +from cement.core import foundation, handler +from cement.core.controller import CementBaseController, expose + +# define application controllers +class MyAppBaseController(CementBaseController): + class Meta: + label = 'base' + +class UsersController(CementBaseController): + class Meta: + label = 'users' + description = "this is the users controller" + stacked_on = 'base' + stacked_type = 'nested' + +class HostsController(CementBaseController): + class Meta: + label = 'hosts' + description = "this is the hosts controller" + stacked_on = 'base' + stacked_type = 'nested' + +class UsersListController(CementBaseController): + class Meta: + label = 'users_list' + description = 'list all available users' + aliases = ['list'] + aliases_only = True + stacked_on = 'users' + stacked_type = 'nested' + + @expose(hide=True) + def default(self): + print "Inside UsersListController.default()" + +class HostsListController(CementBaseController): + class Meta: + label = 'hosts_list' + description = 'list all available hosts' + aliases = ['list'] + aliases_only = True + stacked_on = 'hosts' + stacked_type = 'nested' + + @expose(hide=True) + def default(self): + print "Inside HostsListController.default()" + +def main(): + try: + # create the application + app = foundation.CementApp('myapp') + + # register non-base controllers + handler.register(MyAppBaseController) + handler.register(UsersController) + handler.register(HostsController) + handler.register(UsersListController) + handler.register(HostsListController) + + # setup the application + app.setup() + + # run it + app.run() + finally: + # close it + app.close() + +if __name__ == '__main__': + main() diff --git a/examples/load_extensions_via_config/myapp.conf b/examples/load_extensions_via_config/myapp.conf new file mode 100644 index 00000000..0b0a0d2a --- /dev/null +++ b/examples/load_extensions_via_config/myapp.conf @@ -0,0 +1,2 @@ +[myapp] +extensions = json, yaml diff --git a/examples/load_extensions_via_config/myapp.py b/examples/load_extensions_via_config/myapp.py new file mode 100644 index 00000000..026dd2d4 --- /dev/null +++ b/examples/load_extensions_via_config/myapp.py @@ -0,0 +1,46 @@ + +from cement.core.foundation import CementApp + +class MyApp(CementApp): + class Meta: + label = 'myapp' + + config_files = [ + './myapp.conf', + ] + + def validate_config(self): + if 'extensions' in self.config.keys('myapp'): + exts = self.config.get('myapp', 'extensions') + + # convert a comma-separated string to a list + if type(exts) is str: + ext_list = exts.split(',') + + # clean up extra space if they had it inbetween commas + ext_list = (x.strip() for x in ext_list) + + # set the new extensions value in the config + self.config.set('myapp', 'extensions', ext_list) + + # otherwise, if it's a list (ConfigObj?) + elif type(exts) is list: + ext_list = exts + + for ext in ext_list: + # load the extension + self.ext.load_extension(ext) + + # add to meta data + self._meta.extensions.append(ext) + +def main(): + app = MyApp() + try: + app.setup() + app.run() + finally: + app.close() + +if __name__ == '__main__': + main() diff --git a/examples/multiple_stacked_controllers/myapp.py b/examples/multiple_stacked_controllers/myapp.py new file mode 100644 index 00000000..0d2f64d1 --- /dev/null +++ b/examples/multiple_stacked_controllers/myapp.py @@ -0,0 +1,89 @@ + +from cement.core import foundation, handler +from cement.core.controller import CementBaseController, expose + +# define application controllers +class MyAppBaseController(CementBaseController): + class Meta: + label = 'base' + description = "my application does amazing things" + arguments = [ + (['--base-opt'], dict(help="option under base controller")), + ] + + @expose(help="base controller default command", hide=True) + def default(self): + print "Inside MyAppBaseController.default()" + + @expose(help="another base controller command") + def command1(self): + print "Inside MyAppBaseController.command1()" + +class SecondController(CementBaseController): + class Meta: + label = 'second_controller' + stacked_on = 'base' + stacked_type = 'nested' + description = "this is the second controller (stacked/nested on base)" + arguments = [ + (['--2nd-opt'], dict(help="another option under base controller")), + ] + + @expose(help="second-controller default command", hide=True) + def default(self): + print "Inside SecondController.default()" + + @expose(help="this is a command under the second-controller namespace") + def command2(self): + print "Inside SecondController.command2()" + +class ThirdController(CementBaseController): + class Meta: + label = 'third_controller' + stacked_on = 'second_controller' + stacked_type = 'embedded' + description = "this controller is embedded in the second-controller" + arguments = [ + (['--3rd-opt'], dict(help="an option only under 3rd controller")), + ] + + @expose(help="another command under the second-controller namespace") + def command3(self): + print "Inside ThirdController.command3()" + +class FourthController(CementBaseController): + class Meta: + label = 'fourth_controller' + stacked_on = 'second_controller' + stacked_type = 'nested' + description = "this controller is nested on the second-controller" + arguments = [ + (['--4th-opt'], dict(help="an option only under 3rd controller")), + ] + + @expose(help="a command only under the fourth-controller namespace") + def command4(self): + print "Inside FourthController.command4()" + +def main(): + try: + # create the application + app = foundation.CementApp('myapp') + + # register controllers + handler.register(MyAppBaseController) + handler.register(SecondController) + handler.register(ThirdController) + handler.register(FourthController) + + # setup the application + app.setup() + + # run the application + app.run() + finally: + # close the application + app.close() + +if __name__ == '__main__': + main()