mirror of
https://github.com/datafolklabs/cement.git
synced 2026-02-06 14:16:46 +00:00
Drop CementBaseController
This commit is contained in:
parent
1c1385dc86
commit
47e335a827
@ -69,9 +69,10 @@ class IController(interface.Interface):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cement.core import controller
|
||||
from cement.core.handler import Handler
|
||||
from cement.core.controller import IController
|
||||
|
||||
class MyBaseController(controller.CementBaseController):
|
||||
class MyController(handler):
|
||||
class Meta:
|
||||
interface = controller.IController
|
||||
...
|
||||
@ -120,411 +121,3 @@ class IController(interface.Interface):
|
||||
"""
|
||||
|
||||
|
||||
class expose(object):
|
||||
|
||||
"""
|
||||
Used to expose controller functions to be listed as commands, and to
|
||||
decorate the function with Meta data for the argument parser.
|
||||
|
||||
:param help: Help text to display for that command.
|
||||
:type help: str
|
||||
:param hide: Whether the command should be visible.
|
||||
:type hide: boolean
|
||||
:param aliases: Aliases to this command.
|
||||
:param aliases_only: Whether to only display the aliases (not the label).
|
||||
This is useful for situations where you have obscure function names
|
||||
which you do not want displayed. Effecively, if there are aliases and
|
||||
`aliases_only` is True, then aliases[0] will appear as the actual
|
||||
command/function label.
|
||||
:type aliases: ``list``
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cement.core.controller import CementBaseController, expose
|
||||
|
||||
class MyAppBaseController(CementBaseController):
|
||||
class Meta:
|
||||
label = 'base'
|
||||
|
||||
@expose(hide=True, aliases=['run'])
|
||||
def default(self):
|
||||
print("In MyAppBaseController.default()")
|
||||
|
||||
@expose()
|
||||
def my_command(self):
|
||||
print("In MyAppBaseController.my_command()")
|
||||
|
||||
"""
|
||||
# pylint: disable=W0622
|
||||
|
||||
def __init__(self, help='', hide=False, aliases=[], aliases_only=False):
|
||||
self.hide = hide
|
||||
self.help = help
|
||||
self.aliases = aliases
|
||||
self.aliases_only = aliases_only
|
||||
|
||||
def __call__(self, func):
|
||||
metadict = {}
|
||||
metadict['label'] = re.sub('_', '-', func.__name__)
|
||||
metadict['func_name'] = func.__name__
|
||||
metadict['exposed'] = True
|
||||
metadict['hide'] = self.hide
|
||||
metadict['help'] = self.help or func.__doc__
|
||||
metadict['aliases'] = self.aliases
|
||||
metadict['aliases_only'] = self.aliases_only
|
||||
metadict['controller'] = None # added by the controller
|
||||
func.__cement_meta__ = metadict
|
||||
return func
|
||||
|
||||
|
||||
# pylint: disable=R0921
|
||||
class CementBaseController(handler.CementBaseHandler):
|
||||
|
||||
"""
|
||||
This is an implementation of the
|
||||
`IControllerHandler <#cement.core.controller.IController>`_ interface, but
|
||||
as a base class that application controllers `should` subclass from.
|
||||
Registering it directly as a handler is useless.
|
||||
|
||||
NOTE: This handler **requires** that the applications 'arg_handler' be
|
||||
argparse. If using an alternative argument handler you will need to
|
||||
write your own controller base class.
|
||||
|
||||
NOTE: This the initial default implementation of CementBaseController. In
|
||||
the future it will be replaced by CementBaseController2, therefore using
|
||||
CementBaseController2 is recommended for new development.
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cement.core.controller import CementBaseController
|
||||
|
||||
class MyAppBaseController(CementBaseController):
|
||||
class Meta:
|
||||
label = 'base'
|
||||
description = 'MyApp is awesome'
|
||||
config_defaults = dict()
|
||||
arguments = []
|
||||
epilog = "This is the text at the bottom of --help."
|
||||
# ...
|
||||
|
||||
class MyStackedController(CementBaseController):
|
||||
class Meta:
|
||||
label = 'second_controller'
|
||||
aliases = ['sec', 'secondary']
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'embedded'
|
||||
# ...
|
||||
|
||||
"""
|
||||
class Meta:
|
||||
|
||||
"""
|
||||
Controller meta-data (can be passed as keyword arguments to the parent
|
||||
class).
|
||||
|
||||
"""
|
||||
|
||||
interface = IController
|
||||
"""The interface this class implements."""
|
||||
|
||||
label = None
|
||||
"""The string identifier for the controller."""
|
||||
|
||||
aliases = []
|
||||
"""
|
||||
A list of aliases for the controller. Will be treated like
|
||||
command/function aliases for non-stacked controllers. For example:
|
||||
``myapp <controller_label> --help`` is the same as
|
||||
``myapp <controller_alias> --help``.
|
||||
"""
|
||||
|
||||
aliases_only = False
|
||||
"""
|
||||
When set to True, the controller label will not be displayed at
|
||||
command line, only the aliases will. Effectively, aliases[0] will
|
||||
appear as the label. This feature is useful for the situation Where
|
||||
you might want two controllers to have the same label when stacked
|
||||
on top of separate controllers. For example, 'myapp users list' and
|
||||
'myapp servers list' where 'list' is a stacked controller, not a
|
||||
function.
|
||||
"""
|
||||
|
||||
description = None
|
||||
"""The description shown at the top of '--help'. Default: None"""
|
||||
|
||||
config_section = None
|
||||
"""
|
||||
A config [section] to merge config_defaults into. Cement will default
|
||||
to controller.<label> if None is set.
|
||||
"""
|
||||
|
||||
config_defaults = {}
|
||||
"""
|
||||
Configuration defaults (type: dict) that are merged into the
|
||||
applications config object for the config_section mentioned above.
|
||||
"""
|
||||
|
||||
arguments = []
|
||||
"""
|
||||
Arguments to pass to the argument_handler. The format is a list
|
||||
of tuples whos items are a ( list, dict ). Meaning:
|
||||
|
||||
``[ ( ['-f', '--foo'], dict(dest='foo', help='foo option') ), ]``
|
||||
|
||||
This is equivelant to manually adding each argument to the argument
|
||||
parser as in the following example:
|
||||
|
||||
``parser.add_argument(['-f', '--foo'], help='foo option', dest='foo')``
|
||||
|
||||
"""
|
||||
|
||||
stacked_on = 'base'
|
||||
"""
|
||||
A label of another controller to 'stack' commands/arguments on top of.
|
||||
"""
|
||||
|
||||
stacked_type = 'embedded'
|
||||
"""
|
||||
Whether to `embed` commands and arguments within the parent controller
|
||||
or to simply `nest` the controller under the parent controller (making
|
||||
it a sub-sub-command). Must be one of `['embedded', 'nested']` only
|
||||
if `stacked_on` is not `None`.
|
||||
"""
|
||||
|
||||
hide = False
|
||||
"""Whether or not to hide the controller entirely."""
|
||||
|
||||
epilog = None
|
||||
"""
|
||||
The text that is displayed at the bottom when '--help' is passed.
|
||||
"""
|
||||
|
||||
usage = None
|
||||
"""
|
||||
The text that is displayed at the top when '--help' is passed.
|
||||
Although the default is `None`, Cement will set this to a generic
|
||||
usage based on the `prog`, `controller` name, etc if nothing else is
|
||||
passed.
|
||||
"""
|
||||
|
||||
argument_formatter = argparse.RawDescriptionHelpFormatter
|
||||
"""
|
||||
The argument formatter class to use to display --help output.
|
||||
"""
|
||||
|
||||
default_func = 'default'
|
||||
"""
|
||||
Function to call if no sub-command is passed. Note that this can
|
||||
**not** start with an ``_`` due to backward compatibility restraints
|
||||
in how Cement discovers and maps commands.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(CementBaseController, self).__init__(*args, **kw)
|
||||
|
||||
self.app = None
|
||||
self._commands = {} # used to store collected commands
|
||||
self._visible_commands = [] # used to sort visible command labels
|
||||
self._arguments = [] # used to store collected arguments
|
||||
self._dispatch_map = {} # used to map commands/aliases to controller
|
||||
self._dispatch_command = None # set during _parse_args()
|
||||
|
||||
def _setup(self, app_obj):
|
||||
"""
|
||||
See `IController._setup() <#cement.core.cache.IController._setup>`_.
|
||||
"""
|
||||
super(CementBaseController, self)._setup(app_obj)
|
||||
|
||||
if getattr(self._meta, 'description', None) is None:
|
||||
self._meta.description = "%s Controller" % \
|
||||
self._meta.label.capitalize()
|
||||
|
||||
self.app = app_obj
|
||||
|
||||
def _collect(self):
|
||||
LOG.debug("collecting arguments/commands for %s" % self)
|
||||
arguments = []
|
||||
commands = []
|
||||
|
||||
# process my arguments and commands first
|
||||
arguments = list(self._meta.arguments)
|
||||
|
||||
for member in dir(self.__class__):
|
||||
if member.startswith('_'):
|
||||
continue
|
||||
try:
|
||||
func = getattr(self.__class__, member).__cement_meta__
|
||||
except AttributeError:
|
||||
continue
|
||||
else:
|
||||
func['controller'] = self
|
||||
commands.append(func)
|
||||
|
||||
# process stacked controllers second for commands and args
|
||||
for contr in self.app.handler.list('controller'):
|
||||
# don't include self here
|
||||
if contr == self.__class__:
|
||||
continue
|
||||
|
||||
contr = contr()
|
||||
contr._setup(self.app)
|
||||
if contr._meta.stacked_on == self._meta.label:
|
||||
if contr._meta.stacked_type == 'embedded':
|
||||
contr_arguments, contr_commands = contr._collect()
|
||||
for arg in contr_arguments:
|
||||
arguments.append(arg)
|
||||
for func in contr_commands:
|
||||
commands.append(func)
|
||||
elif contr._meta.stacked_type == 'nested':
|
||||
metadict = {}
|
||||
metadict['label'] = re.sub('_', '-', contr._meta.label)
|
||||
metadict['func_name'] = '_dispatch'
|
||||
metadict['exposed'] = True
|
||||
metadict['hide'] = contr._meta.hide
|
||||
metadict['help'] = contr._meta.description
|
||||
metadict['aliases'] = contr._meta.aliases
|
||||
metadict['aliases_only'] = contr._meta.aliases_only
|
||||
metadict['controller'] = contr
|
||||
commands.append(metadict)
|
||||
|
||||
return (arguments, commands)
|
||||
|
||||
def _process_arguments(self):
|
||||
for _arg, _kw in self._arguments:
|
||||
try:
|
||||
self.app.args.add_argument(*_arg, **_kw)
|
||||
except argparse.ArgumentError as e:
|
||||
raise exc.FrameworkError(e.__str__())
|
||||
|
||||
def _process_commands(self):
|
||||
self._dispatch_map = {}
|
||||
self._visible_commands = []
|
||||
|
||||
for cmd in self._commands:
|
||||
# process command labels
|
||||
if cmd['label'] in self._dispatch_map.keys():
|
||||
raise exc.FrameworkError(
|
||||
"Duplicate command named '%s' " % cmd['label'] +
|
||||
"found in controller '%s'" % cmd['controller']
|
||||
)
|
||||
self._dispatch_map[cmd['label']] = cmd
|
||||
|
||||
if not cmd['hide']:
|
||||
self._visible_commands.append(cmd['label'])
|
||||
|
||||
# process command aliases
|
||||
for alias in cmd['aliases']:
|
||||
if alias in self._dispatch_map.keys():
|
||||
raise exc.FrameworkError(
|
||||
"The alias '%s' of the " % alias +
|
||||
"'%s' controller collides " % cmd['controller'] +
|
||||
"with a command or alias of the same name."
|
||||
)
|
||||
self._dispatch_map[alias] = cmd
|
||||
self._visible_commands.sort()
|
||||
|
||||
def _get_dispatch_command(self):
|
||||
default_func_key = re.sub('_', '-', self._meta.default_func)
|
||||
|
||||
if (len(self.app.argv) <= 0) or (self.app.argv[0].startswith('-')):
|
||||
# if no command is passed, then use default
|
||||
if default_func_key in self._dispatch_map.keys():
|
||||
self._dispatch_command = self._dispatch_map[default_func_key]
|
||||
|
||||
elif self.app.argv[0] in self._dispatch_map.keys():
|
||||
self._dispatch_command = self._dispatch_map[self.app.argv[0]]
|
||||
self.app.argv.pop(0)
|
||||
else:
|
||||
# check for default again (will get here if command line has
|
||||
# positional arguments that don't start with a -)
|
||||
if default_func_key in self._dispatch_map.keys():
|
||||
self._dispatch_command = self._dispatch_map[default_func_key]
|
||||
|
||||
def _parse_args(self):
|
||||
self.app.args.description = self._help_text
|
||||
self.app.args.usage = self._usage_text
|
||||
self.app.args.formatter_class = self._meta.argument_formatter
|
||||
self.app._parse_args()
|
||||
|
||||
def _dispatch(self):
|
||||
"""
|
||||
Takes the remaining arguments from self.app.argv and parses for a
|
||||
command to dispatch, and if so... dispatches it.
|
||||
|
||||
"""
|
||||
if hasattr(self._meta, 'epilog'):
|
||||
if self._meta.epilog is not None:
|
||||
self.app.args.epilog = self._meta.epilog
|
||||
|
||||
self._arguments, self._commands = self._collect()
|
||||
self._process_commands()
|
||||
self._get_dispatch_command()
|
||||
|
||||
if self._dispatch_command:
|
||||
if self._dispatch_command['func_name'] == '_dispatch':
|
||||
func = getattr(self._dispatch_command['controller'],
|
||||
'_dispatch')
|
||||
return func()
|
||||
else:
|
||||
self._process_arguments()
|
||||
self._parse_args()
|
||||
func = getattr(self._dispatch_command['controller'],
|
||||
self._dispatch_command['func_name'])
|
||||
return func()
|
||||
else:
|
||||
self._process_arguments()
|
||||
self._parse_args()
|
||||
|
||||
@property
|
||||
def _usage_text(self):
|
||||
"""Returns the usage text displayed when ``--help`` is passed."""
|
||||
|
||||
if self._meta.usage is not None:
|
||||
return self._meta.usage
|
||||
|
||||
txt = "%s (sub-commands ...) [options ...] {arguments ...}" % \
|
||||
self.app.args.prog
|
||||
return txt
|
||||
|
||||
@property
|
||||
def _help_text(self):
|
||||
"""Returns the help text displayed when '--help' is passed."""
|
||||
|
||||
cmd_txt = ''
|
||||
for label in self._visible_commands:
|
||||
cmd = self._dispatch_map[label]
|
||||
if len(cmd['aliases']) > 0 and cmd['aliases_only']:
|
||||
if len(cmd['aliases']) > 1:
|
||||
first = cmd['aliases'].pop(0)
|
||||
cmd_txt = cmd_txt + " %s (aliases: %s)\n" % \
|
||||
(first, ', '.join(cmd['aliases']))
|
||||
else:
|
||||
cmd_txt = cmd_txt + " %s\n" % cmd['aliases'][0]
|
||||
elif len(cmd['aliases']) > 0:
|
||||
cmd_txt = cmd_txt + " %s (aliases: %s)\n" % \
|
||||
(label, ', '.join(cmd['aliases']))
|
||||
else:
|
||||
cmd_txt = cmd_txt + " %s\n" % label
|
||||
|
||||
if cmd['help']:
|
||||
cmd_txt = cmd_txt + " %s\n\n" % cmd['help']
|
||||
else:
|
||||
cmd_txt = cmd_txt + "\n"
|
||||
|
||||
if len(cmd_txt) > 0:
|
||||
txt = '''%s
|
||||
|
||||
commands:
|
||||
|
||||
%s
|
||||
|
||||
|
||||
''' % (self._meta.description, cmd_txt)
|
||||
else:
|
||||
txt = self._meta.description
|
||||
|
||||
return textwrap.dedent(txt)
|
||||
|
||||
@ -11,9 +11,7 @@ Requirements
|
||||
|
||||
|
||||
This extension currently only works when using
|
||||
:class:`cement.ext.ext_argparse.ArgparseArgumentHandler` (default) and
|
||||
:class:`cement.ext.ext_argparse.ArgparseController` (new in Cement 2.8). It
|
||||
will not work with :class:`cement.core.controller.CementBaseController`.
|
||||
:class:`cement.ext.ext_argparse.ArgparseArgumentHandler` (default).
|
||||
|
||||
|
||||
Configuration
|
||||
|
||||
@ -334,10 +334,6 @@ class ArgparseController(CementBaseHandler):
|
||||
``argparse``. If using an alternative argument handler you will need to
|
||||
write your own controller base class or modify this one.
|
||||
|
||||
NOTE: This is a re-implementation of
|
||||
:class:`cement.core.controller.CementBaseController`.
|
||||
In the future, this class will eventually replace it as the default.
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -112,15 +112,14 @@ rather than the entire parent application. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cement import App
|
||||
from cement.core.controller import CementBaseController, expose
|
||||
from cement import App, Controller, ex
|
||||
|
||||
|
||||
class MyBaseController(CementBaseController):
|
||||
class MyBaseController(Controller):
|
||||
class Meta:
|
||||
label = 'base'
|
||||
|
||||
@expose(help="run the daemon command.")
|
||||
@ex(help="run the daemon command.")
|
||||
def run_forever(self):
|
||||
from time import sleep
|
||||
self.app.daemonize()
|
||||
|
||||
@ -97,17 +97,16 @@ perform an arbitrary action any time configuration changes are detected.
|
||||
|
||||
from time import sleep
|
||||
from cement.core.exc import CaughtSignal
|
||||
from cement import App
|
||||
from cement.core.controller import CementBaseController, expose
|
||||
from cement import App, Controller, ex
|
||||
|
||||
def print_foo(app):
|
||||
print "Foo => %s" % app.config.get('myapp', 'foo')
|
||||
|
||||
class Base(CementBaseController):
|
||||
class Base(Controller):
|
||||
class Meta:
|
||||
label = 'base'
|
||||
|
||||
@expose(hide=True)
|
||||
@ex(hide=True)
|
||||
def default(self):
|
||||
print('Inside Base.default()')
|
||||
|
||||
|
||||
@ -8,366 +8,5 @@ from cement.utils.misc import rando, init_defaults
|
||||
APP = "app-%s" % rando()[:12]
|
||||
|
||||
|
||||
class TestController(controller.CementBaseController):
|
||||
|
||||
class Meta:
|
||||
label = 'base'
|
||||
arguments = [
|
||||
(['-f', '--foo'], dict(help='foo option'))
|
||||
]
|
||||
usage = 'My Custom Usage TXT'
|
||||
epilog = "This is the epilog"
|
||||
|
||||
@controller.expose(hide=True)
|
||||
def default(self):
|
||||
pass
|
||||
|
||||
@controller.expose()
|
||||
def some_command(self):
|
||||
pass
|
||||
|
||||
|
||||
class TestWithPositionalController(controller.CementBaseController):
|
||||
|
||||
class Meta:
|
||||
label = 'base'
|
||||
arguments = [
|
||||
(['foo'], dict(help='foo option', nargs='?'))
|
||||
]
|
||||
|
||||
@controller.expose(hide=True)
|
||||
def default(self):
|
||||
self.app.render(dict(foo=self.app.pargs.foo))
|
||||
|
||||
|
||||
class Embedded(controller.CementBaseController):
|
||||
|
||||
class Meta:
|
||||
label = 'embedded_controller'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'embedded'
|
||||
arguments = [(['-t'], dict())]
|
||||
|
||||
@controller.expose(aliases=['emcmd1'], help='This is my help txt')
|
||||
def embedded_cmd1(self):
|
||||
pass
|
||||
|
||||
|
||||
class Nested(controller.CementBaseController):
|
||||
|
||||
class Meta:
|
||||
label = 'nested_controller'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'nested'
|
||||
arguments = [(['-t'], dict())]
|
||||
|
||||
@controller.expose()
|
||||
def nested_cmd1(self):
|
||||
pass
|
||||
|
||||
|
||||
class AliasesOnly(controller.CementBaseController):
|
||||
|
||||
class Meta:
|
||||
label = 'aliases_only_controller'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'nested'
|
||||
aliases = ['this_is_ao_controller']
|
||||
aliases_only = True
|
||||
|
||||
@controller.expose(aliases=['ao_cmd1'], aliases_only=True)
|
||||
def aliases_only_cmd1(self):
|
||||
pass
|
||||
|
||||
@controller.expose(aliases=['ao_cmd2', 'ao2'], aliases_only=True)
|
||||
def aliases_only_cmd2(self):
|
||||
pass
|
||||
|
||||
|
||||
class DuplicateCommand(controller.CementBaseController):
|
||||
|
||||
class Meta:
|
||||
label = 'duplicate_command'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'embedded'
|
||||
|
||||
@controller.expose()
|
||||
def default(self):
|
||||
pass
|
||||
|
||||
|
||||
class DuplicateAlias(controller.CementBaseController):
|
||||
|
||||
class Meta:
|
||||
label = 'duplicate_command'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'embedded'
|
||||
|
||||
@controller.expose(aliases=['default'])
|
||||
def cmd(self):
|
||||
pass
|
||||
|
||||
|
||||
class Bad(controller.CementBaseController):
|
||||
|
||||
class Meta:
|
||||
label = 'bad_controller'
|
||||
arguments = []
|
||||
|
||||
|
||||
class BadStackedType(controller.CementBaseController):
|
||||
|
||||
class Meta:
|
||||
label = 'bad_stacked_type'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'bogus'
|
||||
arguments = []
|
||||
|
||||
|
||||
class ArgumentConflict(controller.CementBaseController):
|
||||
|
||||
class Meta:
|
||||
label = 'embedded'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'embedded'
|
||||
arguments = [(['-f', '--foo'], dict())]
|
||||
|
||||
|
||||
class Unstacked(controller.CementBaseController):
|
||||
|
||||
class Meta:
|
||||
label = 'unstacked'
|
||||
stacked_on = None
|
||||
arguments = [
|
||||
(['--foo6'], dict(dest='foo6')),
|
||||
]
|
||||
|
||||
|
||||
class BadStackType(controller.CementBaseController):
|
||||
|
||||
class Meta:
|
||||
label = 'bad_stack_type'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'bogus_stacked_type'
|
||||
arguments = [
|
||||
(['--foo6'], dict(dest='foo6')),
|
||||
]
|
||||
|
||||
|
||||
class ControllerTestCase(test.CementCoreTestCase):
|
||||
|
||||
def test_default(self):
|
||||
app = self.make_app(base_controller=TestController)
|
||||
app.setup()
|
||||
app.run()
|
||||
|
||||
def test_epilog(self):
|
||||
app = self.make_app(base_controller=TestController)
|
||||
app.setup()
|
||||
app.run()
|
||||
self.eq(app.args.epilog, 'This is the epilog')
|
||||
|
||||
def test_txt_defined_base_controller(self):
|
||||
self.app.handler.register(TestController)
|
||||
self.app.setup()
|
||||
|
||||
@test.raises(exc.InterfaceError)
|
||||
def test_invalid_arguments_1(self):
|
||||
Bad.Meta.arguments = ['this is invalid']
|
||||
self.app.handler.register(Bad)
|
||||
|
||||
@test.raises(exc.InterfaceError)
|
||||
def test_invalid_arguments_2(self):
|
||||
Bad.Meta.arguments = [('this is also invalid', dict())]
|
||||
self.app.handler.register(Bad)
|
||||
|
||||
@test.raises(exc.InterfaceError)
|
||||
def test_invalid_arguments_3(self):
|
||||
Bad.Meta.arguments = [(['-f'], 'and this is invalid')]
|
||||
self.app.handler.register(Bad)
|
||||
|
||||
@test.raises(exc.InterfaceError)
|
||||
def test_invalid_arguments_4(self):
|
||||
Bad.Meta.arguments = 'totally jacked'
|
||||
self.app.handler.register(Bad)
|
||||
|
||||
def test_embedded_controller(self):
|
||||
app = self.make_app(argv=['embedded-cmd1'])
|
||||
app.handler.register(TestController)
|
||||
app.handler.register(Embedded)
|
||||
app.setup()
|
||||
app.run()
|
||||
|
||||
check = 'embedded-cmd1' in app.controller._visible_commands
|
||||
self.ok(check)
|
||||
|
||||
# also check for the alias here
|
||||
check = 'emcmd1' in app.controller._dispatch_map
|
||||
self.ok(check)
|
||||
|
||||
def test_nested_controller(self):
|
||||
app = self.make_app(argv=['nested-controller'])
|
||||
app.handler.register(TestController)
|
||||
app.handler.register(Nested)
|
||||
app.setup()
|
||||
app.run()
|
||||
|
||||
check = 'nested-controller' in app.controller._visible_commands
|
||||
self.ok(check)
|
||||
|
||||
self.eq(app.controller._dispatch_command['func_name'], '_dispatch')
|
||||
|
||||
def test_aliases_only_controller(self):
|
||||
app = self.make_app(argv=['aliases-only-controller'])
|
||||
app.handler.register(TestController)
|
||||
app.handler.register(AliasesOnly)
|
||||
app.setup()
|
||||
app.run()
|
||||
|
||||
@test.raises(exc.FrameworkError)
|
||||
def test_bad_stacked_type(self):
|
||||
app = self.make_app()
|
||||
app.handler.register(TestController)
|
||||
app.handler.register(BadStackedType)
|
||||
app.setup()
|
||||
app.run()
|
||||
|
||||
@test.raises(exc.FrameworkError)
|
||||
def test_duplicate_command(self):
|
||||
app = self.make_app()
|
||||
app.handler.register(TestController)
|
||||
app.handler.register(DuplicateCommand)
|
||||
app.setup()
|
||||
app.run()
|
||||
|
||||
@test.raises(exc.FrameworkError)
|
||||
def test_duplicate_alias(self):
|
||||
app = self.make_app()
|
||||
app.handler.register(TestController)
|
||||
app.handler.register(DuplicateAlias)
|
||||
app.setup()
|
||||
app.run()
|
||||
|
||||
def test_usage_txt(self):
|
||||
app = self.make_app()
|
||||
app.handler.register(TestController)
|
||||
app.setup()
|
||||
self.eq(app.controller._usage_text, 'My Custom Usage TXT')
|
||||
|
||||
@test.raises(exc.FrameworkError)
|
||||
def test_argument_conflict(self):
|
||||
try:
|
||||
app = self.make_app(base_controller=TestController)
|
||||
app.handler.register(ArgumentConflict)
|
||||
app.setup()
|
||||
app.run()
|
||||
except NameError as e:
|
||||
# This is a hack due to a Travis-CI Bug:
|
||||
# https://github.com/travis-ci/travis-ci/issues/998
|
||||
if e.args[0] == "global name 'ngettext' is not defined":
|
||||
bug = "https://github.com/travis-ci/travis-ci/issues/998"
|
||||
raise test.SkipTest("Travis-CI Bug: %s" % bug)
|
||||
else:
|
||||
raise
|
||||
|
||||
def test_default_command_with_positional(self):
|
||||
app = self.make_app(base_controller=TestWithPositionalController,
|
||||
argv=['mypositional'])
|
||||
app.setup()
|
||||
app.run()
|
||||
self.eq(app.get_last_rendered()[0]['foo'], 'mypositional')
|
||||
|
||||
def test_load_extensions_from_config_list(self):
|
||||
defaults = init_defaults(APP)
|
||||
defaults[APP]['extensions'] = ['json', 'yaml']
|
||||
|
||||
app = self.make_app(
|
||||
label=APP,
|
||||
extensions=[],
|
||||
config_defaults=defaults,
|
||||
)
|
||||
app.setup()
|
||||
app.run()
|
||||
|
||||
res = 'cement.ext.ext_json' in app.ext._loaded_extensions
|
||||
self.ok(res)
|
||||
|
||||
res = 'cement.ext.ext_yaml' in app.ext._loaded_extensions
|
||||
self.ok(res)
|
||||
|
||||
def test_load_extensions_from_config_str(self):
|
||||
defaults = init_defaults(APP)
|
||||
defaults[APP]['extensions'] = 'json, yaml'
|
||||
|
||||
app = self.make_app(
|
||||
label=APP,
|
||||
extensions=[],
|
||||
config_defaults=defaults,
|
||||
)
|
||||
app.setup()
|
||||
app.run()
|
||||
|
||||
res = 'cement.ext.ext_json' in app.ext._loaded_extensions
|
||||
self.ok(res)
|
||||
|
||||
res = 'cement.ext.ext_yaml' in app.ext._loaded_extensions
|
||||
self.ok(res)
|
||||
|
||||
@test.raises(exc.InterfaceError)
|
||||
def test_invalid_stacked_on(self):
|
||||
self.reset_backend()
|
||||
try:
|
||||
self.app = self.make_app(APP,
|
||||
handlers=[
|
||||
TestController,
|
||||
Unstacked,
|
||||
],
|
||||
)
|
||||
with self.app as app:
|
||||
app.run()
|
||||
except exc.InterfaceError as e:
|
||||
self.ok(re.match("(.*)is not stacked anywhere!(.*)", e.msg))
|
||||
raise
|
||||
|
||||
@test.raises(exc.InterfaceError)
|
||||
def test_invalid_stacked_type(self):
|
||||
self.reset_backend()
|
||||
try:
|
||||
self.app = self.make_app(APP,
|
||||
handlers=[
|
||||
TestController,
|
||||
BadStackType,
|
||||
],
|
||||
)
|
||||
with self.app as app:
|
||||
app.run()
|
||||
except exc.InterfaceError as e:
|
||||
self.ok(re.match("(.*)has an unknown stacked type(.*)", e.msg))
|
||||
raise
|
||||
|
||||
def test_usage_text(self):
|
||||
self.reset_backend()
|
||||
self.app = self.make_app(APP,
|
||||
handlers=[
|
||||
TestController,
|
||||
],
|
||||
)
|
||||
with self.app as app:
|
||||
self.app.controller._meta.usage = None
|
||||
usage = app.controller._usage_text
|
||||
self.ok(usage.startswith('%s (sub-commands ...)' %
|
||||
self.app._meta.label))
|
||||
|
||||
def test_help_text(self):
|
||||
self.reset_backend()
|
||||
self.app = self.make_app(APP,
|
||||
handlers=[
|
||||
TestController,
|
||||
AliasesOnly,
|
||||
],
|
||||
)
|
||||
with self.app as app:
|
||||
app.run()
|
||||
app.controller._help_text
|
||||
# self.ok(usage.startswith('%s (sub-commands ...)' % \
|
||||
# self.app._meta.label))
|
||||
pass
|
||||
|
||||
@ -4,12 +4,11 @@ import os
|
||||
import sys
|
||||
import json
|
||||
import signal
|
||||
from cement import App
|
||||
from cement import App, Controller, ex
|
||||
from cement.core.foundation import cement_signal_handler
|
||||
from cement.core import exc, extension
|
||||
from cement.core.handler import CementBaseHandler
|
||||
from cement.core.controller import CementBaseController, expose
|
||||
from cement.core import output, hook, controller
|
||||
from cement.core import output, hook
|
||||
from cement.core.interface import Interface
|
||||
from cement.utils import test
|
||||
from cement.utils.misc import init_defaults, rando, minimal_logger
|
||||
@ -59,7 +58,7 @@ class TestOutputHandler(output.CementOutputHandler):
|
||||
return None
|
||||
|
||||
|
||||
class BogusBaseController(controller.CementBaseController):
|
||||
class BogusBaseController(Controller):
|
||||
|
||||
class Meta:
|
||||
label = 'bad_base_controller_label'
|
||||
@ -483,15 +482,15 @@ class FoundationTestCase(test.CementCoreTestCase):
|
||||
|
||||
@test.raises(AssertionError)
|
||||
def test_run_forever(self):
|
||||
class Controller(CementBaseController):
|
||||
class MyController(Controller):
|
||||
class Meta:
|
||||
label = 'base'
|
||||
|
||||
@expose()
|
||||
@ex()
|
||||
def runit(self):
|
||||
raise Exception("Fake some error")
|
||||
|
||||
app = self.make_app(base_controller=Controller, argv=['runit'])
|
||||
app = self.make_app(base_controller=MyController, argv=['runit'])
|
||||
|
||||
def handler(signum, frame):
|
||||
raise AssertionError('It ran forever!')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user