From patchwork Thu Mar 22 18:31:10 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zygmunt Krynicki X-Patchwork-Id: 7420 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 0AB6423E12 for ; Thu, 22 Mar 2012 18:31:26 +0000 (UTC) Received: from mail-iy0-f180.google.com (mail-iy0-f180.google.com [209.85.210.180]) by fiordland.canonical.com (Postfix) with ESMTP id 9127BA18040 for ; Thu, 22 Mar 2012 18:31:25 +0000 (UTC) Received: by mail-iy0-f180.google.com with SMTP id e36so4557986iag.11 for ; Thu, 22 Mar 2012 11:31:25 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to:received-spf :content-type:mime-version:x-launchpad-project:x-launchpad-branch :x-launchpad-message-rationale:x-launchpad-branch-revision-number :x-launchpad-notification-type:to:from:subject:message-id:date :reply-to:sender:errors-to:precedence:x-generated-by :x-launchpad-hash:x-gm-message-state; bh=Kf1AspJppH/HxXco+mt2239Hnk/wHRdVzW0JrezzQfc=; b=oft83pGJxcKKkIJ1ot+UVC2Mie1Z6BLManK1G+kJjZ7MBsM0QMLiVQU1e+Qr1XFirz 6Z/on4CYzU+UYNm88R0My3Rk6T4HtbCavrPP8tOKEaaLSwioEbn3AtS5jYfXgayisiRD u8yamTFuf75nIrt+k3bNdCa5esEZYdfUkFVyZIuGSNm4R/89hXyEV8r6dyTtTaBo+xfd Ruwov7SIvukLS3ygSszi2wmmgaNsvr5rzw3uAQTYwk+rXGnqCQJogI8Xgl1TwGttfTKU tTmPhD4EMsutlsBKaYLrojjlTfnpJN+4r4ipl3WQ3Zuc0D1uVNYgyU/A1IiwVCixioXZ eKjQ== Received: by 10.42.72.130 with SMTP id o2mr6538072icj.8.1332441085345; Thu, 22 Mar 2012 11:31:25 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.231.203.79 with SMTP id fh15csp3345ibb; Thu, 22 Mar 2012 11:31:24 -0700 (PDT) Received: by 10.180.101.72 with SMTP id fe8mr7653610wib.4.1332441083528; Thu, 22 Mar 2012 11:31:23 -0700 (PDT) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id u4si3223421wid.6.2012.03.22.11.31.23 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 22 Mar 2012 11:31:23 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) client-ip=91.189.90.7; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) smtp.mail=bounces@canonical.com Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1SAmnC-0001GU-VD for ; Thu, 22 Mar 2012 18:31:22 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id E1F51E0118 for ; Thu, 22 Mar 2012 18:31:10 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: lava-tool X-Launchpad-Branch: ~linaro-validation/lava-tool/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 173 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~linaro-validation/lava-tool/trunk] Rev 173: Improve global logging support Message-Id: <20120322183110.21452.52279.launchpad@ackee.canonical.com> Date: Thu, 22 Mar 2012 18:31:10 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="14981"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: d32bbff2b97acd9d6840c289f9d1b33abd393577 X-Gm-Message-State: ALoCoQmdnD/0nJPzPOYMGHCX8wpn+IQq/hzztoeDWc8UuRpWTYUNALtcZipUaWtP8rYTfwS3R1G+ Merge authors: Zygmunt Krynicki (zkrynicki) Related merge proposals: https://code.launchpad.net/~zkrynicki/lava-tool/next/+merge/85586 proposed by: Zygmunt Krynicki (zkrynicki) ------------------------------------------------------------ revno: 173 [merge] committer: Zygmunt Krynicki branch nick: release timestamp: Thu 2012-03-22 19:03:03 +0100 message: Improve global logging support modified: lava/tool/command.py lava/tool/dispatcher.py lava/tool/main.py lava_tool/interface.py --- lp:lava-tool https://code.launchpad.net/~linaro-validation/lava-tool/trunk You are subscribed to branch lp:lava-tool. To unsubscribe from this branch go to https://code.launchpad.net/~linaro-validation/lava-tool/trunk/+edit-subscription === modified file 'lava/tool/command.py' --- lava/tool/command.py 2012-03-19 16:07:55 +0000 +++ lava/tool/command.py 2012-03-22 18:03:03 +0000 @@ -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' --- lava/tool/dispatcher.py 2012-03-19 16:08:08 +0000 +++ lava/tool/dispatcher.py 2012-03-22 18:03:03 +0000 @@ -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' --- lava/tool/main.py 2012-03-13 18:22:16 +0000 +++ lava/tool/main.py 2012-03-22 18:03:03 +0000 @@ -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' --- lava_tool/interface.py 2012-03-13 16:30:24 +0000 +++ lava_tool/interface.py 2012-03-22 18:03:03 +0000 @@ -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