aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElena ``of Valhalla'' Grandi <valhalla@trueelena.org>2022-02-22 17:03:55 +0100
committerElena ``of Valhalla'' Grandi <valhalla@trueelena.org>2022-02-22 17:03:55 +0100
commit3195e3e213084f086dec5d81447e0db16d622337 (patch)
tree7ba1f2bd5dc0206a30084d16fe998fdede701b1e
parent0582101ca0e07f5f9b62e2cc0be405f8e4bc2d7c (diff)
Main Command
-rw-r--r--hazwaz/__init__.py2
-rw-r--r--hazwaz/command.py65
-rw-r--r--tests/test_command.py73
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__':