summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Ulrich Niedermann <hun@n-dimensional.de>2008-07-03 22:40:29 +0200
committerHans Ulrich Niedermann <hun@n-dimensional.de>2008-07-15 12:28:56 +0200
commit5eff502ced858a7d3a4f3b1c4d44b28e83dc92de (patch)
tree86babcca4fc2a521d5c4f19873c3a2ca6af66eae
parent4b538e4003103171dacaa7a0ac2bad52432cdfbd (diff)
downloadnbb-5eff502ced858a7d3a4f3b1c4d44b28e83dc92de.tar.gz
nbb-5eff502ced858a7d3a4f3b1c4d44b28e83dc92de.tar.xz
nbb-5eff502ced858a7d3a4f3b1c4d44b28e83dc92de.zip
Rework command framework
Implies catching exceptions in a single place.
-rw-r--r--TODO4
-rw-r--r--src/nbblib/bs.py4
-rw-r--r--src/nbblib/commands.py151
-rw-r--r--src/nbblib/main.py36
-rw-r--r--test/nbb-basic.at15
-rw-r--r--test/nbb-detect.at20
6 files changed, 124 insertions, 106 deletions
diff --git a/TODO b/TODO
index 3fb0272..e9ab673 100644
--- a/TODO
+++ b/TODO
@@ -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()