Changeset - 8eef1ef7f731
[Not reviewed]
0 0 2
Branko Majic (branko) - 6 years ago 2018-02-27 12:49:11
branko@majic.rs
GC-11: Implemented decorator mechanism for registering subcommands:

- Added decorator that will register the provided functions.
- Added a simple interface function for fetching list of such
registered functions.
2 files changed with 157 insertions and 0 deletions:
0 comments (0 inline, 0 general)
gimmecert/decorators.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8 -*-
 
#
 
# Copyright (C) 2018 Branko Majic
 
#
 
# This file is part of Gimmecert.
 
#
 
# Gimmecert is free software: you can redistribute it and/or modify it
 
# under the terms of the GNU General Public License as published by the Free
 
# Software Foundation, either version 3 of the License, or (at your option) any
 
# later version.
 
#
 
# Gimmecert is distributed in the hope that it will be useful, but
 
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 
# details.
 
#
 
# You should have received a copy of the GNU General Public License along with
 
# Gimmecert.  If not, see <http://www.gnu.org/licenses/>.
 
#
 

	
 

	
 
import inspect
 

	
 

	
 
subcommand_parser_functions = []
 

	
 

	
 
class SetupSubcommandParserInvalidSignature(Exception):
 
    """
 
    Exception thrown if the registred subcommand parser setup
 
    functions has invalid signature.
 
    """
 
    pass
 

	
 

	
 
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:
 

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

	
 
    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:
 

	
 
    def mysubcommand(args):
 
        print("I received args: %s" % args)
 

	
 
    @subcommand_parser
 
    def mysubcommand_parser(parser, subparsers):
 
        subparser = subparsers.add_parser('mysubcommand', description='Does stuff!')
 

	
 
        subparser.set_defaults(func=mysubcommand)
 

	
 
    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.
 
    """
 

	
 
    signature = inspect.signature(func)
 

	
 
    if len(signature.parameters) != 2:
 
        raise SetupSubcommandParserInvalidSignature("Function %s must accept two arguments" % func)
 

	
 
    subcommand_parser_functions.append(func)
 

	
 
    return func
 

	
 

	
 
def get_subcommand_parser_setup_functions():
 
    """
 
    Returns list of registered subcommand parser setup functions.
 

	
 
    :returns: List of registered subcommand parser setup functions.
 
    :rtype: list[callable]
 
    """
 

	
 
    return subcommand_parser_functions
tests/test_decorators.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8 -*-
 
#
 
# Copyright (C) 2018 Branko Majic
 
#
 
# This file is part of Gimmecert.
 
#
 
# Gimmecert is free software: you can redistribute it and/or modify it
 
# under the terms of the GNU General Public License as published by the Free
 
# Software Foundation, either version 3 of the License, or (at your option) any
 
# later version.
 
#
 
# Gimmecert is distributed in the hope that it will be useful, but
 
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 
# details.
 
#
 
# You should have received a copy of the GNU General Public License along with
 
# Gimmecert.  If not, see <http://www.gnu.org/licenses/>.
 
#
 

	
 

	
 
import collections
 

	
 
import gimmecert.decorators
 

	
 
import pytest
 

	
 

	
 
def test_get_subcommand_parser_setup_functions_returns_list():
 

	
 
    registered_functions = gimmecert.decorators.get_subcommand_parser_setup_functions()
 

	
 
    assert isinstance(registered_functions, collections.Iterable)
 

	
 

	
 
def test_subcommand_parser_decorator_registers_function():
 

	
 
    @gimmecert.decorators.subcommand_parser
 
    def myfunction1(parser, subparsers):
 
        pass
 

	
 
    @gimmecert.decorators.subcommand_parser
 
    def myfunction2(parser, subparsers):
 
        pass
 

	
 
    registered_functions = gimmecert.decorators.get_subcommand_parser_setup_functions()
 

	
 
    assert registered_functions == [myfunction1, myfunction2]
 

	
 

	
 
def test_subcommand_parser_decorator_ensures_function_has_correct_signature():
 

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

	
 
        @gimmecert.decorators.subcommand_parser
 
        def invalid_signature_no_arguments():
 
            pass
 

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

	
 
        @gimmecert.decorators.subcommand_parser
 
        def invalid_signature_too_many_arguments(parser, subparsers, extra):
 
            pass
0 comments (0 inline, 0 general)