mirror of
https://github.com/datafolklabs/cement.git
synced 2026-02-06 14:16:46 +00:00
Resolves Issue #205
This commit is contained in:
parent
58bab70e65
commit
76d004534f
16
ChangeLog
16
ChangeLog
@ -26,9 +26,21 @@ This is a branch off of the 2.6.x stable code base. Maintenance releases for
|
||||
2.6.x will happen under the stable/2.6.x git branch, while forward feature
|
||||
development will happen as 2.7.x under the git master branch.
|
||||
|
||||
Bugs:
|
||||
Bugs:
|
||||
|
||||
* :issue:`314` - Resolved inconsistent behavor in ``colorlog`` extension.
|
||||
* :issue:`314` - Resolved inconsistent behavor in ``colorlog`` extension.
|
||||
* :issue:`316` - Running Nose tests with ``-s`` option causes traceback
|
||||
* :issue:`317` - CementApp.run() should return results from
|
||||
``Controller._dispatch()``
|
||||
|
||||
Features:
|
||||
|
||||
* :issue:`205` - Added new ``ArgparseController`` and ``expose`` decorator
|
||||
in ``ext_argparse`` to eventually replace ``CementBaseController``.
|
||||
|
||||
Refactoring:
|
||||
|
||||
* :issue:`319` - Use ``os.devnull`` instead of internal ``NullOut`` hack.
|
||||
|
||||
|
||||
2.6.0 - Thu May 14, 2015
|
||||
|
||||
@ -49,6 +49,18 @@ def controller_validator(klass, obj):
|
||||
if type(item[1]) is not dict:
|
||||
raise exc.InterfaceError(errmsg)
|
||||
|
||||
if not obj._meta.label == 'base' and obj._meta.stacked_on is None:
|
||||
errmsg = "Controller `%s` is not stacked anywhere!" % \
|
||||
obj.__class__.__name__
|
||||
raise exc.InterfaceError(errmsg)
|
||||
if not obj._meta.label == 'base' and \
|
||||
obj._meta.stacked_type not in ['nested', 'embedded']:
|
||||
raise exc.InterfaceError(
|
||||
"Controller '%s' " % obj._meta.label +
|
||||
"has an unknown stacked type of '%s'." %
|
||||
obj._meta.stacked_type
|
||||
)
|
||||
|
||||
|
||||
class IController(interface.Interface):
|
||||
|
||||
@ -383,12 +395,7 @@ class CementBaseController(handler.CementBaseHandler):
|
||||
metadict['aliases_only'] = contr._meta.aliases_only
|
||||
metadict['controller'] = contr
|
||||
commands.append(metadict)
|
||||
else:
|
||||
raise exc.FrameworkError(
|
||||
"Controller '%s' " % contr._meta.label +
|
||||
"has an unknown stacked type of '%s'." %
|
||||
contr._meta.stacked_type
|
||||
)
|
||||
|
||||
return (arguments, commands)
|
||||
|
||||
def _process_arguments(self):
|
||||
@ -443,11 +450,11 @@ class CementBaseController(handler.CementBaseHandler):
|
||||
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 _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):
|
||||
"""
|
||||
@ -470,13 +477,13 @@ class CementBaseController(handler.CementBaseHandler):
|
||||
return func()
|
||||
else:
|
||||
self._process_arguments()
|
||||
self.app._parse_args()
|
||||
self._parse_args()
|
||||
func = getattr(self._dispatch_command['controller'],
|
||||
self._dispatch_command['func_name'])
|
||||
return func()
|
||||
else:
|
||||
self._process_arguments()
|
||||
self.app._parse_args()
|
||||
self._parse_args()
|
||||
|
||||
@property
|
||||
def _usage_text(self):
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
"""Argparse Framework Extension"""
|
||||
|
||||
import re
|
||||
import argparse
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
from argparse import ArgumentParser, SUPPRESS
|
||||
from ..core import backend, arg, handler, hook
|
||||
from ..core.arg import CementArgumentHandler, IArgument
|
||||
from ..core.controller import IController
|
||||
from ..core.exc import FrameworkError
|
||||
from ..utils.misc import minimal_logger
|
||||
|
||||
LOG = minimal_logger(__name__)
|
||||
@ -16,7 +17,7 @@ def _clean_command_label(label):
|
||||
def _clean_command_func(func):
|
||||
return re.sub('-', '_', func)
|
||||
|
||||
class ArgparseArgumentHandler(arg.CementArgumentHandler, ArgumentParser):
|
||||
class ArgparseArgumentHandler(CementArgumentHandler, ArgumentParser):
|
||||
|
||||
"""
|
||||
This class implements the :ref:`IArgument <cement.core.arg>`
|
||||
@ -33,7 +34,7 @@ class ArgparseArgumentHandler(arg.CementArgumentHandler, ArgumentParser):
|
||||
|
||||
"""Handler meta-data."""
|
||||
|
||||
interface = arg.IArgument
|
||||
interface = IArgument
|
||||
"""The interface that this class implements."""
|
||||
|
||||
label = 'argparse'
|
||||
@ -56,13 +57,20 @@ class ArgparseArgumentHandler(arg.CementArgumentHandler, ArgumentParser):
|
||||
args = self.parse_args(arg_list)
|
||||
return args
|
||||
|
||||
def add(self, *args, **kw):
|
||||
"""
|
||||
A slightly more convenient synonym for ``add_argument``.
|
||||
"""
|
||||
self.add_argument(*args, **kw)
|
||||
|
||||
def add_argument(self, *args, **kw):
|
||||
"""
|
||||
Add an argument to the parser. Arguments and keyword arguments are
|
||||
passed directly to ArgumentParser.add_argument().
|
||||
|
||||
passed directly to ``ArgumentParser.add_argument()``. See the
|
||||
`Argparse Documentation
|
||||
<http://docs.python.org/dev/library/argparse.html>`_ for help.
|
||||
"""
|
||||
return super(ArgumentParser, self).add_argument(*args, **kw)
|
||||
super(ArgparseArgumentHandler, self).add_argument(*args, **kw)
|
||||
|
||||
|
||||
# FIXME: Backward compat name, will remove in Cement 3.x
|
||||
@ -73,13 +81,14 @@ class ArgParseArgumentHandler(ArgparseArgumentHandler):
|
||||
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.
|
||||
Used to expose functions to be listed as sub-commands under the
|
||||
controller namespace. It also decorates the function with meta-data for
|
||||
the argument parser.
|
||||
|
||||
:param hide: Whether the command should be visible.
|
||||
:type hide: boolean
|
||||
:param arguments: List of tuples that define arguments to add to this
|
||||
command sub-parser.
|
||||
commands sub-parser.
|
||||
:keyword parser_options: Additional options to pass to Argparse.
|
||||
:type parser_options: dict
|
||||
|
||||
@ -87,16 +96,16 @@ class expose(object):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cement.core.controller import CementBaseController, expose
|
||||
from cement.ext.ext_argparse import ArgparseController, expose
|
||||
|
||||
class MyAppBaseController(CementBaseController):
|
||||
class Base(ArgparseController):
|
||||
class Meta:
|
||||
label = 'base'
|
||||
|
||||
# Note: Default functions only work on Python 3.4+
|
||||
# Note: Default functions only work in Python > 3.4
|
||||
@expose(hide=True)
|
||||
def default(self):
|
||||
print("In MyAppBaseController.default()")
|
||||
print("In Base.default()")
|
||||
|
||||
@expose(
|
||||
help='this is the help message for my_command',
|
||||
@ -107,7 +116,7 @@ class expose(object):
|
||||
]
|
||||
)
|
||||
def my_command(self):
|
||||
print("In MyAppBaseController.my_command()")
|
||||
print("In Base.my_command()")
|
||||
|
||||
"""
|
||||
# pylint: disable=W0622
|
||||
@ -131,19 +140,22 @@ class expose(object):
|
||||
func.__cement_meta__ = metadict
|
||||
return func
|
||||
|
||||
# FIX ME: Should be refactored into separate BaseController and Controller
|
||||
# classes in Cement 3, but that would break the interface spec in 2.x
|
||||
class ArgparseController(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.
|
||||
`IController <#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
|
||||
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 or modify this one.
|
||||
|
||||
NOTE: This is a re-implementation of CementBaseController. In the future,
|
||||
this class will eventually replace CementBaseController.
|
||||
NOTE: This is a re-implementation of
|
||||
`CementBaseController <#cement.core.controller.CementBaseController>`_.
|
||||
In the future, this class will eventually replace it as the default.
|
||||
|
||||
Usage:
|
||||
|
||||
@ -151,22 +163,23 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
|
||||
from cement.ext.ext_argparse import ArgparseController
|
||||
|
||||
class MyAppBaseController(ArgparseController):
|
||||
class Base(ArgparseController):
|
||||
class Meta:
|
||||
label = 'base'
|
||||
description = 'MyApp is awesome'
|
||||
config_defaults = dict()
|
||||
arguments = []
|
||||
epilog = "This is the text at the bottom of --help."
|
||||
# ...
|
||||
description = 'description at the top of --help'
|
||||
epilog = "the text at the bottom of --help."
|
||||
arguments = [
|
||||
(['-f', '--foo'], dict(help='my foo option', dest='foo')),
|
||||
]
|
||||
|
||||
class MyStackedController(ArgparseController):
|
||||
class Second(ArgparseController):
|
||||
class Meta:
|
||||
label = 'second_controller'
|
||||
aliases = ['sec', 'secondary']
|
||||
label = 'second'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'embedded'
|
||||
# ...
|
||||
arguments = [
|
||||
(['--foo2'], dict(help='my foo2 option', dest='foo2')),
|
||||
]
|
||||
|
||||
"""
|
||||
|
||||
@ -185,7 +198,7 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
label = 'base'
|
||||
|
||||
#: A list of aliases for the controller/sub-parser. **Only available
|
||||
#: in Python 3+.
|
||||
#: in Python > 3**.
|
||||
aliases = []
|
||||
|
||||
#: A config [section] to merge config_defaults into. Cement will
|
||||
@ -257,7 +270,7 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
#: restraints in how Cement discovers and maps commands.
|
||||
#:
|
||||
#: Note: Currently, default function/sub-command only functions on
|
||||
#: Python 3.4+. Previous versions of Python/Argparse will throw the
|
||||
#: Python > 3.4. Previous versions of Python/Argparse will throw the
|
||||
#: exception ``error: too few arguments``.
|
||||
default_func = 'default'
|
||||
|
||||
@ -281,12 +294,16 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
self.app = app
|
||||
|
||||
def _setup_controllers(self):
|
||||
# note this is only called on base controller, so self == 'base'
|
||||
|
||||
# need a list to maintain order
|
||||
resolved_controllers = []
|
||||
|
||||
# need a dict to do key/label based lookup
|
||||
resolved_controllers_map = {}
|
||||
|
||||
# list to maintain which controllers we haven't resolved yet
|
||||
unresolved_controllers = []
|
||||
for contr in handler.list('controller'):
|
||||
# don't include self
|
||||
# don't include self/base
|
||||
if contr == self.__class__:
|
||||
continue
|
||||
|
||||
@ -294,8 +311,9 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
contr._setup(self.app)
|
||||
unresolved_controllers.append(contr)
|
||||
|
||||
# treat base/self separately
|
||||
# treat self/base separately
|
||||
resolved_controllers.append(self)
|
||||
resolved_controllers_map['base'] = self
|
||||
|
||||
# all this crazy shit is to resolve controllers in the order that they
|
||||
# are nested/embedded, otherwise argparse does weird things
|
||||
@ -304,10 +322,15 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
|
||||
current_parent = self._meta.label
|
||||
while unresolved_controllers:
|
||||
LOG.debug('unresolved controllers > %s' % unresolved_controllers)
|
||||
LOG.debug('current parent > %s' % current_parent)
|
||||
|
||||
# handle all controllers nested on parent
|
||||
current_children = []
|
||||
resolved_child_controllers = []
|
||||
for contr in list(unresolved_controllers):
|
||||
# if stacked_on is the current parent, we want to process
|
||||
# its children in this run first
|
||||
if contr._meta.stacked_on == current_parent:
|
||||
current_children.append(contr)
|
||||
if contr._meta.stacked_type == 'embedded':
|
||||
@ -315,9 +338,23 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
else:
|
||||
resolved_child_controllers.insert(0, contr)
|
||||
unresolved_controllers.remove(contr)
|
||||
LOG.debug('resolve controller %s' % contr)
|
||||
LOG.debug('resolved controller %s %s on %s' % \
|
||||
(contr, contr._meta.stacked_type,
|
||||
current_parent))
|
||||
|
||||
# if not, fall back on whether the stacked_on parent is
|
||||
# already resolved
|
||||
elif contr._meta.stacked_on in resolved_controllers_map.keys():
|
||||
resolved_controllers.append(contr)
|
||||
resolved_controllers_map[contr._meta.label] = contr
|
||||
unresolved_controllers.remove(contr)
|
||||
LOG.debug('resolved controller %s %s on %s' % \
|
||||
(contr, contr._meta.stacked_type,
|
||||
contr._meta.stacked_on))
|
||||
|
||||
resolved_controllers.extend(resolved_child_controllers)
|
||||
for contr in resolved_child_controllers:
|
||||
resolved_controllers_map[contr._meta.label] = contr
|
||||
|
||||
# then, for all those controllers... handler all controllers
|
||||
# nested on them
|
||||
@ -328,13 +365,17 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
if contr._meta.stacked_type == 'embedded':
|
||||
resolved_child_controllers.append(contr)
|
||||
else:
|
||||
|
||||
resolved_child_controllers.insert(0, contr)
|
||||
|
||||
unresolved_controllers.remove(contr)
|
||||
LOG.debug('resolve controller %s' % contr)
|
||||
LOG.debug('resolved controller %s %s on %s' % \
|
||||
(contr, contr._meta.stacked_type,
|
||||
child_contr._meta.label))
|
||||
|
||||
resolved_controllers.extend(resolved_child_controllers)
|
||||
for contr in resolved_child_controllers:
|
||||
resolved_controllers_map[contr._meta.label] = contr
|
||||
|
||||
# re-iterate with the next in line as the parent (handles multiple
|
||||
# level nesting)
|
||||
if unresolved_controllers:
|
||||
@ -342,10 +383,7 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
|
||||
|
||||
self._controllers = resolved_controllers
|
||||
|
||||
# so that we can look up by controller label in self._dispatch
|
||||
for contr in resolved_controllers:
|
||||
self._controllers_map[contr._meta.label] = contr
|
||||
self._controllers_map = resolved_controllers_map
|
||||
|
||||
def _process_parsed_arguments(self):
|
||||
pass
|
||||
@ -356,16 +394,16 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
if 'title' not in kwargs.keys():
|
||||
kwargs['title'] = contr._meta.title
|
||||
|
||||
kwargs['dest'] = '__subparser__'
|
||||
kwargs['dest'] = 'command'
|
||||
|
||||
return kwargs
|
||||
|
||||
def _get_parser_options(self, contr):
|
||||
kwargs = contr._meta.parser_options.copy()
|
||||
|
||||
if sys.version_info[0] > 3:
|
||||
if 'aliases' not in kwargs.keys():
|
||||
kwargs['aliases'] = contr._meta.aliases
|
||||
if sys.version_info[0] >= 3:
|
||||
if 'aliases' not in kwargs.keys(): # pragma: nocover
|
||||
kwargs['aliases'] = contr._meta.aliases # pragma: nocover
|
||||
|
||||
if 'description' not in kwargs.keys():
|
||||
kwargs['description'] = contr._meta.description
|
||||
@ -373,6 +411,8 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
kwargs['usage'] = contr._meta.usage
|
||||
if 'epilog' not in kwargs.keys():
|
||||
kwargs['epilog'] = contr._meta.epilog
|
||||
if 'help' not in kwargs.keys():
|
||||
kwargs['help'] = contr._meta.help
|
||||
|
||||
if contr._meta.hide == True:
|
||||
if 'help' in kwargs.keys():
|
||||
@ -405,10 +445,6 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
|
||||
def _setup_parsers(self):
|
||||
# this should only be run by the base controller
|
||||
if not self._meta.label == 'base':
|
||||
raise FrameworkError('Private function _setup_parsers() called'
|
||||
'from non-base controller')
|
||||
|
||||
from cement.utils.misc import rando
|
||||
|
||||
_rando = rando()[:12]
|
||||
@ -424,11 +460,11 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
parsers['base'] = self.app.args
|
||||
|
||||
# handle base controller separately
|
||||
parsers['base'].add_argument(self._controller_option,
|
||||
parsers['base'].add(self._controller_option,
|
||||
action='store',
|
||||
default='base',
|
||||
nargs='?',
|
||||
help=argparse.SUPPRESS,
|
||||
help=SUPPRESS,
|
||||
dest='__controller_namespace__',
|
||||
)
|
||||
|
||||
@ -441,7 +477,7 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
|
||||
# and if only base controller registered... go ahead and return
|
||||
if len(handler.list('controller')) <= 1:
|
||||
return
|
||||
return # pragma: nocover
|
||||
|
||||
# note that the order of self._controllers was already organized by
|
||||
# stacking/embedding order in self._setup_controllers ... order is
|
||||
@ -468,10 +504,10 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
|
||||
# add an invisible controller option so we can figure out what
|
||||
# to call later in self._dispatch
|
||||
parsers[label].add_argument(self._controller_option,
|
||||
parsers[label].add(self._controller_option,
|
||||
action='store',
|
||||
default=contr._meta.label,
|
||||
help=argparse.SUPPRESS,
|
||||
help=SUPPRESS,
|
||||
dest='__controller_namespace__',
|
||||
)
|
||||
|
||||
@ -482,22 +518,16 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
parsers[label] = parsers[stacked_on]
|
||||
|
||||
def _get_parser_by_controller(self, controller):
|
||||
if controller._meta.stacked_on:
|
||||
if controller._meta.stacked_type == 'embedded':
|
||||
parser = self._get_parser(controller._meta.stacked_on)
|
||||
else:
|
||||
parser = self._get_parser(controller._meta.label)
|
||||
if controller._meta.stacked_type == 'embedded':
|
||||
parser = self._get_parser(controller._meta.stacked_on)
|
||||
else:
|
||||
parser = self._get_parser(controller._meta.label)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_parser_parent_by_controller(self, controller):
|
||||
if controller._meta.stacked_on:
|
||||
if controller._meta.stacked_type == 'embedded':
|
||||
parent = self._get_parser_parent(controller._meta.stacked_on)
|
||||
else:
|
||||
parent = self._get_parser_parent(controller._meta.label)
|
||||
if controller._meta.stacked_type == 'embedded':
|
||||
parent = self._get_parser_parent(controller._meta.stacked_on)
|
||||
else:
|
||||
parent = self._get_parser_parent(controller._meta.label)
|
||||
|
||||
@ -518,12 +548,9 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
parser = self._get_parser_by_controller(controller)
|
||||
arguments = controller._collect_arguments()
|
||||
for arg, kw in arguments:
|
||||
try:
|
||||
LOG.debug('adding argument (args=%s, kwargs=%s)' % \
|
||||
(arg, kw))
|
||||
parser.add_argument(*arg, **kw)
|
||||
except argparse.ArgumentError as e:
|
||||
raise exc.FrameworkError(e.__str__())
|
||||
LOG.debug('adding argument (args=%s, kwargs=%s)' % \
|
||||
(arg, kw))
|
||||
parser.add(*arg, **kw)
|
||||
|
||||
def _process_commands(self, controller):
|
||||
label = controller._meta.label
|
||||
@ -545,11 +572,11 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
|
||||
# add an invisible dispatch option so we can figure out what to
|
||||
# call later in self._dispatch
|
||||
command_parser.add_argument(self._dispatch_option,
|
||||
command_parser.add(self._dispatch_option,
|
||||
action='store',
|
||||
default="%s.%s" % (command['controller']._meta.label,
|
||||
command['func_name']),
|
||||
help=argparse.SUPPRESS,
|
||||
help=SUPPRESS,
|
||||
dest='__dispatch__',
|
||||
)
|
||||
|
||||
@ -557,12 +584,9 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
LOG.debug("processing arguments for '%s' " % command['label'] + \
|
||||
"command namespace")
|
||||
for arg, kw in command['arguments']:
|
||||
try:
|
||||
LOG.debug('adding argument (args=%s, kwargs=%s)' % \
|
||||
(arg, kw))
|
||||
command_parser.add_argument(*arg, **kw)
|
||||
except argparse.ArgumentError as e:
|
||||
raise exc.FrameworkError(e.__str__())
|
||||
LOG.debug('adding argument (args=%s, kwargs=%s)' % \
|
||||
(arg, kw))
|
||||
command_parser.add(*arg, **kw)
|
||||
|
||||
def _collect(self):
|
||||
arguments = self._collect_arguments()
|
||||
@ -615,8 +639,15 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
# controller with not sub-command (argparse doesn't support
|
||||
# default sub-command yet... so we rely on
|
||||
# __controller_namespace__ and it's default func
|
||||
contr_label = self.app.pargs.__controller_namespace__
|
||||
func_name = _clean_command_func(contr._meta.default_func)
|
||||
|
||||
# We never get here on Python < 3 as Argparse would have already
|
||||
# complained about too few arguments
|
||||
contr_label = self.app.pargs\
|
||||
.__controller_namespace__ # pragma: nocover
|
||||
contr = self._controllers_map[contr_label] # pragma: nocover
|
||||
func_name = _clean_command_func( # pragma: nocover
|
||||
contr._meta.default_func # pragma: nocover
|
||||
) # pragma: nocover
|
||||
|
||||
if contr_label == 'base':
|
||||
contr = self
|
||||
@ -627,10 +658,15 @@ class ArgparseController(handler.CementBaseHandler):
|
||||
func = getattr(contr, func_name)
|
||||
return func()
|
||||
else:
|
||||
self.app.log.fatal(
|
||||
"Controller function does not exist " + \
|
||||
"%s.%s()" % (contr.__class__, func_name))
|
||||
return None
|
||||
# only time that we'd get here is if Controller.Meta.default_func
|
||||
# is pointing to something that doesn't exist
|
||||
#
|
||||
# We never get here on Python < 3 as Argparse would have already
|
||||
# complained about too few arguments
|
||||
raise FrameworkError( # pragma: nocover
|
||||
"Controller function does not exist %s.%s()" % \
|
||||
(contr.__class__.__name__, func_name)) # pragma: nocover
|
||||
|
||||
|
||||
def load(app):
|
||||
handler.register(ArgparseArgumentHandler)
|
||||
|
||||
@ -3,6 +3,23 @@
|
||||
What's New
|
||||
==========
|
||||
|
||||
New Features in Cement 2.7
|
||||
--------------------------
|
||||
|
||||
ArgparseController
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Work has finally begun, and is mostly completed on the complete refactoring of
|
||||
``CementBaseController``. The new ``ArgparseController`` and ``expose``
|
||||
decorator from the ``argparse`` extension have the following added benefits
|
||||
over ``CementBaseController``:
|
||||
|
||||
* FIX ME
|
||||
|
||||
Related:
|
||||
|
||||
* :issue:`205`
|
||||
|
||||
New Features in Cement 2.6
|
||||
--------------------------
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
"""Tests for cement.core.controller."""
|
||||
|
||||
import re
|
||||
from cement.core import exc, controller, handler
|
||||
from cement.utils import test
|
||||
from cement.utils.misc import rando, init_defaults
|
||||
@ -21,6 +22,10 @@ class TestController(controller.CementBaseController):
|
||||
def default(self):
|
||||
pass
|
||||
|
||||
@controller.expose()
|
||||
def some_command(self):
|
||||
pass
|
||||
|
||||
|
||||
class TestWithPositionalController(controller.CementBaseController):
|
||||
|
||||
@ -128,6 +133,25 @@ class ArgumentConflict(controller.CementBaseController):
|
||||
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):
|
||||
@ -286,3 +310,62 @@ class ControllerTestCase(test.CementCoreTestCase):
|
||||
|
||||
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:
|
||||
res = 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:
|
||||
res = 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()
|
||||
help = app.controller._help_text
|
||||
# self.ok(usage.startswith('%s (sub-commands ...)' % \
|
||||
# self.app._meta.label))
|
||||
@ -1,37 +1,72 @@
|
||||
"""Tests for cement.ext.ext_argparse."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
from argparse import ArgumentError
|
||||
from cement.ext.ext_argparse import ArgparseArgumentHandler
|
||||
from cement.ext.ext_argparse import ArgparseController, expose
|
||||
from cement.ext.ext_argparse import _clean_command_label, _clean_command_func
|
||||
from cement.utils import test
|
||||
from cement.utils.misc import rando, init_defaults
|
||||
from cement.core import handler
|
||||
from cement.core.exc import InterfaceError, FrameworkError
|
||||
|
||||
APP = rando()[:12]
|
||||
|
||||
if (sys.version_info[0] > 3 and sys.version_info[1] >= 4):
|
||||
ARGPARSE_SUPPORTS_DEFAULTS = True
|
||||
else:
|
||||
ARGPARSE_SUPPORTS_DEFAULTS = False
|
||||
|
||||
class Base(ArgparseController):
|
||||
class Meta:
|
||||
label = 'base'
|
||||
arguments = [
|
||||
(['--foo'], dict(dest='foo')),
|
||||
]
|
||||
|
||||
@expose(hide=True, help="this help doesn't get seen")
|
||||
def default(self):
|
||||
return "Inside Base.default"
|
||||
|
||||
@expose()
|
||||
def cmd1(self):
|
||||
return "Inside Base.cmd1"
|
||||
|
||||
@expose()
|
||||
def command_with_dashes(self):
|
||||
return "Inside Base.command_with_dashes"
|
||||
|
||||
class Second(ArgparseController):
|
||||
class Meta:
|
||||
label = 'second'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'embedded'
|
||||
arguments = [
|
||||
(['--foo2'], dict(dest='foo2')),
|
||||
]
|
||||
|
||||
@expose()
|
||||
@expose(
|
||||
arguments=[
|
||||
(['--cmd2-foo'],
|
||||
dict(help='cmd2 sub-command only options', dest='cmd2_foo')),
|
||||
]
|
||||
)
|
||||
def cmd2(self):
|
||||
return "Inside Second.cmd2"
|
||||
if self.app.pargs.cmd2_foo:
|
||||
return "Inside Second.cmd2 : Foo > %s" % self.app.pargs.cmd2_foo
|
||||
else:
|
||||
return "Inside Second.cmd2"
|
||||
|
||||
class Third(ArgparseController):
|
||||
class Meta:
|
||||
label = 'third'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'nested'
|
||||
arguments = [
|
||||
(['--foo3'], dict(dest='foo3')),
|
||||
]
|
||||
|
||||
@expose(hide=True)
|
||||
def default(self):
|
||||
@ -48,6 +83,9 @@ class Fourth(ArgparseController):
|
||||
stacked_type = 'embedded'
|
||||
hide = True
|
||||
help = "this help doesn't get seen cause we're hiding"
|
||||
arguments = [
|
||||
(['--foo4'], dict(dest='foo4')),
|
||||
]
|
||||
|
||||
@expose()
|
||||
def cmd4(self):
|
||||
@ -58,6 +96,11 @@ class Fifth(ArgparseController):
|
||||
label = 'fifth'
|
||||
stacked_on = 'third'
|
||||
stacked_type = 'nested'
|
||||
hide = True
|
||||
help = "this help isn't seen... i'm hiding"
|
||||
arguments = [
|
||||
(['--foo5'], dict(dest='foo5')),
|
||||
]
|
||||
|
||||
@expose(hide=True)
|
||||
def default(self):
|
||||
@ -67,6 +110,101 @@ class Fifth(ArgparseController):
|
||||
def cmd5(self):
|
||||
return "Inside Fifth.cmd5"
|
||||
|
||||
class Sixth(ArgparseController):
|
||||
class Meta:
|
||||
label = 'sixth'
|
||||
stacked_on = 'fifth'
|
||||
stacked_type = 'nested'
|
||||
arguments = [
|
||||
(['--foo6'], dict(dest='foo6')),
|
||||
]
|
||||
|
||||
@expose(hide=True)
|
||||
def default(self):
|
||||
return "Inside Sixth.default"
|
||||
|
||||
@expose()
|
||||
def cmd6(self):
|
||||
return "Inside Sixth.cmd6"
|
||||
|
||||
class Seventh(ArgparseController):
|
||||
class Meta:
|
||||
label = 'seventh'
|
||||
stacked_on = 'fourth'
|
||||
stacked_type = 'embedded'
|
||||
arguments = [
|
||||
(['--foo7'], dict(dest='foo7')),
|
||||
]
|
||||
|
||||
@expose()
|
||||
def cmd7(self):
|
||||
return "Inside Seventh.cmd7"
|
||||
|
||||
class Unstacked(ArgparseController):
|
||||
class Meta:
|
||||
label = 'unstacked'
|
||||
stacked_on = None
|
||||
arguments = [
|
||||
(['--foo6'], dict(dest='foo6')),
|
||||
]
|
||||
|
||||
class BadStackType(ArgparseController):
|
||||
class Meta:
|
||||
label = 'bad_stack_type'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'bogus_stacked_type'
|
||||
arguments = [
|
||||
(['--foo6'], dict(dest='foo6')),
|
||||
]
|
||||
|
||||
class DuplicateArguments(ArgparseController):
|
||||
class Meta:
|
||||
label = 'duplicate_arguments'
|
||||
arguments = [
|
||||
(['--foo'], dict(dest='foo')),
|
||||
]
|
||||
|
||||
class ControllerCommandDuplicateArguments(ArgparseController):
|
||||
class Meta:
|
||||
label = 'controller_command_duplicate_arguments'
|
||||
|
||||
@expose(
|
||||
arguments = [
|
||||
(['--foo'], dict(dest='foo')),
|
||||
(['--foo'], dict(dest='foo')),
|
||||
]
|
||||
)
|
||||
def sub_command(self):
|
||||
pass
|
||||
|
||||
class AlternativeDefault(ArgparseController):
|
||||
class Meta:
|
||||
label = 'alternative_default'
|
||||
default_func = 'alternative_default'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'nested'
|
||||
|
||||
@expose(hide=True)
|
||||
def alternative_default(self):
|
||||
return "Inside AlternativeDefault.alternative_default"
|
||||
|
||||
class BadAlternativeDefault(ArgparseController):
|
||||
class Meta:
|
||||
label = 'bad_alternative_default'
|
||||
default_func = 'bogus_default'
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'nested'
|
||||
|
||||
class Aliases(ArgparseController):
|
||||
class Meta:
|
||||
label = 'aliases'
|
||||
aliases = ['aliases-controller', 'ac']
|
||||
stacked_on = 'base'
|
||||
stacked_type = 'nested'
|
||||
|
||||
@expose(aliases=['aliases-cmd-1', 'ac1'])
|
||||
def aliases_cmd1(self):
|
||||
return "Inside Aliases.aliases_cmd1"
|
||||
|
||||
class ArgparseExtTestCase(test.CementExtTestCase):
|
||||
|
||||
@ -75,46 +213,358 @@ class ArgparseExtTestCase(test.CementExtTestCase):
|
||||
self.app = self.make_app(APP,
|
||||
argument_handler=ArgparseArgumentHandler,
|
||||
handlers=[
|
||||
Sixth,
|
||||
Base,
|
||||
Second,
|
||||
Third,
|
||||
Fourth,
|
||||
Fifth,
|
||||
Seventh,
|
||||
],
|
||||
)
|
||||
|
||||
def test_base_controller_default(self):
|
||||
def test_clean_command_label(self):
|
||||
self.eq(_clean_command_label('some_cmd_name'), 'some-cmd-name')
|
||||
|
||||
def test_clean_command_func(self):
|
||||
self.eq(_clean_command_func('some-cmd-name'), 'some_cmd_name')
|
||||
|
||||
def test_base_default(self):
|
||||
if not ARGPARSE_SUPPORTS_DEFAULTS:
|
||||
raise test.SkipTest(
|
||||
'Argparse does not support default commands in Python < 3.4'
|
||||
)
|
||||
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Base.default")
|
||||
|
||||
def test_base_cmd1(self):
|
||||
with self.app as app:
|
||||
app._meta.argv = ['cmd1']
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Base.cmd1")
|
||||
|
||||
def test_base_command_with_dashes(self):
|
||||
with self.app as app:
|
||||
app._meta.argv = ['command-with-dashes']
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Base.command_with_dashes")
|
||||
|
||||
def test_controller_commands(self):
|
||||
with self.app as app:
|
||||
app._meta.argv = ['cmd2']
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Second.cmd2")
|
||||
|
||||
self.setUp()
|
||||
with self.app as app:
|
||||
app._meta.argv = ['third', 'cmd3']
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Third.cmd3")
|
||||
|
||||
self.setUp()
|
||||
with self.app as app:
|
||||
app._meta.argv = ['third', 'cmd4']
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Fourth.cmd4")
|
||||
|
||||
self.setUp()
|
||||
with self.app as app:
|
||||
app._meta.argv = ['third', 'fifth', 'cmd5']
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Fifth.cmd5")
|
||||
|
||||
self.setUp()
|
||||
with self.app as app:
|
||||
app._meta.argv = ['third', 'fifth', 'sixth', 'cmd6']
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Sixth.cmd6")
|
||||
|
||||
self.setUp()
|
||||
with self.app as app:
|
||||
app._meta.argv = ['third', 'cmd7']
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Seventh.cmd7")
|
||||
|
||||
def test_base_cmd1_parsing(self):
|
||||
with self.app as app:
|
||||
app._meta.argv = ['--foo=bar', 'cmd1']
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Base.cmd1")
|
||||
self.eq(app.pargs.foo, 'bar')
|
||||
|
||||
def test_second_cmd2(self):
|
||||
with self.app as app:
|
||||
app._meta.argv = ['--foo=bar', '--foo2=bar2', 'cmd2']
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Second.cmd2")
|
||||
self.eq(app.pargs.foo, 'bar')
|
||||
self.eq(app.pargs.foo2, 'bar2')
|
||||
|
||||
def test_third_cmd3(self):
|
||||
with self.app as app:
|
||||
app._meta.argv = [
|
||||
'--foo=bar', '--foo2=bar2',
|
||||
'third', '--foo3=bar3', '--foo4=bar4', '--foo7=bar7', 'cmd3',
|
||||
]
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Third.cmd3")
|
||||
self.eq(app.pargs.foo, 'bar')
|
||||
self.eq(app.pargs.foo2, 'bar2')
|
||||
self.eq(app.pargs.foo3, 'bar3')
|
||||
self.eq(app.pargs.foo4, 'bar4')
|
||||
self.eq(app.pargs.foo7, 'bar7')
|
||||
|
||||
def test_fifth_cmd5(self):
|
||||
with self.app as app:
|
||||
app._meta.argv = [
|
||||
'--foo=bar', '--foo2=bar2',
|
||||
'third', '--foo3=bar3', '--foo4=bar4',
|
||||
'fifth', '--foo5=bar5', 'cmd5'
|
||||
]
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Fifth.cmd5")
|
||||
self.eq(app.pargs.foo, 'bar')
|
||||
self.eq(app.pargs.foo2, 'bar2')
|
||||
self.eq(app.pargs.foo3, 'bar3')
|
||||
self.eq(app.pargs.foo4, 'bar4')
|
||||
self.eq(app.pargs.foo5, 'bar5')
|
||||
|
||||
def test_sixth_cmd6(self):
|
||||
with self.app as app:
|
||||
app._meta.argv = [
|
||||
'--foo=bar', '--foo2=bar2',
|
||||
'third', '--foo3=bar3', '--foo4=bar4',
|
||||
'fifth', '--foo5=bar5', 'sixth', '--foo6=bar6', 'cmd6',
|
||||
]
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Sixth.cmd6")
|
||||
self.eq(app.pargs.foo, 'bar')
|
||||
self.eq(app.pargs.foo2, 'bar2')
|
||||
self.eq(app.pargs.foo3, 'bar3')
|
||||
self.eq(app.pargs.foo4, 'bar4')
|
||||
self.eq(app.pargs.foo5, 'bar5')
|
||||
self.eq(app.pargs.foo6, 'bar6')
|
||||
|
||||
def test_seventh_cmd7(self):
|
||||
with self.app as app:
|
||||
app._meta.argv = [
|
||||
'--foo=bar', '--foo2=bar2',
|
||||
'third', '--foo3=bar3', '--foo4=bar4', '--foo7=bar7', 'cmd7',
|
||||
]
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Seventh.cmd7")
|
||||
self.eq(app.pargs.foo, 'bar')
|
||||
self.eq(app.pargs.foo2, 'bar2')
|
||||
self.eq(app.pargs.foo3, 'bar3')
|
||||
self.eq(app.pargs.foo4, 'bar4')
|
||||
self.eq(app.pargs.foo7, 'bar7')
|
||||
|
||||
def test_collect(self):
|
||||
with self.app as app:
|
||||
args = self.app.controller._collect_arguments()
|
||||
cmds = self.app.controller._collect_commands()
|
||||
args2, cmds2 = self.app.controller._collect()
|
||||
self.eq((args, cmds), (args2, cmds2))
|
||||
|
||||
def test_controller_embedded_on_base(self):
|
||||
self.app._meta.argv = ['cmd2']
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Second.cmd2")
|
||||
|
||||
def test_controller_nested_on_base(self):
|
||||
def test_controller_command_arguments(self):
|
||||
self.app._meta.argv = ['cmd2', '--cmd2-foo=bar2']
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Second.cmd2 : Foo > bar2")
|
||||
|
||||
def test_controller_default_nested_on_base(self):
|
||||
if not ARGPARSE_SUPPORTS_DEFAULTS:
|
||||
raise test.SkipTest(
|
||||
'Argparse does not support default commands in Python < 3.4'
|
||||
)
|
||||
|
||||
self.app._meta.argv = ['third']
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Third.default")
|
||||
|
||||
def test_controller_command_nested_on_base(self):
|
||||
self.app._meta.argv = ['third', 'cmd3']
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Third.cmd3")
|
||||
|
||||
def test_controller_doubled_embedded(self):
|
||||
self.app._meta.argv = ['third', 'cmd4']
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Fourth.cmd4")
|
||||
|
||||
def test_controller_double_nested(self):
|
||||
def test_controller_default_double_nested(self):
|
||||
if not ARGPARSE_SUPPORTS_DEFAULTS:
|
||||
raise test.SkipTest(
|
||||
'Argparse does not support default commands in Python < 3.4'
|
||||
)
|
||||
self.app._meta.argv = ['third', 'fifth']
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Fifth.default")
|
||||
|
||||
self.setUp()
|
||||
|
||||
def test_controller_command_double_nested(self):
|
||||
self.app._meta.argv = ['third', 'fifth', 'cmd5']
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Fifth.cmd5")
|
||||
self.eq(res, "Inside Fifth.cmd5")
|
||||
|
||||
def test_alternative_default(self):
|
||||
if not ARGPARSE_SUPPORTS_DEFAULTS:
|
||||
raise test.SkipTest(
|
||||
'Argparse does not support default commands in Python < 3.4'
|
||||
)
|
||||
|
||||
self.reset_backend()
|
||||
self.app = self.make_app(APP,
|
||||
argv=['alternative_default'],
|
||||
argument_handler=ArgparseArgumentHandler,
|
||||
handlers=[
|
||||
Base,
|
||||
AlternativeDefault,
|
||||
],
|
||||
)
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
self.eq(res,
|
||||
"Inside AlternativeDefault.alternative_default")
|
||||
|
||||
@test.raises(FrameworkError)
|
||||
def test_bad_alternative_default_command(self):
|
||||
if not ARGPARSE_SUPPORTS_DEFAULTS:
|
||||
raise test.SkipTest(
|
||||
'Argparse does not support default commands in Python < 3.4'
|
||||
)
|
||||
|
||||
self.reset_backend()
|
||||
self.app = self.make_app(APP,
|
||||
argv=['bad_alternative_default'],
|
||||
argument_handler=ArgparseArgumentHandler,
|
||||
handlers=[
|
||||
Base,
|
||||
BadAlternativeDefault,
|
||||
],
|
||||
)
|
||||
try:
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
|
||||
except FrameworkError as e:
|
||||
res = re.match("(.*)does not exist(.*)bogus_default(.*)",
|
||||
e.__str__())
|
||||
self.ok(res)
|
||||
raise
|
||||
|
||||
@test.raises(InterfaceError)
|
||||
def test_invalid_stacked_on(self):
|
||||
self.reset_backend()
|
||||
try:
|
||||
self.app = self.make_app(APP,
|
||||
argument_handler=ArgparseArgumentHandler,
|
||||
handlers=[
|
||||
Base,
|
||||
Unstacked,
|
||||
],
|
||||
)
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
except InterfaceError as e:
|
||||
self.ok(re.match("(.*)is not stacked anywhere!(.*)", e.msg))
|
||||
raise
|
||||
|
||||
@test.raises(InterfaceError)
|
||||
def test_invalid_stacked_type(self):
|
||||
self.reset_backend()
|
||||
try:
|
||||
self.app = self.make_app(APP,
|
||||
argument_handler=ArgparseArgumentHandler,
|
||||
handlers=[
|
||||
Base,
|
||||
BadStackType,
|
||||
],
|
||||
)
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
except InterfaceError as e:
|
||||
self.ok(re.match("(.*)has an unknown stacked type(.*)", e.msg))
|
||||
raise
|
||||
|
||||
@test.raises(ArgumentError)
|
||||
def test_duplicate_arguments(self):
|
||||
self.reset_backend()
|
||||
try:
|
||||
self.app = self.make_app(APP,
|
||||
argument_handler=ArgparseArgumentHandler,
|
||||
handlers=[
|
||||
Base,
|
||||
DuplicateArguments,
|
||||
],
|
||||
)
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
except ArgumentError as e:
|
||||
self.ok(re.match("(.*)conflicting option string(.*)",
|
||||
e.__str__()))
|
||||
raise
|
||||
|
||||
@test.raises(ArgumentError)
|
||||
def test_controller_command_duplicate_arguments(self):
|
||||
self.reset_backend()
|
||||
try:
|
||||
self.app = self.make_app(APP,
|
||||
argument_handler=ArgparseArgumentHandler,
|
||||
handlers=[
|
||||
Base,
|
||||
ControllerCommandDuplicateArguments,
|
||||
],
|
||||
)
|
||||
with self.app as app:
|
||||
res = app.run()
|
||||
except ArgumentError as e:
|
||||
self.ok(re.match("(.*)conflicting option string(.*)",
|
||||
e.__str__()))
|
||||
raise
|
||||
|
||||
def test_aliases(self):
|
||||
if sys.version_info[0] < 3:
|
||||
raise test.SkipTest(
|
||||
'Argparse does not support aliases in Python < 3'
|
||||
)
|
||||
self.reset_backend()
|
||||
|
||||
self.app = self.make_app(APP,
|
||||
argument_handler=ArgparseArgumentHandler,
|
||||
handlers=[
|
||||
Base,
|
||||
Aliases,
|
||||
],
|
||||
)
|
||||
with self.app as app:
|
||||
app._meta.argv = ['aliases', 'aliases-cmd1']
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Aliases.aliases_cmd1")
|
||||
|
||||
app._meta.argv = ['aliases', 'aliases-cmd-1']
|
||||
app._setup_arg_handler()
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Aliases.aliases_cmd1")
|
||||
|
||||
app._meta.argv = ['aliases-controller', 'aliases-cmd1']
|
||||
app._setup_arg_handler()
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Aliases.aliases_cmd1")
|
||||
|
||||
app._meta.argv = ['ac', 'ac1']
|
||||
app._setup_arg_handler()
|
||||
res = app.run()
|
||||
self.eq(res, "Inside Aliases.aliases_cmd1")
|
||||
|
||||
@ -53,8 +53,8 @@ class ShellUtilsTestCase(test.CementCoreTestCase):
|
||||
self.eq(p.exitcode, 0)
|
||||
|
||||
def test_spawn_thread(self):
|
||||
t = shell.spawn_thread(time.sleep, args=(10))
|
||||
|
||||
t = shell.spawn_thread(time.sleep, args=(2,))
|
||||
|
||||
# before joining it is alive
|
||||
res = t.is_alive()
|
||||
self.eq(res, True)
|
||||
@ -65,7 +65,7 @@ class ShellUtilsTestCase(test.CementCoreTestCase):
|
||||
res = t.is_alive()
|
||||
self.eq(res, False)
|
||||
|
||||
t = shell.spawn_thread(time.sleep, join=True, args=(10))
|
||||
t = shell.spawn_thread(time.sleep, join=True, args=(2,))
|
||||
res = t.is_alive()
|
||||
self.eq(res, False)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user