Changeset - 0590c952df64
[Not reviewed]
gc-11
0 5 0
Branko Majic (branko) - 6 years ago 2018-02-27 12:49:26
branko@majic.rs
GC-11: Added a help subcommand to the tool:

- Updated functional test for checking on the help subcommand as well.
- Implemented the 'help' subcommand.
- Updated required function signature for the subcommand_parser
decorator (functions should return ArgumentParser instance).
- Fixed a typo in inline documentation.
- Updated the test for subcommand_parser decorator to be a more robust
when checking if function registration works.
5 files changed with 91 insertions and 12 deletions:
0 comments (0 inline, 0 general)
functional_tests/test_help.py
Show inline comments
 
@@ -60,23 +60,34 @@ def test_usage_help_shown():
 

	
 

	
 
def test_extended_help_shown():
 
    # John is still not quite sure how the tool works. Therefore he
 
    # decides to try out the -h flag to the command.
 
    process = subprocess.Popen(["gimmecert", "-h"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
    stdout, stderr = process.communicate()
 
    stdout, stderr = stdout.decode(), stderr.decode()
 
    stdout_h_flag, stderr_h_flag = process.communicate()
 
    stdout_h_flag, stderr_h_flag = stdout_h_flag.decode(), stderr_h_flag.decode()
 

	
 
    # In doing so, John is presented with much more extensive
 
    # instructions that provide him with better idea on how to use the
 
    # tool.
 
    assert stderr == ''
 
    assert stderr_h_flag == ''
 
    assert process.returncode == 0
 
    assert "usage: gimmecert [-h]" in stdout
 
    assert "Examples:" in stdout
 
    assert "optional arguments" in stdout
 
    assert "usage: gimmecert [-h]" in stdout_h_flag
 
    assert "Examples:" in stdout_h_flag
 
    assert "optional arguments" in stdout_h_flag
 
    # Subcommands listed.
 
    assert "{help}" in stdout_h_flag
 
    # @TODO: Can't really test this without producing errors, but
 
    # possibly not needed.
 
    # assert "positional arguments" in stdout
 
    # @TODO: Can't test at the moment, should be added once the first
 
    # commands is implemented.
 
    # assert "command1|command2" in stdout
 
    # assert "positional arguments" in stdout_h_flag
 

	
 
    # John also notices the help command in the list. He tries that
 
    # one out as well.
 
    process = subprocess.Popen(["gimmecert", "help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
    stdout_help, stderr_help = process.communicate()
 
    stdout_help, stderr_help = stdout_help.decode(), stderr_help.decode()
 

	
 
    # John is presented with identical results as when running just
 
    # with the -h flag.
 
    assert process.returncode == 0
 
    assert stdout_help == stdout_h_flag
 
    assert stderr_help == stderr_h_flag
gimmecert/cli.py
Show inline comments
 
@@ -18,32 +18,48 @@
 
# Gimmecert.  If not, see <http://www.gnu.org/licenses/>.
 
#
 

	
 

	
 
import argparse
 

	
 
from .decorators import subcommand_parser, get_subcommand_parser_setup_functions
 

	
 

	
 
DESCRIPTION = """\
 
Issues server and client X.509 certificates using a local CA
 
hierarchy.
 

	
 
Examples:
 
"""
 

	
 

	
 
@subcommand_parser
 
def setup_help_subcommand_parser(parser, subparsers):
 
    subparser = subparsers.add_parser('help', description='shows help')
 

	
 
    subparser.set_defaults(func=lambda args: parser.print_help())
 

	
 
    return subparser
 

	
 

	
 
def get_parser():
 
    """
 
    Sets-up and returns a CLI argument parser.
 

	
 
    :returns: argparse.ArgumentParser -- argument parser for CLI.
 
    """
 

	
 
    parser = argparse.ArgumentParser(description=DESCRIPTION)
 

	
 
    parser.set_defaults(func=lambda args: parser.print_usage())
 

	
 
    subparsers = parser.add_subparsers()
 

	
 
    for setup_subcommad_parser in get_subcommand_parser_setup_functions():
 
        setup_subcommad_parser(parser, subparsers)
 

	
 
    return parser
 

	
 

	
 
def main():
 
    """
 
    This function is a CLI entry point for the tool. It is a thin
gimmecert/decorators.py
Show inline comments
 
@@ -38,21 +38,24 @@ def subcommand_parser(func):
 
    Decorator used for registering subcommand parser functions. By
 
    utilising this decorator it is possible to more easily register
 
    new subcommands as they are implemented, and to not depend on
 
    having multiple code paths that deal with instantion and set-up of
 
    subcommand parsers.
 

	
 
    The registerd functions are expected to accept two arguments:
 
    The registered functions are expected to accept two arguments:
 

	
 
    - parser (ArgumentParser), instance of parent parser to which the
 
      subcommand (sub)parser belongs to.
 
    - subparsers, which has been previously obtained through a
 
      parser.get_subparsers() method on parent parser. The function
 
      should instantiate a subparser through it by calling the
 
      standard subparsers.add_parser() method.
 

	
 
    The registered functions are expeceted to return the subparser
 
    itself.
 

	
 
    It is expected that each subcomand parser will set a default
 
    function to be invoked with parsed arguments by doing a call to
 
    set_defaults(func=subcommand_function).
 

	
 
    Example usage:
 

	
 
@@ -62,12 +65,14 @@ def subcommand_parser(func):
 
    @subcommand_parser
 
    def mysubcommand_parser(parser, subparsers):
 
        subparser = subparsers.add_parser('mysubcommand', description='Does stuff!')
 

	
 
        subparser.set_defaults(func=mysubcommand)
 

	
 
        return subparser
 

	
 
    Later on the registered setup functions should be retrieved
 
    through get_subcommand_parser_setup_functions() function.
 

	
 
    :param func: Function (or callable) that should be registered for setting-up a subparser.
 
    :type func: callable
 
    :returns: func -- unchanged decorated function.
tests/test_cli.py
Show inline comments
 
@@ -19,12 +19,13 @@
 
#
 

	
 

	
 
import argparse
 

	
 
import gimmecert.cli
 
import gimmecert.decorators
 

	
 
from unittest import mock
 

	
 

	
 
def test_get_parser_returns_parser():
 
    parser = gimmecert.cli.get_parser()
 
@@ -85,6 +86,51 @@ def test_main_invokes_parser_function(mock_get_parser):
 

	
 

	
 
def test_parser_help_contains_examples():
 
    parser = gimmecert.cli.get_parser()
 

	
 
    assert 'Examples' in parser.description
 

	
 

	
 
def test_setup_help_subcommand_parser_registered():
 
    registered_functions = gimmecert.decorators.get_subcommand_parser_setup_functions()
 

	
 
    assert gimmecert.cli.setup_help_subcommand_parser in registered_functions
 

	
 

	
 
@mock.patch('gimmecert.cli.get_subcommand_parser_setup_functions')
 
def test_get_parser_calls_setup_subcommand_parser_functions(mock_get_subcommand_parser_setup_functions):
 
    mock_setup1 = mock.Mock()
 
    mock_setup2 = mock.Mock()
 
    mock_get_subcommand_parser_setup_functions.return_value = [mock_setup1, mock_setup2]
 

	
 
    gimmecert.cli.get_parser()
 

	
 
    assert mock_setup1.called
 
    assert mock_setup2.called
 

	
 

	
 
def test_setup_help_subcommand_parser_adds_parser():
 
    mock_parser = mock.Mock()
 
    mock_subparsers = mock.Mock()
 

	
 
    gimmecert.cli.setup_help_subcommand_parser(mock_parser, mock_subparsers)
 

	
 
    assert mock_subparsers.add_parser.called
 

	
 

	
 
def test_help_subcommand_returns_parser():
 
    parser = argparse.ArgumentParser()
 
    subparsers = parser.add_subparsers()
 

	
 
    subparser = gimmecert.cli.setup_help_subcommand_parser(parser, subparsers)
 

	
 
    assert isinstance(subparser, argparse.ArgumentParser)
 

	
 

	
 
def test_help_subcommand_sets_function_callback():
 
    parser = argparse.ArgumentParser()
 
    subparsers = parser.add_subparsers()
 

	
 
    subparser = gimmecert.cli.setup_help_subcommand_parser(parser, subparsers)
 

	
 
    assert callable(subparser.get_default('func'))
tests/test_decorators.py
Show inline comments
 
@@ -42,13 +42,14 @@ def test_subcommand_parser_decorator_registers_function():
 
    @gimmecert.decorators.subcommand_parser
 
    def myfunction2(parser, subparsers):
 
        pass
 

	
 
    registered_functions = gimmecert.decorators.get_subcommand_parser_setup_functions()
 

	
 
    assert registered_functions == [myfunction1, myfunction2]
 
    assert myfunction1 in registered_functions
 
    assert myfunction2 in registered_functions
 

	
 

	
 
def test_subcommand_parser_decorator_ensures_function_has_correct_signature():
 

	
 
    with pytest.raises(gimmecert.decorators.SetupSubcommandParserInvalidSignature):
 

	
0 comments (0 inline, 0 general)