From 65d197906beff2948caea96a33a2166c29c140c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Armando=20Garc=C3=ADa=20Sancio?= Date: Sun, 22 Feb 2015 02:45:00 +0000 Subject: [PATCH] Refactor command execution --- dcos/api/cmds.py | 49 +++++++++++++++++++++ dcos/cli/app/main.py | 72 ++++++++++++++++++++----------- pydoc/modules.rst | 9 ++++ tests/test_cmds.py | 100 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 24 deletions(-) create mode 100644 dcos/api/cmds.py create mode 100644 tests/test_cmds.py diff --git a/dcos/api/cmds.py b/dcos/api/cmds.py new file mode 100644 index 0000000..2977de0 --- /dev/null +++ b/dcos/api/cmds.py @@ -0,0 +1,49 @@ +import collections + +from dcos.api import errors + +Command = collections.namedtuple( + 'Command', + ['hierarchy', 'arg_keys', 'function']) +"""Describe a CLI command. + +:param hierarchy: the noun and verbs that need to be set for the command to + execute +:type hierarchy: list of str +:param arg_keys: the arguments that must get passed to the function; the order + of the keys determines the order in which they get passed to + the function +:type arg_keys: list of str +:param function: the function to execute +:type function: func(args) -> int +""" + + +def execute(cmds, args): + """Executes one of the commands based on the arguments passed. + + :param cmds: commands to try to execute; the order determines the order of + evaluation + :type cmds: list of Command + :param args: command line arguments + :type args: dict + :returns: the process status + :rtype: (int, dcos.errors.Error) + """ + + for hierarchy, arg_keys, function in cmds: + # Let's find if the function matches the command + match = True + for positional in hierarchy: + if not args[positional]: + match = False + + if match: + params = [args[name] for name in arg_keys] + return (function(*params), None) + + return ( + None, + errors.DefaultError( + 'Could not find a command with the passed arguments') + ) diff --git a/dcos/cli/app/main.py b/dcos/cli/app/main.py index 1952e9d..174094d 100644 --- a/dcos/cli/app/main.py +++ b/dcos/cli/app/main.py @@ -41,8 +41,8 @@ import sys import docopt import pkg_resources -from dcos.api import (config, constants, emitting, errors, jsonitem, marathon, - options, util) +from dcos.api import (cmds, config, constants, emitting, errors, jsonitem, + marathon, options, util) logger = util.get_logger(__name__) emitter = emitting.FlatEmitter() @@ -62,39 +62,63 @@ def main(): emitter.publish(options.make_generic_usage_error(__doc__)) return 1 - if args['info']: - return _info() + returncode, err = cmds.execute(_cmds(), args) + if err is not None: + emitter.publish(err) + emitter.publish(options.make_generic_usage_error(__doc__)) + return 1 - if args['add']: - return _add() + return returncode - if args['version'] and args['list']: - return _version_list(args[''], args['--max-count']) - if args['list']: - return _list() +def _cmds(): + """ + :returns: all the supported commands + :rtype: dcos.api.cmds.Command + """ - if args['remove']: - return _remove(args[''], args['--force']) + return [ + cmds.Command(hierarchy=['info'], arg_keys=[], function=_info), - if args['show']: - return _show(args[''], args['--app-version']) + cmds.Command(hierarchy=['add'], arg_keys=[], function=_add), - if args['start']: - return _start(args[''], args[''], args['--force']) + cmds.Command( + hierarchy=['version', 'list'], + arg_keys=['', '--max-count'], + function=_version_list), - if args['stop']: - return _stop(args[''], args['--force']) + cmds.Command(hierarchy=['list'], arg_keys=[], function=_list), - if args['update']: - return _update(args[''], args[''], args['--force']) + cmds.Command( + hierarchy=['remove'], + arg_keys=['', '--force'], + function=_remove), - if args['restart']: - return _restart(args[''], args['--force']) + cmds.Command( + hierarchy=['show'], + arg_keys=['', '--app-version'], + function=_show), - emitter.publish(options.make_generic_usage_error(__doc__)) + cmds.Command( + hierarchy=['start'], + arg_keys=['', '', '--force'], + function=_start), - return 1 + cmds.Command( + hierarchy=['stop'], + arg_keys=['', '--force'], + function=_stop), + + cmds.Command( + hierarchy=['update'], + arg_keys=['', '', '--force'], + function=_update), + + cmds.Command( + hierarchy=['restart'], + arg_keys=['', '--force'], + function=_restart), + ] def _info(): diff --git a/pydoc/modules.rst b/pydoc/modules.rst index 15dadf0..6b366b8 100644 --- a/pydoc/modules.rst +++ b/pydoc/modules.rst @@ -1,6 +1,15 @@ API Documentation ================= +The :mod:`dcos.api.cmds` Module +--------------------------------- + +.. automodule:: dcos.api.cmds + :members: + :undoc-members: + :show-inheritance: + :inherited-members: + The :mod:`dcos.api.config` Module --------------------------------- diff --git a/tests/test_cmds.py b/tests/test_cmds.py new file mode 100644 index 0000000..a1218a7 --- /dev/null +++ b/tests/test_cmds.py @@ -0,0 +1,100 @@ +import pytest +from dcos.api import cmds, errors + + +@pytest.fixture +def args(): + return { + 'cmd-a': True, + 'cmd-b': True, + 'cmd-c': False, + 'arg-1': 'arg-1', + 'arg-2': 'arg-2', + 'arg-0': 'arg-0', + } + + +def test_single_cmd(args): + commands = [ + cmds.Command( + hierarchy=['cmd-a', 'cmd-b'], + arg_keys=['arg-0', 'arg-1', 'arg-2'], + function=function), + ] + + assert cmds.execute(commands, args) == (1, None) + + +def test_multiple_cmd(args): + commands = [ + cmds.Command( + hierarchy=['cmd-c'], + arg_keys=['arg-0', 'arg-1', 'arg-2'], + function=pytest.fail), + cmds.Command( + hierarchy=['cmd-a', 'cmd-b'], + arg_keys=['arg-0', 'arg-1', 'arg-2'], + function=function), + ] + + assert cmds.execute(commands, args) == (1, None) + + +def test_no_matching_cmd(args): + commands = [ + cmds.Command( + hierarchy=['cmd-c'], + arg_keys=['arg-0', 'arg-1', 'arg-2'], + function=pytest.fail), + ] + + returncode, err = cmds.execute(commands, args) + + assert returncode is None + assert isinstance(err, errors.Error) + + +def test_similar_cmds(args): + commands = [ + cmds.Command( + hierarchy=['cmd-a', 'cmd-b'], + arg_keys=['arg-0', 'arg-1', 'arg-2'], + function=function), + cmds.Command( + hierarchy=['cmd-a'], + arg_keys=['arg-0', 'arg-1', 'arg-2'], + function=pytest.fail), + ] + + assert cmds.execute(commands, args) == (1, None) + + +def test_missing_cmd(args): + commands = [ + cmds.Command( + hierarchy=['cmd-d'], + arg_keys=['arg-0', 'arg-1', 'arg-2'], + function=pytest.fail), + ] + + with pytest.raises(KeyError): + returncode, err = cmds.execute(commands, args) + + +def test_missing_arg(args): + commands = [ + cmds.Command( + hierarchy=['cmd-a'], + arg_keys=['arg-3'], + function=pytest.fail), + ] + + with pytest.raises(KeyError): + returncode, err = cmds.execute(commands, args) + + +def function(*args): + for i in range(len(args)): + assert args[i] == 'arg-{}'.format(i) + + return 1