=== modified file 'lava/tool/command.py'
@@ -112,7 +112,7 @@
pass
-class SubCommand(Command):
+class CommandGroup(Command):
"""
Base class for all command sub-command hubs.
@@ -120,7 +120,7 @@
options that can be freely extended, just like the top-level lava-tool
command.
- For example, a SubCommand 'actions' will load additional commands from a
+ For example, a CommandGroup 'actions' will load additional commands from a
the 'lava.actions' namespace. For the end user it will be available as::
$ lava-tool foo actions xxx
@@ -161,3 +161,6 @@
if namespace is not None:
dispatcher.import_commands(namespace)
parser.set_defaults(dispatcher=dispatcher)
+
+
+SubCommand = CommandGroup
=== modified file 'lava/tool/dispatcher.py'
@@ -54,10 +54,13 @@
dispatcher instance.
"""
parser_args = dict(add_help=True)
+ # Set description based on class description
if cls.description is not None:
parser_args['description'] = cls.description
+ # Set the epilog based on class epilog
if cls.epilog is not None:
parser_args['epilog'] = cls.epilog
+ # Return the fresh parser
return argparse.ArgumentParser(**parser_args)
def import_commands(self, entrypoint_name):
@@ -86,13 +89,15 @@
command_cls.get_name(),
help=command_cls.get_help(),
epilog=command_cls.get_epilog())
- from lava.tool.command import SubCommand
- if issubclass(command_cls, SubCommand):
- # Handle SubCommand somewhat different. Instead of calling
+ from lava.tool.command import CommandGroup
+ if issubclass(command_cls, CommandGroup):
+ # Handle CommandGroup somewhat different. Instead of calling
# register_arguments we call register_subcommands
command_cls.register_subcommands(sub_parser)
+ # Let's also call register arguments in case we need both
+ command_cls.register_arguments(sub_parser)
else:
- # Handle plain commands easily by recording their commands in the
+ # Handle plain commands by recording their commands in the
# dedicated sub-parser we've crated for them.
command_cls.register_arguments(sub_parser)
# In addition, since we don't want to require all sub-classes of
@@ -105,6 +110,11 @@
# Make sure the sub-parser knows about this dispatcher
sub_parser.set_defaults(dispatcher=self)
+ def _adjust_logging_level(self, args):
+ """
+ Adjust logging level after seeing the initial arguments
+ """
+
def dispatch(self, raw_args=None):
"""
Dispatch a command with the specified arguments.
@@ -113,6 +123,8 @@
"""
# First parse whatever input arguments we've got
args = self.parser.parse_args(raw_args)
+ # Adjust logging level after seeing arguments
+ self._adjust_logging_level(args)
# Then look up the command class and construct it with the parser it
# belongs to and the parsed arguments.
command = args.command_cls(args.parser, args)
=== modified file 'lava/tool/main.py'
@@ -23,6 +23,9 @@
Implementation of the `lava` shell command.
"""
+import logging
+import sys
+
from lava.tool.dispatcher import Dispatcher
@@ -37,5 +40,93 @@
"""
def __init__(self):
+ # Call this early so that we don't get logging.basicConfig
+ # being called by accident. Otherwise we'd have to
+ # purge all loggers from the root logger and that sucks
+ self.setup_logging()
+ # Initialize the base dispatcher
super(LavaDispatcher, self).__init__()
+ # And import the non-flat namespace commands
self.import_commands('lava.commands')
+
+ @classmethod
+ def construct_parser(cls):
+ """
+ Construct a parser for this dispatcher.
+
+ This is only used if the parser is not provided by the parent
+ dispatcher instance.
+ """
+ # Construct a basic parser
+ parser = super(LavaDispatcher, cls).construct_parser()
+ # Add the --verbose flag
+ parser.add_argument(
+ "-v", "--verbose",
+ default=False,
+ action="store_true",
+ help="Be more verbose (displays more messages globally)")
+ # Add the --debug flag
+ parser.add_argument(
+ "-D", "--debug",
+ action="store_true",
+ default=False,
+ help="Enable debugging on all loggers")
+ # Add the --trace flag
+ parser.add_argument(
+ "-T", "--trace",
+ action="append",
+ default=[],
+ help="Enable debugging of the specified logger, can be specified multiple times")
+ # Return the improved parser
+ return parser
+
+ def setup_logging(self):
+ """
+ Setup logging for the root dispatcher
+ """
+ # Enable warning/error message handler
+ class OnlyProblemsFilter(logging.Filterer):
+ def filter(self, record):
+ if record.levelno >= logging.WARN:
+ return 1
+ return 0
+ err_handler = logging.StreamHandler(sys.stderr)
+ err_handler.setLevel(logging.WARN)
+ err_handler.setFormatter(
+ logging.Formatter("%(levelname)s: %(message)s"))
+ err_handler.addFilter(OnlyProblemsFilter())
+ logging.getLogger().addHandler(err_handler)
+ # Enable the debug handler
+ class DebugFilter(logging.Filter):
+ def filter(self, record):
+ if record.levelno == logging.DEBUG:
+ return 1
+ return 0
+ dbg_handler = logging.StreamHandler(sys.stderr)
+ dbg_handler.setLevel(logging.DEBUG)
+ dbg_handler.setFormatter(
+ logging.Formatter("%(levelname)s %(name)s: %(message)s"))
+ dbg_handler.addFilter(DebugFilter())
+ logging.getLogger().addHandler(dbg_handler)
+
+ def _adjust_logging_level(self, args):
+ # Enable verbose message handler
+ if args.verbose:
+ logging.getLogger().setLevel(logging.INFO)
+ class OnlyInfoFilter(logging.Filterer):
+ def filter(self, record):
+ if record.levelno == logging.INFO:
+ return 1
+ return 0
+ msg_handler = logging.StreamHandler(sys.stdout)
+ msg_handler.setLevel(logging.INFO)
+ msg_handler.setFormatter(
+ logging.Formatter("%(message)s"))
+ msg_handler.addFilter(OnlyInfoFilter())
+ logging.getLogger().addHandler(msg_handler)
+ # Enable debugging
+ if args.debug:
+ logging.getLogger().setLevel(logging.DEBUG)
+ # Enable trace loggers
+ for name in args.trace:
+ logging.getLogger(name).setLevel(logging.DEBUG)
=== modified file 'lava_tool/interface.py'
@@ -21,4 +21,4 @@
"""
from lava.tool.errors import CommandError as LavaCommandError
-from lava.tool.command import Command, SubCommand
+from lava.tool.command import Command, CommandGroup as SubCommand