X-Git-Url: https://git.distorted.org.uk/~mdw/stgit/blobdiff_plain/de4c9d27c59100d6c2c3e04720f0c9651e58a87c..111251132edff92d1681d915af0d060af35250d2:/stgit/main.py diff --git a/stgit/main.py b/stgit/main.py index 06611a9..e324179 100644 --- a/stgit/main.py +++ b/stgit/main.py @@ -18,117 +18,160 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ -import sys, os -from optparse import OptionParser, make_option - -from stgit.utils import * -from stgit import stack, git -from stgit.version import version -from stgit.config import config -from stgit.commands.common import * - -# The commands -import stgit.commands.add -import stgit.commands.applied -import stgit.commands.delete -import stgit.commands.diff -import stgit.commands.clean -import stgit.commands.export -import stgit.commands.files -import stgit.commands.init -import stgit.commands.new -import stgit.commands.pop -import stgit.commands.push -import stgit.commands.refresh -import stgit.commands.resolved -import stgit.commands.rm -import stgit.commands.series -import stgit.commands.status -import stgit.commands.top -import stgit.commands.unapplied +import sys, os, traceback +import stgit.commands +from stgit.out import * +from stgit import argparse, run, utils # # The commands map # -commands = { - 'add': stgit.commands.add, - 'applied': stgit.commands.applied, - 'delete': stgit.commands.delete, - 'diff': stgit.commands.diff, - 'clean': stgit.commands.clean, - 'export': stgit.commands.export, - 'files': stgit.commands.files, - 'init': stgit.commands.init, - 'new': stgit.commands.new, - 'pop': stgit.commands.pop, - 'push': stgit.commands.push, - 'refresh': stgit.commands.refresh, - 'resolved': stgit.commands.resolved, - 'rm': stgit.commands.rm, - 'series': stgit.commands.series, - 'status': stgit.commands.status, - 'top': stgit.commands.top, - 'unapplied':stgit.commands.unapplied, - } +class Commands(dict): + """Commands class. It performs on-demand module loading + """ + def canonical_cmd(self, key): + """Return the canonical name for a possibly-shortenned + command name. + """ + candidates = [cmd for cmd in self.keys() if cmd.startswith(key)] + + if not candidates: + out.error('Unknown command: %s' % key, + 'Try "%s help" for a list of supported commands' % prog) + sys.exit(utils.STGIT_GENERAL_ERROR) + elif len(candidates) > 1: + out.error('Ambiguous command: %s' % key, + 'Candidates are: %s' % ', '.join(candidates)) + sys.exit(utils.STGIT_GENERAL_ERROR) + + return candidates[0] + + def __getitem__(self, key): + cmd_mod = self.get(key) or self.get(self.canonical_cmd(key)) + return stgit.commands.get_command(cmd_mod) + +cmd_list = stgit.commands.get_commands() +commands = Commands((cmd, mod) for cmd, (mod, kind, help) + in cmd_list.iteritems()) def print_help(): print 'usage: %s [options]' % os.path.basename(sys.argv[0]) print - print 'commands:' - print ' help print this message' - - cmds = commands.keys() - cmds.sort() - for cmd in cmds: - print ' ' + cmd + ' ' * (12 - len(cmd)) + commands[cmd].help + print 'Generic commands:' + print ' help print the detailed command usage' + print ' version display version information' + print ' copyright display copyright information' + print + stgit.commands.pretty_command_list(cmd_list, sys.stdout) # # The main function (command dispatcher) # -def main(): +def _main(): """The main function """ + global prog + prog = os.path.basename(sys.argv[0]) if len(sys.argv) < 2: - print >> sys.stderr, 'Unknown command' + print >> sys.stderr, 'usage: %s ' % prog print >> sys.stderr, \ - ' Try "%s help" for a list of supported commands' % prog - sys.exit(1) + ' Try "%s --help" for a list of supported commands' % prog + sys.exit(utils.STGIT_GENERAL_ERROR) cmd = sys.argv[1] - if cmd in ['-h', '--help', 'help']: - print_help() - sys.exit(0) - if cmd in ['-v', '--version']: - print '%s %s' % (prog, version) - sys.exit(0) - if not cmd in commands: - print >> sys.stderr, 'Unknown command: %s' % cmd - print >> sys.stderr, ' Try "%s help" for a list of supported commands' \ - % prog - sys.exit(1) + if cmd in ['-h', '--help']: + if len(sys.argv) >= 3: + cmd = commands.canonical_cmd(sys.argv[2]) + sys.argv[2] = '--help' + else: + print_help() + sys.exit(utils.STGIT_SUCCESS) + if cmd == 'help': + if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']: + cmd = commands.canonical_cmd(sys.argv[2]) + if not cmd in commands: + out.error('%s help: "%s" command unknown' % (prog, cmd)) + sys.exit(utils.STGIT_GENERAL_ERROR) + + sys.argv[0] += ' %s' % cmd + command = commands[cmd] + parser = argparse.make_option_parser(command) + from pydoc import pager + pager(parser.format_help()) + else: + print_help() + sys.exit(utils.STGIT_SUCCESS) + if cmd in ['-v', '--version', 'version']: + from stgit.version import version + print 'Stacked GIT %s' % version + os.system('git --version') + print 'Python version %s' % sys.version + sys.exit(utils.STGIT_SUCCESS) + if cmd in ['copyright']: + print __copyright__ + sys.exit(utils.STGIT_SUCCESS) # re-build the command line arguments + cmd = commands.canonical_cmd(cmd) sys.argv[0] += ' %s' % cmd del(sys.argv[1]) command = commands[cmd] - parser = OptionParser(usage = command.usage, - option_list = command.options) + parser = argparse.make_option_parser(command) options, args = parser.parse_args() + directory = command.directory + + # These modules are only used from this point onwards and do not + # need to be imported earlier + from stgit.exception import StgException + from stgit.config import config_setup + from ConfigParser import ParsingError, NoSectionError + from stgit.stack import Series + + try: + debug_level = int(os.environ.get('STGIT_DEBUG_LEVEL', 0)) + except ValueError: + out.error('Invalid STGIT_DEBUG_LEVEL environment variable') + sys.exit(utils.STGIT_GENERAL_ERROR) + + try: + directory.setup() + config_setup() + + # Some commands don't (always) need an initialized series. + if directory.needs_current_series: + if hasattr(options, 'branch') and options.branch: + command.crt_series = Series(options.branch) + else: + command.crt_series = Series() + + ret = command.func(parser, options, args) + except (StgException, IOError, ParsingError, NoSectionError), err: + directory.write_log(cmd) + out.error(str(err), title = '%s %s' % (prog, cmd)) + if debug_level > 0: + traceback.print_exc() + sys.exit(utils.STGIT_COMMAND_ERROR) + except SystemExit: + # Triggered by the option parser when it finds bad commandline + # parameters. + sys.exit(utils.STGIT_COMMAND_ERROR) + except KeyboardInterrupt: + sys.exit(utils.STGIT_GENERAL_ERROR) + except: + out.error('Unhandled exception:') + traceback.print_exc() + sys.exit(utils.STGIT_BUG_ERROR) + + directory.write_log(cmd) + sys.exit(ret or utils.STGIT_SUCCESS) + +def main(): try: - # the lines below are a simple way to avoid an exception when - # stgit is run outside an initialised tree - stgit.commands.common.crt_series = stack.Series() - setattr(command, 'crt_series', stgit.commands.common.crt_series) - - command.func(parser, options, args) - except (IOError, CmdException, stack.StackException, git.GitException), \ - err: - print >> sys.stderr, '%s %s: %s' % (prog, cmd, err) - sys.exit(2) - - sys.exit(0) + _main() + finally: + run.finish_logging()