diff mbox

[Branch,~linaro-validation/lava-tool/trunk] Rev 170: Merge less-lava-tool branch

Message ID 20120322172813.4834.52293.launchpad@ackee.canonical.com
State Accepted
Headers show

Commit Message

Zygmunt Krynicki March 22, 2012, 5:28 p.m. UTC
Merge authors:
  Zygmunt Krynicki (zkrynicki)
Related merge proposals:
  https://code.launchpad.net/~zkrynicki/lava-tool/less-lava-tool/+merge/97287
  proposed by: Zygmunt Krynicki (zkrynicki)
  review: Approve - Michael Hudson-Doyle (mwhudson)
------------------------------------------------------------
revno: 170 [merge]
committer: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
branch nick: release
timestamp: Thu 2012-03-22 18:25:31 +0100
message:
  Merge less-lava-tool branch
  
  This branch transitions part of the code in lava-tool to lava.tool while
  maintaining compatibility with anyone depending on the old interface.
removed:
  lava_tool/commands/misc.py
added:
  lava/
  lava/__init__.py
  lava/tool/
  lava/tool/__init__.py
  lava/tool/command.py
  lava/tool/commands/
  lava/tool/commands/__init__.py
  lava/tool/commands/help.py
  lava/tool/dispatcher.py
  lava/tool/errors.py
  lava/tool/main.py
modified:
  lava_tool/__init__.py
  lava_tool/commands/__init__.py
  lava_tool/dispatcher.py
  lava_tool/interface.py
  setup.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
diff mbox

Patch

=== added directory 'lava'
=== added file 'lava/__init__.py'
--- lava/__init__.py	1970-01-01 00:00:00 +0000
+++ lava/__init__.py	2012-03-13 15:34:36 +0000
@@ -0,0 +1,3 @@ 
+__import__('pkg_resources').declare_namespace(__name__)
+# DO NOT ADD ANYTHING TO THIS FILE!
+# IT MUST STAY AS IS (empty apart from the two lines above)

=== added directory 'lava/tool'
=== added file 'lava/tool/__init__.py'
--- lava/tool/__init__.py	1970-01-01 00:00:00 +0000
+++ lava/tool/__init__.py	2012-03-13 15:40:30 +0000
@@ -0,0 +1,27 @@ 
+# Copyright (C) 2010, 2011 Linaro Limited
+#
+# Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
+# Author: Michael Hudson-Doyle <michael.hudson@linaro.org>
+#
+# This file is part of lava-tool.
+#
+# lava-tool is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# as published by the Free Software Foundation
+#
+# lava-tool 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 Lesser General Public License
+# along with lava-tool.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+lava.tool
+=========
+
+Generic code for command line utilities for LAVA
+"""
+
+__version__ = (0, 4, 0, "dev", 0)

=== added file 'lava/tool/command.py'
--- lava/tool/command.py	1970-01-01 00:00:00 +0000
+++ lava/tool/command.py	2012-03-13 16:30:24 +0000
@@ -0,0 +1,157 @@ 
+# Copyright (C) 2010, 2011 Linaro Limited
+#
+# Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
+#
+# This file is part of lava-tool.
+#
+# lava-tool is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# as published by the Free Software Foundation
+#
+# lava-tool 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 Lesser General Public License
+# along with lava-tool.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Interface for all lava-tool commands
+"""
+
+import inspect
+
+
+class Command(object):
+    """
+    Base class for all command line tool sub-commands.
+    """
+
+    def __init__(self, parser, args):
+        """
+        Prepare instance for executing commands.
+
+        This method is called immediately after all arguments are parsed and
+        results are available. This gives subclasses a chance to configure
+        themselves. The provided parser is an instance of
+        argparse.ArgumentParser but it may not be the top-level parser (it will
+        be a parser specific for this command)
+
+        The default implementation stores both arguments as instance
+        attributes.
+        """
+        self.parser = parser
+        self.args = args
+
+    def invoke(self):
+        """
+        Invoke command action.
+        """
+        raise NotImplementedError()
+
+    def reparse_arguments(self, parser, raw_args):
+        """
+        Re-parse raw arguments into normal arguments
+
+        Parser is the same as in register_arguments (a sub-parser) The true,
+        topmost parser is in self.parser.
+
+        This method is only needed for specific commands that need to peek at
+        the arguments before being able to truly redefine the parser and
+        re-parse the raw arguments again.
+        """
+        raise NotImplementedError()
+
+    @classmethod
+    def get_name(cls):
+        """
+        Return the name of this command.
+
+        The default implementation strips any leading underscores and replaces
+        all other underscores with dashes.
+        """
+        return cls.__name__.lstrip("_").replace("_", "-")
+
+    @classmethod
+    def get_help(cls):
+        """
+        Return the help message of this command
+        """
+        doc = inspect.getdoc(cls)
+        if doc is not None and "" in doc:
+            doc = doc[:doc.index("")].rstrip()
+        return doc
+
+    @classmethod
+    def get_epilog(cls):
+        """
+        Return the epilog of the help message
+        """
+        doc = inspect.getdoc(cls)
+        if doc is not None and "" in doc:
+            doc = doc[doc.index("") + 1:].lstrip()
+        else:
+            doc = None
+        return doc
+
+    @classmethod
+    def register_arguments(cls, parser):
+        """
+        Register arguments if required.
+
+        Subclasses can override this to add any arguments that will be
+        exposed to the command line interface.
+        """
+        pass
+
+
+class SubCommand(Command):
+    """
+    Base class for all command sub-command hubs.
+
+    This class is needed when one wants to get a custom level of command
+    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
+    the 'lava.actions' namespace. For the end user it will be available as::
+
+        $ lava-tool foo actions xxx
+
+    Where xxx is one of the Commands that is declared to live in the namespace
+    provided by 'foo actions'.
+    """
+
+    namespace = None
+
+    @classmethod
+    def get_namespace(cls):
+        """
+        Return the pkg-resources entry point namespace name from which
+        sub-commands will be loaded.
+        """
+        return cls.namespace
+
+    @classmethod
+    def register_subcommands(cls, parser):
+        """
+        Register sub commands.
+
+        This method is called around the same time as register_arguments()
+        would be called for the plain command classes. It loads commands from
+        the entry point namespace returned by get_namespace() and registeres
+        them with a BaseDispatcher class. The parsers used by that dispatcher
+        are linked to the calling dispatcher parser so the new commands enrich
+        the top-level parser tree.
+
+        In addition, the provided parser stores a dispatcher instance in its
+        defaults. This is useful when one wants to access it later. To a final
+        command instance it shall be available as self.args.dispatcher.
+        """
+        from lava.tool.dispatcher import BaseDispatcher
+        dispatcher = BaseDispatcher(parser, name=cls.get_name())
+        namespace = cls.get_namespace()
+        if namespace is not None:
+            dispatcher.import_commands(namespace)
+        parser.set_defaults(dispatcher=dispatcher)

=== added directory 'lava/tool/commands'
=== added file 'lava/tool/commands/__init__.py'
--- lava/tool/commands/__init__.py	1970-01-01 00:00:00 +0000
+++ lava/tool/commands/__init__.py	2012-03-13 18:17:45 +0000
@@ -0,0 +1,83 @@ 
+# Copyright (C) 2010 Linaro Limited
+#
+# Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
+#
+# This file is part of lava-tool.
+#
+# lava-tool is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# as published by the Free Software Foundation
+#
+# lava-tool 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 Lesser General Public License
+# along with lava-tool.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Package with command line commands
+"""
+
+import argparse
+import re
+
+
+class ExperimentalNoticeAction(argparse.Action):
+    """
+    Argparse action that implements the --experimental-notice
+    """
+
+    message = """
+    Some lc-tool sub-commands are marked as EXPERIMENTAL. Those commands are
+    not guaranteed to work identically, or have identical interface between
+    subsequent lc-tool releases.
+
+    We do that to make it possible to provide good user interface and
+    server-side API when working on new features. Once a feature is stabilized
+    the UI will be frozen and all subsequent changes will retain backwards
+    compatibility.
+    """
+    message = message.lstrip()
+    message = re.sub(re.compile("[ \t]+", re.M), " ", message)
+    message = re.sub(re.compile("^ ", re.M), "", message)
+
+    def __init__(self,
+                 option_strings, dest, default=None, required=False,
+                 help=None):
+        super(ExperimentalNoticeAction, self).__init__(
+            option_strings=option_strings, dest=dest, default=default, nargs=0,
+            help=help)
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        parser.exit(message=self.message)
+
+
+class ExperimentalCommandMixIn(object):
+    """
+    Experimental command.
+
+    Prints a warning message on each call to invoke()
+    """
+
+    def invoke(self):
+        self.print_experimental_notice()
+        return super(ExperimentalCommandMixIn, self).invoke()
+
+    @classmethod
+    def register_arguments(cls, parser):
+        retval = super(ExperimentalCommandMixIn,
+                       cls).register_arguments(parser)
+        parser.register("action", "experimental_notice",
+                        ExperimentalNoticeAction)
+        group = parser.add_argument_group("experimental commands")
+        group.add_argument("--experimental-notice",
+                            action="experimental_notice",
+                            default=argparse.SUPPRESS,
+                            help="Explain the nature of experimental commands")
+        return retval
+
+    def print_experimental_notice(self):
+        print ("EXPERIMENTAL - SUBJECT TO CHANGE"
+               " (See --experimental-notice for more info)")

=== added file 'lava/tool/commands/help.py'
--- lava/tool/commands/help.py	1970-01-01 00:00:00 +0000
+++ lava/tool/commands/help.py	2012-03-13 18:21:16 +0000
@@ -0,0 +1,35 @@ 
+# Copyright (C) 2010 Linaro Limited
+#
+# Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
+#
+# This file is part of lava-tool.
+#
+# lava-tool is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# as published by the Free Software Foundation
+#
+# lava-tool 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 Lesser General Public License
+# along with lava-tool.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+lava.tool.commands.help
+=======================
+
+Implementation of `lava help`
+"""
+
+from lava.tool.command import Command
+
+
+class help(Command):
+    """
+    Show a summary of all available commands
+    """
+
+    def invoke(self):
+        self.parser.print_help()

=== added file 'lava/tool/dispatcher.py'
--- lava/tool/dispatcher.py	1970-01-01 00:00:00 +0000
+++ lava/tool/dispatcher.py	2012-03-13 18:22:16 +0000
@@ -0,0 +1,128 @@ 
+# Copyright (C) 2010, 2011, 2012 Linaro Limited
+#
+# Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
+#
+# This file is part of lava-tool.
+#
+# lava-tool is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# as published by the Free Software Foundation
+#
+# lava-tool 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 Lesser General Public License
+# along with lava-tool.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Module with LavaDispatcher - the command dispatcher
+"""
+
+import argparse
+import logging
+import pkg_resources
+import sys
+
+from lava.tool.errors import CommandError
+
+
+class Dispatcher(object):
+    """
+    Class implementing command line interface for launch control
+    """
+
+    description = None
+    epilog = None
+
+    def __init__(self, parser=None, name=None):
+        self.parser = parser or self.construct_parser()
+        self.subparsers = self.parser.add_subparsers(
+                title="Sub-command to invoke")
+        self.name = name
+
+    def __repr__(self):
+        return "%r(name=%r)" % (self.__class__.__name__, self.name)
+
+    @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.
+        """
+        parser_args = dict(add_help=True)
+        if cls.description is not None:
+            parser_args['description'] = cls.description
+        if cls.epilog is not None:
+            parser_args['epilog'] = cls.epilog
+        return argparse.ArgumentParser(**parser_args)
+
+    def import_commands(self, entrypoint_name):
+        """
+        Import commands from given entry point namespace
+        """
+        logging.debug("Loading commands in entry point %r", entrypoint_name)
+        for entrypoint in pkg_resources.iter_entry_points(entrypoint_name):
+                self.add_command_cls(entrypoint.load())
+
+    def add_command_cls(self, command_cls):
+        """
+        Add a new command class to this dispatcher.
+
+        The command must be a subclass of Command or SubCommand.
+        """
+        logging.debug("Loading command class %r", command_cls)
+        # Create a sub-parser where the command/sub-command can register
+        # things.
+        sub_parser = self.subparsers.add_parser(
+            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
+            # register_arguments we call register_subcommands
+            command_cls.register_subcommands(sub_parser)
+        else:
+            # Handle plain commands easily 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
+            # Command to super-call register_arguments (everyone would forget
+            # this anyway) we manually register the command class for that
+            # sub-parser so that dispatch() can look it up later.
+            sub_parser.set_defaults(
+                command_cls=command_cls,
+                parser=sub_parser)
+
+    def dispatch(self, raw_args=None):
+        """
+        Dispatch a command with the specified arguments.
+
+        If arguments are left out they are looked up in sys.argv automatically
+        """
+        # First parse whatever input arguments we've got
+        args = self.parser.parse_args(raw_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)
+        try:
+            # Give the command a chance to re-parse command line arguments
+            command.reparse_arguments(args.parser, raw_args)
+        except NotImplementedError:
+            pass
+        try:
+            return command.invoke()
+        except CommandError as ex:
+            print >> sys.stderr, "ERROR: %s" % (ex,)
+            return 1
+
+    @classmethod
+    def run(cls):
+        """
+        Dispatch commandsd and exit
+        """
+        raise SystemExit(cls().dispatch())

=== added file 'lava/tool/errors.py'
--- lava/tool/errors.py	1970-01-01 00:00:00 +0000
+++ lava/tool/errors.py	2012-03-13 16:17:50 +0000
@@ -0,0 +1,31 @@ 
+# Copyright (C) 2010, 2011, 2012 Linaro Limited
+#
+# Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
+#
+# This file is part of lava-tool.
+#
+# lava-tool is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# as published by the Free Software Foundation
+#
+# lava-tool 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 Lesser General Public License
+# along with lava-tool.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+lava.tool.errors
+================
+
+Error classes for LAVA Tool.
+"""
+
+class CommandError(Exception):
+    """
+    Raise this from a Command's invoke() method to display an error nicely.
+
+    lava-tool will exit with a status of 1 if this is raised.
+    """

=== added file 'lava/tool/main.py'
--- lava/tool/main.py	1970-01-01 00:00:00 +0000
+++ lava/tool/main.py	2012-03-13 18:22:16 +0000
@@ -0,0 +1,41 @@ 
+# Copyright (C) 2010, 2011 Linaro Limited
+#
+# Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
+#
+# This file is part of lava-tool.
+#
+# lava-tool is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# as published by the Free Software Foundation
+#
+# lava-tool 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 Lesser General Public License
+# along with lava-tool.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+lava.tool.main
+==============
+
+Implementation of the `lava` shell command.
+"""
+
+from lava.tool.dispatcher import Dispatcher
+
+
+class LavaDispatcher(Dispatcher):
+    """
+    Dispatcher implementing the `lava` shell command
+
+    This dispatcher imports plugins from `lava.commands` pkg_resources
+    namespace. Additional plugins can be registered as either
+    :class:`lava.command.Command` or :class:`lava.command.SubCommand`
+    sub-classes.
+    """
+
+    def __init__(self):
+        super(LavaDispatcher, self).__init__()
+        self.import_commands('lava.commands')

=== modified file 'lava_tool/__init__.py'
--- lava_tool/__init__.py	2011-12-17 00:58:38 +0000
+++ lava_tool/__init__.py	2012-03-13 15:40:30 +0000
@@ -18,7 +18,7 @@ 
 # along with lava-tool.  If not, see <http://www.gnu.org/licenses/>.
 
 """
-Lava Tool package
+Deprecated lava_tool package
 """
 
-__version__ = (0, 4, 0, "dev", 0)
+from lava.tool import __version__

=== modified file 'lava_tool/commands/__init__.py'
--- lava_tool/commands/__init__.py	2011-06-17 13:33:45 +0000
+++ lava_tool/commands/__init__.py	2012-03-13 18:17:45 +0000
@@ -20,64 +20,5 @@ 
 Package with command line commands
 """
 
-import argparse
-import re
-
-
-class ExperimentalNoticeAction(argparse.Action):
-    """
-    Argparse action that implements the --experimental-notice
-    """
-
-    message = """
-    Some lc-tool sub-commands are marked as EXPERIMENTAL. Those commands are
-    not guaranteed to work identically, or have identical interface between
-    subsequent lc-tool releases.
-
-    We do that to make it possible to provide good user interface and
-    server-side API when working on new features. Once a feature is stabilized
-    the UI will be frozen and all subsequent changes will retain backwards
-    compatibility.
-    """
-    message = message.lstrip()
-    message = re.sub(re.compile("[ \t]+", re.M), " ", message)
-    message = re.sub(re.compile("^ ", re.M), "", message)
-
-    def __init__(self,
-                 option_strings, dest, default=None, required=False,
-                 help=None):
-        super(ExperimentalNoticeAction, self).__init__(
-            option_strings=option_strings, dest=dest, default=default, nargs=0,
-            help=help)
-
-    def __call__(self, parser, namespace, values, option_string=None):
-        parser.exit(message=self.message)
-
-
-class ExperimentalCommandMixIn(object):
-    """
-    Experimental command.
-
-    Prints a warning message on each call to invoke()
-    """
-
-    def invoke(self):
-        self.print_experimental_notice()
-        return super(ExperimentalCommandMixIn, self).invoke()
-
-    @classmethod
-    def register_arguments(cls, parser):
-        retval = super(ExperimentalCommandMixIn,
-                       cls).register_arguments(parser)
-        parser.register("action", "experimental_notice",
-                        ExperimentalNoticeAction)
-        group = parser.add_argument_group("experimental commands")
-        group.add_argument("--experimental-notice",
-                            action="experimental_notice",
-                            default=argparse.SUPPRESS,
-                            help="Explain the nature of experimental commands")
-        return retval
-
-    def print_experimental_notice(self):
-        print ("EXPERIMENTAL - SUBJECT TO CHANGE"
-               " (See --experimental-notice for more info)")
+
+from lava.tool.commands import ExperimentalNoticeAction, ExperimentalCommandMixIn

=== removed file 'lava_tool/commands/misc.py'
--- lava_tool/commands/misc.py	2011-05-04 11:57:26 +0000
+++ lava_tool/commands/misc.py	1970-01-01 00:00:00 +0000
@@ -1,31 +0,0 @@ 
-# Copyright (C) 2010 Linaro Limited
-#
-# Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
-#
-# This file is part of lava-tool.
-#
-# lava-tool is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License version 3
-# as published by the Free Software Foundation
-#
-# lava-tool 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 Lesser General Public License
-# along with lava-tool.  If not, see <http://www.gnu.org/licenses/>.
-
-"""
-Module with miscellaneous commands (such as help and version)
-"""
-
-from lava_tool.interface import Command
-
-
-class help(Command):
-    """
-    Show a summary of all available commands
-    """
-    def invoke(self):
-        self.parser.print_help()

=== modified file 'lava_tool/dispatcher.py'
--- lava_tool/dispatcher.py	2011-12-13 16:19:52 +0000
+++ lava_tool/dispatcher.py	2012-03-13 18:22:16 +0000
@@ -20,10 +20,12 @@ 
 Module with LavaDispatcher - the command dispatcher
 """
 
-from lava_tool.interface import LavaCommandError, BaseDispatcher 
-
-
-class LavaDispatcher(BaseDispatcher):
+from lava.tool.dispatcher import Dispatcher
+from lava.tool.main import LavaDispatcher as LavaNonLegacyDispatcher
+from lava_tool.interface import LavaCommandError
+
+
+class LavaDispatcher(Dispatcher):
     """
     Class implementing command line interface for launch control
     """
@@ -39,26 +41,9 @@ 
             self.import_commands("%s.commands" % prefix)
 
 
-class LavaNonLegacyDispatcher(BaseDispatcher):
-    """
-    A dispatcher that wants to load only top-level commands,
-    not everything-and-the-kitchen-sink into one flat namespace
-
-    Available as `lava` command from shell
-    """
-
-    def __init__(self):
-        super(LavaNonLegacyDispatcher, self).__init__()
-        self.import_commands('lava.commands')
-
-
 def run_with_dispatcher_class(cls):
-    raise SystemExit(cls().dispatch())
+    raise cls.run()
 
 
 def main():
-    run_with_dispatcher_class(LavaDispatcher)
-
-
-def main_nonlegacy():
-    run_with_dispatcher_class(LavaNonLegacyDispatcher)
+    LavaDispatcher.run()

=== modified file 'lava_tool/interface.py'
--- lava_tool/interface.py	2011-12-13 16:20:05 +0000
+++ lava_tool/interface.py	2012-03-13 16:30:24 +0000
@@ -20,239 +20,5 @@ 
 Interface for all lava-tool commands
 """
 
-import argparse
-import inspect
-import logging
-import pkg_resources
-import sys
-
-
-class LavaCommandError(Exception):
-    """
-    Raise this from a Command's invoke() method to display an error nicely.
-
-    lava-tool will exit with a status of 1 if this is raised.
-    """
-
-
-class Command(object):
-    """
-    Base class for all command line tool sub-commands.
-    """
-
-    def __init__(self, parser, args):
-        """
-        Prepare instance for executing commands.
-
-        This method is called immediately after all arguments are parsed and
-        results are available. This gives subclasses a chance to configure
-        themselves. The provided parser is an instance of
-        argparse.ArgumentParser but it may not be the top-level parser (it will
-        be a parser specific for this command)
-
-        The default implementation stores both arguments as instance attributes.
-        """
-        self.parser = parser
-        self.args = args
-
-    def invoke(self):
-        """
-        Invoke command action.
-        """
-        raise NotImplementedError()
-
-    def reparse_arguments(self, parser, raw_args):
-        """
-        Re-parse raw arguments into normal arguments
-
-        Parser is the same as in register_arguments (a sub-parser) The true,
-        topmost parser is in self.parser.
-
-        This method is only needed for specific commands that need to peek at
-        the arguments before being able to truly redefine the parser and
-        re-parse the raw arguments again.
-        """
-        raise NotImplementedError()
-
-    @classmethod
-    def get_name(cls):
-        """
-        Return the name of this command.
-
-        The default implementation strips any leading underscores and replaces
-        all other underscores with dashes.
-        """
-        return cls.__name__.lstrip("_").replace("_", "-")
-
-    @classmethod
-    def get_help(cls):
-        """
-        Return the help message of this command
-        """
-        doc = inspect.getdoc(cls)
-        if doc is not None and "" in doc:
-            doc = doc[:doc.index("")].rstrip()
-        return doc
-
-    @classmethod
-    def get_epilog(cls):
-        """
-        Return the epilog of the help message
-        """
-        doc = inspect.getdoc(cls)
-        if doc is not None and "" in doc:
-            doc = doc[doc.index("") + 1:].lstrip()
-        else:
-            doc = None
-        return doc
-
-    @classmethod
-    def register_arguments(cls, parser):
-        """
-        Register arguments if required.
-
-        Subclasses can override this to add any arguments that will be
-        exposed to the command line interface.
-        """
-        pass
-
-
-class SubCommand(Command):
-    """
-    Base class for all command sub-command hubs.
-
-    This class is needed when one wants to get a custom level of command
-    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
-    the 'lava.actions' namespace. For the end user it will be available as::
-
-        $ lava-tool foo actions xxx
-
-    Where xxx is one of the Commands that is declared to live in the namespace
-    provided by 'foo actions'.
-    """
-
-    namespace = None
-
-    @classmethod
-    def get_namespace(cls):
-        """
-        Return the pkg-resources entry point namespace name from which
-        sub-commands will be loaded.
-        """
-        return cls.namespace
-
-    @classmethod
-    def register_subcommands(cls, parser):
-        """
-        Register sub commands.
-
-        This method is called around the same time as register_arguments()
-        would be called for the plain command classes. It loads commands from
-        the entry point namespace returned by get_namespace() and registeres
-        them with a BaseDispatcher class. The parsers used by that dispatcher
-        are linked to the calling dispatcher parser so the new commands enrich
-        the top-level parser tree.
-
-        In addition, the provided parser stores a dispatcher instance in its
-        defaults. This is useful when one wants to access it later. To a final
-        command instance it shall be available as self.args.dispatcher.
-        """
-        dispatcher = BaseDispatcher(parser, name=cls.get_name())
-        namespace = cls.get_namespace()
-        if namespace is not None:
-            dispatcher.import_commands(namespace)
-        parser.set_defaults(dispatcher=dispatcher)
-
-
-class BaseDispatcher(object):
-    """
-    Class implementing command line interface for launch control
-    """
-
-    description = None
-    epilog = None
-
-    def __init__(self, parser=None, name=None):
-        self.parser = parser or self.construct_parser()
-        self.subparsers = self.parser.add_subparsers(
-                title="Sub-command to invoke")
-        self.name = name
-
-    def __repr__(self):
-        return "%r(name=%r)" % (self.__class__.__name__, self.name)
-
-    @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.
-        """
-        parser_args = dict(add_help=True)
-        if cls.description is not None:
-            parser_args['description'] = cls.description
-        if cls.epilog is not None:
-            parser_args['epilog'] = cls.epilog
-        return argparse.ArgumentParser(**parser_args)
-
-    def import_commands(self, entrypoint_name):
-        """
-        Import commands from given entry point namespace
-        """
-        logging.debug("Loading commands in entry point %r", entrypoint_name)
-        for entrypoint in pkg_resources.iter_entry_points(entrypoint_name):
-                self.add_command_cls(entrypoint.load())
-
-    def add_command_cls(self, command_cls):
-        """
-        Add a new command class to this dispatcher.
-
-        The command must be a subclass of Command or SubCommand.
-        """
-        logging.debug("Loading command class %r", command_cls)
-        # Create a sub-parser where the command/sub-command can register things.
-        sub_parser = self.subparsers.add_parser(
-            command_cls.get_name(),
-            help=command_cls.get_help(),
-            epilog=command_cls.get_epilog())
-        if issubclass(command_cls, SubCommand):
-            # Handle SubCommand somewhat different. Instead of calling
-            # register_arguments we call register_subcommands
-            command_cls.register_subcommands(sub_parser)
-        else:
-            # Handle plain commands easily 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
-            # Command to super-call register_arguments (everyone would forget
-            # this anyway) we manually register the command class for that
-            # sub-parser so that dispatch() can look it up later.
-            sub_parser.set_defaults(
-                command_cls=command_cls,
-                parser=sub_parser)
-
-    def dispatch(self, raw_args=None):
-        """
-        Dispatch a command with the specified arguments.
-
-        If arguments are left out they are looked up in sys.argv automatically
-        """
-        # First parse whatever input arguments we've got 
-        args = self.parser.parse_args(raw_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)
-        try:
-            # Give the command a chance to re-parse command line arguments
-            command.reparse_arguments(args.parser, raw_args)
-        except NotImplementedError:
-            pass
-        try:
-            return command.invoke()
-        except LavaCommandError as ex:
-            print >> sys.stderr, "ERROR: %s" % (ex,)
-            return 1
+from lava.tool.errors import CommandError as LavaCommandError
+from lava.tool.command import Command, SubCommand

=== modified file 'setup.py'
--- setup.py	2011-12-13 16:18:55 +0000
+++ setup.py	2012-03-13 18:22:16 +0000
@@ -23,9 +23,10 @@ 
 
 setup(
     name='lava-tool',
-    version=":versiontools:lava_tool:__version__",
+    version=":versiontools:lava.tool:__version__",
     author="Zygmunt Krynicki",
     author_email="zygmunt.krynicki@linaro.org",
+    namespace_packages=['lava'],
     packages=find_packages(),
     description="Command line utility for Linaro validation services",
     url='https://launchpad.net/lava-tool',
@@ -34,11 +35,11 @@ 
     entry_points="""
     [console_scripts]
     lava-tool = lava_tool.dispatcher:main
-    lava = lava_tool.dispatcher:main_nonlegacy
+    lava = lava.tool.main:LavaDispatcher.run
     [lava.commands]
-    help = lava_tool.commands.misc:help
+    help = lava.tool.commands.help:help
     [lava_tool.commands]
-    help = lava_tool.commands.misc:help
+    help = lava.tool.commands.help:help
     auth-add = lava_tool.commands.auth:auth_add
     """,
     classifiers=[