"""The migrate command-line tool. """ import sys from migrate.versioning.base import * from optparse import OptionParser,Values from migrate.versioning import api,exceptions import inspect alias = dict( s=api.script, vc=api.version_control, dbv=api.db_version, v=api.version, ) def alias_setup(): global alias for key,val in alias.iteritems(): setattr(api,key,val) alias_setup() class ShellUsageError(Exception): def die(self,exitcode=None): usage="""%%prog COMMAND ... Available commands: %s Enter "%%prog help COMMAND" for information on a particular command. """ usage = usage.replace("\n"+" "*8,"\n") commands = list(api.__all__) commands.sort() commands = '\n'.join(map((lambda x:'\t'+x),commands)) message = usage%commands try: message = message.replace('%prog',sys.argv[0]) except IndexError: pass if self.args[0] is not None: message += "\nError: %s\n"%str(self.args[0]) if exitcode is None: exitcode = 1 if exitcode is None: exitcode = 0 die(message,exitcode) def die(message,exitcode=1): if message is not None: sys.stderr.write(message) sys.stderr.write("\n") raise SystemExit(int(exitcode)) kwmap = dict( v='verbose', d='debug', f='force', ) def kwparse(arg): ret = arg.split('=',1) if len(ret) == 1: # No value specified (--kw, not --kw=stuff): use True ret = [ret[0],True] return ret def parse_arg(arg,argnames): global kwmap if arg.startswith('--'): # Keyword-argument; either --keyword or --keyword=value kw,val = kwparse(arg[2:]) elif arg.startswith('-'): # Short form of a keyword-argument; map it to a keyword try: parg = kwmap.get(arg) except KeyError: raise ShellUsageError("Invalid argument: %s"%arg) kw,val = kwparse(parg) else: # Simple positional parameter val = arg try: kw = argnames.pop(0) except IndexError,e: raise ShellUsageError("Too many arguments to command") return kw,val def parse_args(*args,**kwargs): """Map positional arguments to keyword-args""" args=list(args) try: cmdname = args.pop(0) if cmdname == 'downgrade': if not args[-1].startswith('--'): kwargs['version'] = args[-1] except IndexError: # No command specified: no error message; just show usage raise ShellUsageError(None) # Special cases: -h and --help should act like 'help' if cmdname == '-h' or cmdname == '--help': cmdname = 'help' cmdfunc = getattr(api,cmdname,None) if cmdfunc is None or cmdname.startswith('_'): raise ShellUsageError("Invalid command %s"%cmdname) argnames, p,k, defaults = inspect.getargspec(cmdfunc) argnames_orig = list(argnames) for arg in args: kw,val = parse_arg(arg,argnames) kwargs[kw] = val if defaults is not None: num_defaults = len(defaults) else: num_defaults = 0 req_argnames = argnames_orig[:len(argnames_orig)-num_defaults] for name in req_argnames: if name not in kwargs: raise ShellUsageError("Too few arguments: %s not specified"%name) return cmdfunc,kwargs def main(argv=None,**kwargs): if argv is None: argv = list(sys.argv[1:]) try: command, kwargs = parse_args(*argv,**kwargs) except ShellUsageError,e: e.die() try: ret = command(**kwargs) if ret is not None: print ret except exceptions.UsageError,e: e = ShellUsageError(e.args[0]) e.die() except exceptions.KnownError,e: die(e.args[0]) if __name__=="__main__": main()