diff --git a/gimmecert/decorators.py b/gimmecert/decorators.py
new file mode 100644
index 0000000000000000000000000000000000000000..a5832e78ae08f209bb6f037239ecd1bcdecfe32c
--- /dev/null
+++ b/gimmecert/decorators.py
@@ -0,0 +1,94 @@
+# -*- 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 .
+#
+
+
+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
diff --git a/tests/test_decorators.py b/tests/test_decorators.py
new file mode 100644
index 0000000000000000000000000000000000000000..255bc59955d67bf261d57d4a604cb17ae1979013
--- /dev/null
+++ b/tests/test_decorators.py
@@ -0,0 +1,63 @@
+# -*- 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 .
+#
+
+
+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