diff options
author | Hans Ulrich Niedermann <hun@n-dimensional.de> | 2008-07-03 22:40:29 +0200 |
---|---|---|
committer | Hans Ulrich Niedermann <hun@n-dimensional.de> | 2008-07-15 12:28:56 +0200 |
commit | 5eff502ced858a7d3a4f3b1c4d44b28e83dc92de (patch) | |
tree | 86babcca4fc2a521d5c4f19873c3a2ca6af66eae | |
parent | 4b538e4003103171dacaa7a0ac2bad52432cdfbd (diff) | |
download | nbb-5eff502ced858a7d3a4f3b1c4d44b28e83dc92de.tar.gz nbb-5eff502ced858a7d3a4f3b1c4d44b28e83dc92de.tar.xz nbb-5eff502ced858a7d3a4f3b1c4d44b28e83dc92de.zip |
Rework command framework
Implies catching exceptions in a single place.
-rw-r--r-- | TODO | 4 | ||||
-rw-r--r-- | src/nbblib/bs.py | 4 | ||||
-rw-r--r-- | src/nbblib/commands.py | 151 | ||||
-rw-r--r-- | src/nbblib/main.py | 36 | ||||
-rw-r--r-- | test/nbb-basic.at | 15 | ||||
-rw-r--r-- | test/nbb-detect.at | 20 |
6 files changed, 124 insertions, 106 deletions
@@ -6,7 +6,8 @@ TODO: (to get git-amb equivalent functionality) * Write docs: man page equivalent, README, and HACKING. TODO: (Large list) - * Move command objects to plugin detection framework + * Switch plugins.Foo.validate() functions to different protocol: + Either return or raise an exception. * supports execution of user commands in source, build, install dirs * BS support: cmake, scons, ... * VCS support: SVN, darcs, hg, ... @@ -33,6 +34,7 @@ TODO: (Large list) * Man page or something similar. Generate from help texts? DONE: + * Do not move command objects to plugin detection framework (bad idea!) * Find or implement @abstractmethod decorator. * Unify detect() methods. * Design nice user interface. Requirements: diff --git a/src/nbblib/bs.py b/src/nbblib/bs.py index f8f247c..f9215a2 100644 --- a/src/nbblib/bs.py +++ b/src/nbblib/bs.py @@ -76,7 +76,9 @@ class BSSourceTree(plugins.GenericDetectPlugin): class AutomakeSourceTree(BSSourceTree): + name = 'automake' + def __init__(self, context, vcs_tree): super(AutomakeSourceTree, self).__init__(context) srcdir = vcs_tree.tree_root @@ -130,7 +132,9 @@ class AutomakeSourceTree(BSSourceTree): class SconsSourceTree(BSSourceTree): + name = 'scons' + def __init__(self, context, vcs_tree): super(SconsSourceTree, self).__init__(context) srcdir = vcs_tree.tree_root diff --git a/src/nbblib/commands.py b/src/nbblib/commands.py index e5eab40..2e653b6 100644 --- a/src/nbblib/commands.py +++ b/src/nbblib/commands.py @@ -15,7 +15,7 @@ import nbblib.vcs as vcs import nbblib.bs as bs -__all__ = [] +__all__ = ['Commander'] def adjust_doc(doc): @@ -36,18 +36,23 @@ def adjust_doc(doc): __all__.append('CommandLineError') class CommandLineError(Exception): - def __init__(self, message, *args, **kwargs): + def __init__(self, message): super(CommandLineError, self).__init__() - if args: - self.msg = message % args - elif kwargs: - self.msg = message % kwargs - else: - self.msg = message + self.msg = message def __str__(self): return "Command line error: %s" % self.msg +__all__.append('UnknownCommand') +class UnknownCommand(Exception): + def __init__(self, context, cmd): + super(UnknownCommand, self).__init__() + self.prog = context.prog + self.cmd = cmd + def __str__(self): + return "Unknown %(prog)s command '%(cmd)s'" % self.__dict__ + + ######################################################################## # Command plugin system ######################################################################## @@ -81,27 +86,44 @@ class Command(object): usage = '' - def __init__(self, *args, **kwargs): + def __init__(self, context, *args, **kwargs): super(Command, self).__init__() self.validate_args(*args, **kwargs) self.args = args self.kwargs = kwargs - self.context = kwargs['context'] + self.context = context @plugins.abstractmethod def run(self): """Run the command""" pass - # Candidate for abstractmethod - def validate_args(self, *args, **kwargs): - """Validate command line arguments""" - print "Command: ", self.name - print "*args: ", args - print "**kwargs:", kwargs - if len(args) > 0: + @plugins.abstractmethod + def validate_args(self, *cmdargs, **kwargs): + """Validate command line arguments: Abstract method. + + May make use of self.context. + """ + pass + + def validate_args_none(self, *cmdargs, **kwargs): + """Validate command line arguments: no argument at all""" + logging.debug("Command: %s", self.name) + logging.debug("*cmdargs: %s", cmdargs) + logging.debug("**kwargs: %s", kwargs) + if len(cmdargs) > 0: raise CommandLineError("'%s' command takes no parameters", self.name) + logging.debug("Command match!") + return True + + def validate_args_any(self, *cmdargs, **kwargs): + """Validate command line arguments: accept any argument""" + logging.debug("Command: %s", self.name) + logging.debug("*cmdargs: %s", cmdargs) + logging.debug("**kwargs: %s", kwargs) + logging.debug("Command match!") + return True def __str__(self): return "Command(%s, %s)" % (self.cmd_name, self.cmdargs) @@ -127,8 +149,7 @@ class HelpCommand(Command): print "List of commands:" keys = Command.plugins.keys() if not keys: - print "Error: No commands found." - sys.exit(2) + raise Exception("No commands found. Please lart the developer.") keys.sort() keys2 = Command.plugins.keys() keys2.sort(lambda a,b: cmp(len(b),len(a))) @@ -163,6 +184,7 @@ class HelpCommand(Command): class InternalConfigCommand(Command): name = 'internal-config' summary = 'print internal program configuration' + validate_args = Command.validate_args_none def run(self): print "Source tree types:", ", ".join(vcs.VCSourceTree.plugins.keys()) print "Build system types:", ", ".join(bs.BSSourceTree.plugins.keys()) @@ -172,10 +194,9 @@ class InternalConfigCommand(Command): class SourceClassCommand(Command): """Base class for commands acting on source trees""" name = None # abstract command class - def __init__(self, *args, **kwargs): - super(SourceClassCommand, self).__init__(*args, **kwargs) + def __init__(self, context, *args, **kwargs): + super(SourceClassCommand, self).__init__(context, *args, **kwargs) - context = kwargs['context'] srcdir = os.getcwd() absdir = os.path.abspath(srcdir) @@ -193,20 +214,18 @@ class SourceClassCommand(Command): class DetectCommand(Command): name = None - def __init__(self, *args, **kwargs): - super(DetectCommand, self).__init__(*args, **kwargs) - self.context = kwargs['context'] + def __init__(self, context, *args, **kwargs): + super(DetectCommand, self).__init__(context, *args, **kwargs) self.srcdir = os.getcwd() self.absdir = os.path.abspath(self.srcdir) - def validate_args(self, *args, **kwargs): - pass + validate_args = Command.validate_args_none class DetectVCSCommand(DetectCommand): name = "detect-vcs" summary = "detect source tree VCS" - def __init__(self, *args, **kwargs): - super(DetectVCSCommand, self).__init__(*args, **kwargs) + def __init__(self, context, *args, **kwargs): + super(DetectVCSCommand, self).__init__(context, *args, **kwargs) self.vcs_sourcetree = vcs.VCSourceTree.detect(self.context, self.absdir) logging.debug("vcs_sourcetree %s", self.vcs_sourcetree) def run(self): @@ -214,13 +233,14 @@ class DetectVCSCommand(DetectCommand): print 'VCS:', self.vcs_sourcetree.name, self.vcs_sourcetree.tree_root else: print 'VCS:', 'Not detected' + validate_args = Command.validate_args_none class DetectBSCommand(DetectCommand): name = "detect-bs" summary = "detect source tree BS" - def __init__(self, *args, **kwargs): - super(DetectBSCommand, self).__init__(*args, **kwargs) + def __init__(self, context, *args, **kwargs): + super(DetectBSCommand, self).__init__(context, *args, **kwargs) self.vcs_sourcetree = vcs.VCSourceTree.detect(self.context, self.absdir) logging.debug("vcs_sourcetree %s", self.vcs_sourcetree) self.bs_sourcetree = bs.BSSourceTree.detect(self.context, @@ -231,6 +251,7 @@ class DetectBSCommand(DetectCommand): print 'BS:', self.bs_sourcetree.name, self.bs_sourcetree.tree_root else: print 'BS:', 'Not detected' + validate_args = Command.validate_args_none class BuildTestCommand(SourceClassCommand): @@ -241,49 +262,37 @@ class BuildTestCommand(SourceClassCommand): self.bs_sourcetree.configure() self.bs_sourcetree.build() self.bs_sourcetree.install() + validate_args = Command.validate_args_none class InitCommand(SourceClassCommand): name = 'init' summary = 'initialize buildsystem' - def validate_args(self, *args, **kwargs): - """Validate command line arguments""" - if len(args) > 0: - raise CommandLineError("'%s' command takes no parameters", - self.name) + validate_args = Command.validate_args_none def run(self): self.bs_sourcetree.init() + class ConfigureCommand(SourceClassCommand): name = 'configure' summary = 'configure buildsystem' - def validate_args(self, *args, **kwargs): - """Validate command line arguments""" - if len(args) > 0: - raise CommandLineError("'%s' command takes no parameters", - self.name) + validate_args = Command.validate_args_none def run(self): self.bs_sourcetree.configure() + class BuildCommand(SourceClassCommand): name = 'build' summary = 'build from source' - def validate_args(self, *args, **kwargs): - """Validate command line arguments""" - if len(args) > 0: - raise CommandLineError("'%s' command takes no parameters", - self.name) + validate_args = Command.validate_args_none def run(self): self.bs_sourcetree.build() + class InstallCommand(SourceClassCommand): name = 'install' summary = 'install the built things' - def validate_args(self, *args, **kwargs): - """Validate command line arguments""" - if len(args) > 0: - raise CommandLineError("'%s' command takes no parameters", - self.name) + validate_args = Command.validate_args_none def run(self): self.bs_sourcetree.install() @@ -291,8 +300,7 @@ class InstallCommand(SourceClassCommand): class MakeCommand(SourceClassCommand): name = 'make' summary = 'run make in builddir' - def validate_args(self, *args, **kwargs): - pass + validate_args = Command.validate_args_any def run(self): os.chdir(self.bs_sourcetree.config.builddir) progutils.prog_run(["make"] + list(self.args), @@ -340,34 +348,17 @@ class ConfigCommand(SourceClassCommand): assert(False) -######################################################################## -# Commands -######################################################################## - +class Commander(object): -__all__.append('UnknownCommand') -class UnknownCommand(Exception): - def __init__(self, cmd): - super(UnknownCommand, self).__init__() - self.cmd = cmd - def __str__(self): - return "Fatal: Unknown command '%(cmd)s'" % self.__dict__ - - -__all__.append('NBB_Command') -class NBB_Command(object): - def __init__(self, cmd, cmdargs, context): + def __init__(self, context, cmd, *cmdargs): + self.context = context + logging.debug("Commander() %s %s", cmd, cmdargs) if cmd in Command.plugins: - try: - c = Command.plugins[cmd](*cmdargs, **{'context':context}) - c.run() - except CommandLineError, e: - print "%(prog)s: Fatal:" % context, e - sys.exit(2) - except progutils.ProgramRunError, e: - print "%(prog)s: Fatal:" % context, e - sys.exit(3) + self.command = Command.plugins[cmd](context, *cmdargs) else: - print "%(prog)s:" % context, UnknownCommand(cmd) - sys.exit(2) + raise UnknownCommand(context, cmd) + + def run(self): + self.command.run() +# End of file. diff --git a/src/nbblib/main.py b/src/nbblib/main.py index fabbdad..22c93e3 100644 --- a/src/nbblib/main.py +++ b/src/nbblib/main.py @@ -75,6 +75,7 @@ from nbblib import commands from nbblib import package from nbblib import plugins from nbblib import vcs +from nbblib import progutils def print_version(context): @@ -206,14 +207,13 @@ def main(argv): if len(argv) < 2: raise commands.CommandLineError(\ - "%(prog)s requires some arguments", - prog=context.prog) + "%(prog)s requires some arguments" % context) i = 1 while i<len(argv): if argv[i][0] != '-': break - if argv[i] in ('-h', '--help'): + elif argv[i] in ('-h', '--help'): print_help(context) return elif argv[i] in ('-V', '--version'): @@ -225,7 +225,7 @@ def main(argv): i = i + 1 assert(i<len(argv)) context.bs = argv[i] - elif argv[i][:6] == '--build-system=': + elif argv[i][:15] == '--build-system=': context.bs = argv[i][6:] elif argv[i] in ('-v', '--vcs'): i = i + 1 @@ -236,32 +236,44 @@ def main(argv): elif argv[i] in ('--info', '--debug'): pass # acted on in previous stage else: - raise commands.CommandLineError('Unknown global option %s' % repr(argv[i])) - # print "", i, argv[i] + raise commands.CommandLineError(\ + 'Unknown global option %s' % repr(argv[i])) i = i + 1 + logging.info("Context: %s", context) cmd = argv[i] cmdargs = argv[i+1:] - nbb = commands.NBB_Command(cmd, cmdargs, context=context) + + cdr = commands.Commander(context, cmd, *cmdargs) + cdr.run() def cmdmain(argv): try: main(argv) logging.shutdown() + except commands.CommandLineError, e: + logging.error(e) + logging.shutdown() + sys.exit(2) + except commands.UnknownCommand, e: + logging.error(e) + logging.shutdown() + sys.exit(2) except plugins.PluginNoMatch, e: + logging.error(e) logging.shutdown() - print e sys.exit(1) except plugins.AmbigousPluginDetection, e: + logging.error(e) logging.shutdown() - print e sys.exit(1) - except commands.CommandLineError, e: + except progutils.ProgramRunError, e: + logging.error(e) logging.shutdown() - print e - sys.exit(2) + sys.exit(3) except SystemExit, e: + logging.error("Someone called sys.exit() who should not have", exc_info=e) logging.shutdown() raise diff --git a/test/nbb-basic.at b/test/nbb-basic.at index 4da4795..81ebf09 100644 --- a/test/nbb-basic.at +++ b/test/nbb-basic.at @@ -33,9 +33,18 @@ dnl =================================================================== AT_SETUP([nbb basic: no parameters at all]) AT_KEYWORDS([nbb no parameters]) -AT_CHECK([AT_NBB], [2], -[Command line error: nbb requires some arguments -], []) +AT_CHECK([AT_NBB], [2], [], +[ERROR: Command line error: nbb requires some arguments +]) +AT_CLEANUP() + +dnl =================================================================== + +AT_SETUP([nbb basic: unknown command]) +AT_KEYWORDS([nbb unknown command]) +AT_CHECK([AT_NBB this-command-does-not-exist], [2], [], +[ERROR: Unknown nbb command 'this-command-does-not-exist' +]) AT_CLEANUP() dnl =================================================================== diff --git a/test/nbb-detect.at b/test/nbb-detect.at index 0ca3176..d5458f4 100644 --- a/test/nbb-detect.at +++ b/test/nbb-detect.at @@ -8,8 +8,8 @@ dnl =================================================================== AT_SETUP([nbb detect-vcs: no VCS repository type]) AT_KEYWORDS([nbb detect vcs]) AT_CHECK([mkdir test.foo && cd test.foo]) -AT_CHECK([echo "Unknown VCS source tree type: '$PWD/test.foo'" > expout -cd test.foo && AT_NBB detect-vcs], [1], [expout]) +AT_CHECK([echo "ERROR: Unknown VCS source tree type: '$PWD/test.foo'" > experr +cd test.foo && AT_NBB detect-vcs], [1], [], [experr]) AT_CHECK([rm -rf test.foo]) AT_CLEANUP() @@ -46,11 +46,11 @@ AT_CHECK([cd test.bzrgit && bzr init && bzr nick testnick]) AT_CHECK([cd test.bzrgit && git init], [0], [Initialized empty Git repository in .git/ ]) -AT_CHECK([echo "Ambigous VCS types detected for '$PWD/test.bzrgit': +AT_CHECK([echo "ERROR: Ambigous VCS types detected for '$PWD/test.bzrgit': VCS type Branch name git master - bzr testnick" > expout -cd test.bzrgit && AT_NBB detect-vcs], [1], [expout]) + bzr testnick" > experr +cd test.bzrgit && AT_NBB detect-vcs], [1], [], [experr]) AT_CHECK([rm -rf test.bzrgit]) AT_CLEANUP() @@ -102,10 +102,10 @@ AC[_]OUTPUT AT_DATA([test.git/SConstruct], [dnl # Test ]) -AT_CHECK([echo "Ambigous BS types detected for '$PWD/test.git': +AT_CHECK([echo "ERROR: Ambigous BS types detected for '$PWD/test.git': automake - scons" > expout -cd test.git && AT_NBB detect-bs], [1], [expout]) + scons" > experr +cd test.git && AT_NBB detect-bs], [1], [], [experr]) AT_CHECK([rm -rf test.git]) AT_CLEANUP() @@ -117,8 +117,8 @@ AT_CHECK([mkdir test.git && cd test.git]) AT_CHECK([cd test.git && git init], [0], [Initialized empty Git repository in .git/ ]) -AT_CHECK([echo "Unknown BS source tree type: '$PWD/test.git'" > expout -cd test.git && AT_NBB detect-bs], [1], [expout]) +AT_CHECK([echo "ERROR: Unknown BS source tree type: '$PWD/test.git'" > experr +cd test.git && AT_NBB detect-bs], [1], [], [experr]) AT_CHECK([rm -rf test.git]) AT_CLEANUP() |