Resolves Issue #275

This commit is contained in:
BJ Dierkes 2014-09-08 21:01:58 -05:00
parent 77e78eab0b
commit 2d1cdf5d2d
20 changed files with 793 additions and 282 deletions

View File

@ -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:

View File

@ -6,5 +6,4 @@
.. automodule:: cement.core.arg
:members:
:undoc-members:
:private-members:
:show-inheritance:
:show-inheritance:

View File

@ -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 <CMD> -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 <CMD> -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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**

View File

@ -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()

0
examples/__init__.py Normal file
View File

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -0,0 +1,2 @@
[myapp]
extensions = json, yaml

View File

@ -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()

View File

@ -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()