From 3195e3e213084f086dec5d81447e0db16d622337 Mon Sep 17 00:00:00 2001 From: Elena ``of Valhalla'' Grandi Date: Tue, 22 Feb 2022 17:03:55 +0100 Subject: Main Command --- hazwaz/__init__.py | 2 +- hazwaz/command.py | 65 +++++++++++++++++++++++++++++++++++++++++++++ tests/test_command.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 137 insertions(+), 3 deletions(-) diff --git a/hazwaz/__init__.py b/hazwaz/__init__.py index 8b85231..f5f0a6d 100644 --- a/hazwaz/__init__.py +++ b/hazwaz/__init__.py @@ -1 +1 @@ -from .command import * +from .command import MainCommand # noqa: F401 diff --git a/hazwaz/command.py b/hazwaz/command.py index eea436a..c99e012 100644 --- a/hazwaz/command.py +++ b/hazwaz/command.py @@ -1,3 +1,68 @@ +import argparse import logging logger = logging.getLogger(__name__) + + +def _get_first_docstring_line(obj): + try: + return obj.__doc__.split('\n')[1].strip() + except (AttributeError, IndexError): + return None + +def _get_remaining_docstring_lines(obj): + try: + return "\n".join(obj.__doc__.split('\n')[2:]).strip() + except (AttributeError, IndexError): + return None + + +class MainCommand: + commands = () + + def __init__(self): + desc = _get_first_docstring_line(self) + epilog = _get_remaining_docstring_lines(self) + self.parser = argparse.ArgumentParser( + description=desc, + epilog=epilog, + ) + self.add_arguments(self.parser) + self.parser.set_defaults(func=self._main) + self.subparsers = self.parser.add_subparsers() + for name, sub in self.commands: + sub_help = _get_first_docstring_line(sub) + sub_parser = self.subparsers.add_parser( + name, + help=sub_help, + description=sub.__doc__, + ) + for arg in sub.arguments: + sub_parser.add_argument(*arg[0], **arg[1]) + sub_parser.set_defaults(func=sub._main) + + def _main(self, args): + self.parser.print_help() + + def add_arguments(self, parser): + group = parser.add_mutually_exclusive_group() + group.add_argument( + '--verbose', '-v', + action='store_true', + help="Show more details", + ) + group.add_argument( + '--debug', + action='store_true', + help="Show debug messages", + ) + + def main(self): + self.args = self.parser.parse_args() + + if self.args.verbose: + logger.setLevel(logging.VERBOSE) + if self.args.debug: + logger.setLevel(logging.DEBUG) + + self.args.func(self.args) diff --git a/tests/test_command.py b/tests/test_command.py index f5b824c..eed6e1f 100644 --- a/tests/test_command.py +++ b/tests/test_command.py @@ -1,10 +1,79 @@ +import contextlib +import io +import sys import unittest -from hazwaz import command +import hazwaz + + +class MyCommand(hazwaz.MainCommand): + """ + A command that does things. + + This is a command, but honestly it doesn't really do anything. + """ + commands = () + + def add_arguments(self, parser): + super().add_arguments(parser) + parser.add_argument( + "--foo", + help="foobar things" + ) class testCommand(unittest.TestCase): - pass + def _run_with_argv(self, cmd, argv): + stream = { + 'stdout': io.StringIO(), + 'stderr': io.StringIO(), + } + old_argv = sys.argv + sys.argv = argv + with contextlib.redirect_stdout(stream['stdout']): + with contextlib.redirect_stderr(stream['stderr']): + cmd.main() + sys.argv = old_argv + return stream + + def test_description(self): + cmd = MyCommand() + self.assertEqual( + cmd.parser.description, + "A command that does things." + ) + self.assertEqual( + cmd.parser.epilog, + "This is a command, but honestly it doesn't really do anything." + ) + + def test_description_none(self): + class NoHelpCommand(hazwaz.MainCommand): + pass + + cmd = NoHelpCommand() + self.assertEqual(cmd.parser.description, None) + + def test_description_empty(self): + class NoHelpCommand(hazwaz.MainCommand): + """ + """ + + cmd = NoHelpCommand() + self.assertEqual(cmd.parser.description, '') + + def test_arguments(self): + cmd = MyCommand() + cmd_help = cmd.parser.format_help() + self.assertIn("--verbose", cmd_help) + self.assertIn("--foo", cmd_help) + + def test_run(self): + cmd = MyCommand() + cmd_help = cmd.parser.format_help() + stream = self._run_with_argv(cmd, ["mycommand"]) + self.assertEqual(stream["stdout"].getvalue(), cmd_help) + if __name__ == '__main__': -- cgit v1.2.3