From: Mike Bayer Date: Thu, 29 Apr 2010 22:17:58 +0000 (-0400) Subject: some ui improvements X-Git-Tag: rel_0_1_0~96 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=839286fa54bc3959b1950134e0d4feae86639c46;p=thirdparty%2Fsqlalchemy%2Falembic.git some ui improvements --- diff --git a/alembic/__init__.py b/alembic/__init__.py index c8c90904..f18873da 100644 --- a/alembic/__init__.py +++ b/alembic/__init__.py @@ -1,97 +1,4 @@ -from alembic import config, command, util -from optparse import OptionParser -import inspect -import os __version__ = '0.1alpha' -def main(argv): - - # TODO: - # OK, what's the super option parser library that - # allows plus command-specfic sub-options, - # and derives everything from callables ? - # we're inventing here a bit. - - commands = {} - for fn in [getattr(command, n) for n in dir(command)]: - if inspect.isfunction(fn) and \ - fn.__name__[0] != '_' and \ - fn.__module__ == 'alembic.command': - - spec = inspect.getargspec(fn) - if spec[3]: - positional = spec[0][1:-len(spec[3])] - kwarg = spec[0][-len(spec[3]):] - else: - positional = spec[0][1:] - kwarg = [] - - commands[fn.__name__] = { - 'name':fn.__name__, - 'fn':fn, - 'positional':positional, - 'kwargs':kwarg - } - - def format_cmd(cmd): - return "%s %s" % ( - cmd['name'], - " ".join(["<%s>" % p for p in cmd['positional']]) - ) - - def format_opt(cmd, padding=32): - opt = format_cmd(cmd) - return " " + opt + \ - ((padding - len(opt)) * " ") + cmd['fn'].__doc__ - - parser = OptionParser( - "usage: %prog [options] [command arguments]\n\n" - "Available Commands:\n" + - "\n".join(sorted([ - format_opt(cmd) - for cmd in commands.values() - ])) + - "\n\n is a hex revision id or 'head'" - ) - - parser.add_option("-c", "--config", - type="string", - default="alembic.ini", - help="Alternate config file") - parser.add_option("-t", "--template", - default='generic', - type="string", - help="Setup template for use with 'init'") - parser.add_option("-m", "--message", - type="string", - help="Message string to use with 'revision'") - - cmd_line_options, cmd_line_args = parser.parse_args(argv[1:]) - - if len(cmd_line_args) < 1: - util.err("no command specified") - - cmd = cmd_line_args.pop(0).replace('-', '_') - - try: - cmd_fn = commands[cmd] - except KeyError: - util.err("no such command %r" % cmd) - - kw = dict( - (k, getattr(cmd_line_options, k)) - for k in cmd_fn['kwargs'] - ) - - if len(cmd_line_args) != len(cmd_fn['positional']): - util.err("Usage: %s %s [options]" % ( - os.path.basename(argv[0]), - format_cmd(cmd_fn) - )) - - cfg = config.Config(cmd_line_options.config) - cmd_fn['fn'](cfg, *cmd_line_args, **kw) - - diff --git a/alembic/config.py b/alembic/config.py index b56fc9d4..d244193a 100644 --- a/alembic/config.py +++ b/alembic/config.py @@ -1,8 +1,9 @@ +from alembic import command, util +from optparse import OptionParser import ConfigParser import inspect import os import sys -from alembic import util class Config(object): def __init__(self, file_): @@ -31,4 +32,93 @@ class Config(object): else: return default - \ No newline at end of file +def main(argv): + + # TODO: + # OK, what's the super option parser library that + # allows plus command-specfic sub-options, + # and derives everything from callables ? + # we're inventing here a bit. + + commands = {} + for fn in [getattr(command, n) for n in dir(command)]: + if inspect.isfunction(fn) and \ + fn.__name__[0] != '_' and \ + fn.__module__ == 'alembic.command': + + spec = inspect.getargspec(fn) + if spec[3]: + positional = spec[0][1:-len(spec[3])] + kwarg = spec[0][-len(spec[3]):] + else: + positional = spec[0][1:] + kwarg = [] + + commands[fn.__name__] = { + 'name':fn.__name__, + 'fn':fn, + 'positional':positional, + 'kwargs':kwarg + } + + def format_cmd(cmd): + return "%s %s" % ( + cmd['name'], + " ".join(["<%s>" % p for p in cmd['positional']]) + ) + + def format_opt(cmd, padding=32): + opt = format_cmd(cmd) + return " " + opt + \ + ((padding - len(opt)) * " ") + cmd['fn'].__doc__ + + parser = OptionParser( + "usage: %prog [options] [command arguments]\n\n" + "Available Commands:\n" + + "\n".join(sorted([ + format_opt(cmd) + for cmd in commands.values() + ])) + + "\n\n is a hex revision id or 'head'" + ) + + parser.add_option("-c", "--config", + type="string", + default="alembic.ini", + help="Alternate config file") + parser.add_option("-t", "--template", + default='generic', + type="string", + help="Setup template for use with 'init'") + parser.add_option("-m", "--message", + type="string", + help="Message string to use with 'revision'") + + cmd_line_options, cmd_line_args = parser.parse_args(argv[1:]) + + if len(cmd_line_args) < 1: + util.err("no command specified") + + cmd = cmd_line_args.pop(0).replace('-', '_') + + try: + cmd_fn = commands[cmd] + except KeyError: + util.err("no such command %r" % cmd) + + kw = dict( + (k, getattr(cmd_line_options, k)) + for k in cmd_fn['kwargs'] + ) + + if len(cmd_line_args) != len(cmd_fn['positional']): + util.err("Usage: %s %s [options]" % ( + os.path.basename(argv[0]), + format_cmd(cmd_fn) + )) + + cfg = Config(cmd_line_options.config) + try: + cmd_fn['fn'](cfg, *cmd_line_args, **kw) + except util.CommandError, e: + util.err(str(e)) diff --git a/alembic/context.py b/alembic/context.py index c0380350..7ec872e6 100644 --- a/alembic/context.py +++ b/alembic/context.py @@ -1,6 +1,9 @@ from alembic.ddl import base from alembic import util from sqlalchemy import MetaData, Table, Column, String +import logging + +log = logging.getLogger(__name__) class ContextMeta(type): def __init__(cls, classname, bases, dict_): @@ -29,6 +32,9 @@ class DefaultContext(object): return self.connection.scalar(_version.select()) def _update_current_rev(self, old, new): + if old == new: + return + if new is None: self.connection.execute(_version.delete()) elif old is None: @@ -37,13 +43,13 @@ class DefaultContext(object): self.connection.execute(_version.update(), {'version_num':new}) def run_migrations(self, **kw): - current_rev = self._current_rev() - rev = -1 + current_rev = prev_rev = rev = self._current_rev() for change, rev in self._migrations_fn(current_rev): - print "-> %s" % (rev, ) + log.info("Running %s %s -> %s", change.__name__, prev_rev, rev) change(**kw) - if rev != -1: - self._update_current_rev(current_rev, rev) + prev_rev = rev + + self._update_current_rev(current_rev, rev) def _exec(self, construct): self.connection.execute(construct) diff --git a/alembic/op.py b/alembic/op.py index 2e1abd17..dbd0de4e 100644 --- a/alembic/op.py +++ b/alembic/op.py @@ -3,7 +3,11 @@ from alembic.context import get_context from sqlalchemy.types import NULLTYPE from sqlalchemy import schema -__all__ = ['alter_column', 'create_foreign_key', 'create_unique_constraint', 'execute'] +__all__ = [ + 'alter_column', + 'create_foreign_key', + 'create_unique_constraint', + 'execute'] def alter_column(table_name, column_name, nullable=util.NO_VALUE, diff --git a/alembic/script.py b/alembic/script.py index e6a62213..f9a4f6b4 100644 --- a/alembic/script.py +++ b/alembic/script.py @@ -13,7 +13,7 @@ class ScriptDirectory(object): self.versions = os.path.join(self.dir, 'versions') if not os.access(dir, os.F_OK): - util.err("Path doesn't exist: %r. Please use " + raise util.CommandError("Path doesn't exist: %r. Please use " "the 'init' command to create a new " "scripts folder." % dir) @@ -24,15 +24,17 @@ class ScriptDirectory(object): def _get_rev(self, id_): if id_ == 'head': - return self._current_head() + id_ = self._current_head() elif id_ == 'base': - return None - else: - return id_ + id_ = None + try: + return self._revision_map[id_] + except KeyError: + raise util.CommandError("No such revision %s" % id_) def _revs(self, upper, lower): - lower = self._revision_map[self._get_rev(lower)] - upper = self._revision_map[self._get_rev(upper)] + lower = self._get_rev(lower) + upper = self._get_rev(upper) script = upper while script != lower: yield script diff --git a/alembic/util.py b/alembic/util.py index 5751e204..e2583679 100644 --- a/alembic/util.py +++ b/alembic/util.py @@ -12,6 +12,9 @@ import uuid NO_VALUE = util.symbol("NO_VALUE") +class CommandError(Exception): + pass + try: width = int(os.environ['COLUMNS']) except (KeyError, ValueError): diff --git a/scripts/alembic b/scripts/alembic index 191cc29d..5a76304c 100755 --- a/scripts/alembic +++ b/scripts/alembic @@ -1,6 +1,6 @@ #!/usr/bin/env python -from alembic import main +from alembic.config import main import sys if __name__ == "__main__":