diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-04-28 15:47:51 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-04-28 15:47:51 -0400 |
commit | 54835cc4cd9c5ac65db888b5ec801f9fb12e29cd (patch) | |
tree | 709503a00dc53cd33efb7cb4a4c9be4d898555a1 | |
parent | 92e8f58ffc8709f332d29a7bc75dad212605377a (diff) | |
download | alembic-54835cc4cd9c5ac65db888b5ec801f9fb12e29cd.tar.gz |
- pushed the whole command line options thing into a single main() function
- command methods now accept a "config" plus positional + kw arguments,
for easy API calling
- main() provides context sensitive arguments and help for each command
using inspect.getargspec()
-rw-r--r-- | alembic/__init__.py | 80 | ||||
-rw-r--r-- | alembic/command.py | 59 | ||||
-rw-r--r-- | alembic/config.py | 34 | ||||
-rw-r--r-- | alembic/options.py | 91 | ||||
-rw-r--r-- | alembic/script.py | 10 | ||||
-rw-r--r-- | alembic/util.py | 5 | ||||
-rw-r--r-- | tests/__init__.py | 23 |
7 files changed, 149 insertions, 153 deletions
diff --git a/alembic/__init__.py b/alembic/__init__.py index 649da28..568b000 100644 --- a/alembic/__init__.py +++ b/alembic/__init__.py @@ -1,18 +1,80 @@ -from alembic import options, command +from alembic import config, command, util +from optparse import OptionParser +import inspect +import os __version__ = '0.1alpha' - - def main(argv): - parser = options.get_option_parser() + # TODO: + # OK, what's the super option parser library that + # allows <command> plus command-specfic sub-options, + # and derives everything from callables ? + # we're inventing here a bit. + + commands = dict([ + (fn.__name__, fn) for fn in + [getattr(command, n) for n in dir(command)] + if inspect.isfunction(fn) and + fn.__name__[0] != '_' and + fn.__module__ == 'alembic.command' + ]) + + parser = OptionParser( + "usage: %prog [options] <command> [command arguments]\n\n" + "Available Commands:\n" + + "\n".join(sorted([ + util.format_opt(fn.__name__.replace('_', '-'), fn.__doc__) + for fn in commands.values() + ])) + ) + 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) + + spec = inspect.getargspec(cmd_fn) + if spec[3]: + positional = spec[0][1:-len(spec[3])] + kwarg = spec[0][-len(spec[3]):] + else: + positional = spec[0][1:] + kwarg = [] + + kw = dict( + (k, getattr(cmd_line_options, k)) + for k in kwarg + ) + + if len(cmd_line_args) != len(positional): + util.err("Usage: %s %s [options] %s" % ( + os.path.basename(argv[0]), + cmd, + " ".join(["<%s>" % p for p in positional]) + )) - opt = options.Options(parser, argv) - cmd = opt.get_command().replace('-', '_') - if cmd not in dir(command): - parser.error("no such command %r" % cmd) - getattr(command, cmd)(opt) + cfg = config.Config(cmd_line_options.config) + cmd_fn(cfg, *cmd_line_args, **kw) diff --git a/alembic/command.py b/alembic/command.py index 50452b0..73e1db8 100644 --- a/alembic/command.py +++ b/alembic/command.py @@ -4,13 +4,13 @@ import os import sys import uuid -def list_templates(opts): +def list_templates(config): """List available templates""" print "Available templates:\n" - for tempname in os.listdir(opts.get_template_directory()): + for tempname in os.listdir(config.get_template_directory()): readme = os.path.join( - opts.get_template_directory(), + config.get_template_directory(), tempname, 'README') synopsis = open(readme).next() @@ -19,40 +19,39 @@ def list_templates(opts): print "\nTemplates are used via the 'init' command, e.g.:" print "\n alembic init --template pylons ./scripts" -def init(opts): +def init(config, directory, template='generic'): """Initialize a new scripts directory.""" - dir_, = opts.get_command_args(1, 'alembic init <directory>') - if os.access(dir_, os.F_OK): - opts.err("Directory %s already exists" % dir_) + if os.access(directory, os.F_OK): + util.err("Directory %s already exists" % directory) - util.status("Creating directory %s" % os.path.abspath(dir_), - os.makedirs, dir_) + template_dir = os.path.join(config.get_template_directory(), + template) + if not os.access(template_dir, os.F_OK): + util.err("No such template %r" % template) + + util.status("Creating directory %s" % os.path.abspath(directory), + os.makedirs, directory) - versions = os.path.join(dir_, 'versions') + versions = os.path.join(directory, 'versions') util.status("Creating directory %s" % os.path.abspath(versions), os.makedirs, versions) - script = ScriptDirectory(dir_, opts) + script = ScriptDirectory(directory) - template_dir = os.path.join(opts.get_template_directory(), - opts.cmd_line_options.template) - if not os.access(template_dir, os.F_OK): - opts.err("No such template %r" % opts.cmd_line_options.template) - for file_ in os.listdir(template_dir): if file_ == 'alembic.ini.mako': - config_file = os.path.abspath(opts.cmd_line_options.config) + config_file = os.path.abspath(config.config_file_name) if os.access(config_file, os.F_OK): util.msg("File %s already exists, skipping" % config_file) else: script.generate_template( os.path.join(template_dir, file_), config_file, - script_location=dir_ + script_location=directory ) else: - output_file = os.path.join(dir_, file_) + output_file = os.path.join(directory, file_) script.copy_file( os.path.join(template_dir, file_), output_file @@ -61,34 +60,34 @@ def init(opts): util.msg("Please edit configuration/connection/logging "\ "settings in %r before proceeding." % config_file) -def revision(opts): +def revision(config, message=None): """Create a new revision file.""" - script = ScriptDirectory.from_options(opts) - script.generate_rev(util.rev_id(), opts.cmd_line_options.message) + script = ScriptDirectory.from_config(config) + script.generate_rev(util.rev_id(), message) -def upgrade(opts): +def upgrade(config): """Upgrade to the latest version.""" - script = ScriptDirectory.from_options(opts) + script = ScriptDirectory.from_config(config) # ... -def revert(opts): +def revert(config): """Revert to a specific previous version.""" - script = ScriptDirectory.from_options(opts) + script = ScriptDirectory.from_config(config) # ... -def history(opts): +def history(config): """List changeset scripts in chronological order.""" - script = ScriptDirectory.from_options(opts) + script = ScriptDirectory.from_config(config) -def splice(opts): +def splice(config): """'splice' two branches, creating a new revision file.""" -def branches(opts): +def branches(config): """Show current un-spliced branch points"""
\ No newline at end of file diff --git a/alembic/config.py b/alembic/config.py new file mode 100644 index 0000000..b56fc9d --- /dev/null +++ b/alembic/config.py @@ -0,0 +1,34 @@ +import ConfigParser +import inspect +import os +import sys +from alembic import util + +class Config(object): + def __init__(self, file_): + self.config_file_name = file_ + + @util.memoized_property + def file_config(self): + file_config = ConfigParser.ConfigParser() + file_config.read([self.config_file_name]) + return file_config + + def get_template_directory(self): + # TODO: what's the official way to get at + # setuptools-installed datafiles ? + return os.path.join(os.path.dirname(__file__), '..', 'templates') + + def get_section(self, name): + return dict(self.file_config.items(name)) + + def get_main_option(self, name, default=None): + if not self.file_config.has_section('alembic'): + util.err("No config file %r found, or file has no " + "'[alembic]' section" % self.config_file_name) + if self.file_config.get('alembic', name): + return self.file_config.get('alembic', name) + else: + return default + +
\ No newline at end of file diff --git a/alembic/options.py b/alembic/options.py deleted file mode 100644 index f54a1b4..0000000 --- a/alembic/options.py +++ /dev/null @@ -1,91 +0,0 @@ -from optparse import OptionParser -import ConfigParser -import inspect -import os -import sys -from alembic import util - -def get_option_parser(): - from alembic import command - - # TODO: - # OK, what's the super option parser library that - # allows <command> plus command-specfic sub-options ? - # we're inventing here a bit. - - commands = [ - (fn.__name__.replace('_', '-'), fn.__doc__) for fn in - [getattr(command, name) for name in sorted(dir(command))] - if inspect.isfunction(fn) and - fn.__name__[0] != '_' and - fn.__module__ == 'alembic.command' - ] - - parser = OptionParser( - "usage: %prog [options] <command> [command arguments]\n\n" - "Available Commands:\n" + - "\n".join([ - util.format_opt(cmd, hlp) - for cmd, hlp in commands - ]) - ) - 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'") - return parser - -class Options(object): - def __init__(self, parser, argv): - self.parser = parser - self.cmd_line_options, \ - self.cmd_line_args = parser.parse_args(argv[1:]) - if len(self.cmd_line_args) < 1: - self.err("no command specified") - - @util.memoized_property - def file_config(self): - self.config_file_name = self.cmd_line_options.config - file_config = ConfigParser.ConfigParser() - file_config.read([self.config_file_name]) - return file_config - - def get_command(self): - return self.cmd_line_args[0] - - def get_command_args(self, count, err): - if len(self.cmd_line_args[1:]) != count: - self.err( - "Command %r syntax: %r" % - (self.get_command(), err)) - return self.cmd_line_args[1:] - - def get_template_directory(self): - # TODO: what's the official way to get at - # setuptools-installed datafiles ? - return os.path.join(os.path.dirname(__file__), '..', 'templates') - - def get_section(self, name): - return dict(self.file_config.items(name)) - - def err(self, msg): - util.msg(msg) - sys.exit(-1) - - def get_main_option(self, name, default=None): - if not self.file_config.has_section('alembic'): - self.err("No config file %r found, or file has no " - "'[alembic]' section" % self.config_file_name) - if self.file_config.get('alembic', name): - return self.file_config.get('alembic', name) - else: - return default - -
\ No newline at end of file diff --git a/alembic/script.py b/alembic/script.py index b2fded6..d35751b 100644 --- a/alembic/script.py +++ b/alembic/script.py @@ -8,21 +8,19 @@ _rev_file = re.compile(r'([a-z0-9]+)\.py$') _mod_def_re = re.compile(r'(upgrade|downgrade)_([a-z0-9]+)') class ScriptDirectory(object): - def __init__(self, dir, options): + def __init__(self, dir): self.dir = dir self.versions = os.path.join(self.dir, 'versions') if not os.access(dir, os.F_OK): - options.err("Path doesn't exist: %r. Please use " + util.err("Path doesn't exist: %r. Please use " "the 'init' command to create a new " "scripts folder." % dir) - self.options = options @classmethod - def from_options(cls, options): + def from_config(cls, options): return ScriptDirectory( - options.get_main_option('script_location'), - options) + options.get_main_option('script_location')) @util.memoized_property def _revision_map(self): diff --git a/alembic/util.py b/alembic/util.py index 79b1e3b..3e29704 100644 --- a/alembic/util.py +++ b/alembic/util.py @@ -39,6 +39,11 @@ def status(_statmsg, fn, *arg, **kw): sys.stdout.write("FAILED\n") raise +def err(message): + msg(message) + sys.exit(-1) + + def warn(msg): warnings.warn(msg) diff --git a/tests/__init__.py b/tests/__init__.py index 85f79d2..40e7441 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -19,29 +19,18 @@ def assert_compiled(element, assert_string, dialect=None): dialect = _get_dialect(dialect) eq_(unicode(element.compile(dialect=dialect)), assert_string) -def _testing_options(**kw): - from alembic.options import Options, get_option_parser +def _testing_config(**kw): + from alembic.config import Config if not os.access(staging_directory, os.F_OK): os.mkdir(staging_directory) - kw.setdefault( - 'config', - os.path.join(staging_directory, 'test_alembic.ini') - ) - - return Options( - get_option_parser(), - ["./scripts/alembic"] + \ - list(itertools.chain(*[["--%s" % k, "%s" % v] for k, v in kw.items()])) + \ - ["init"] +\ - [os.path.join(staging_directory, 'scripts')] - ) + return Config(os.path.join(staging_directory, 'test_alembic.ini')) def staging_env(create=True): from alembic import command, script - opt = _testing_options() + cfg = _testing_config() if create: - command.init(opt) - return script.ScriptDirectory.from_options(opt) + command.init(cfg, os.path.join(staging_directory, 'scripts')) + return script.ScriptDirectory.from_config(cfg) def clear_staging_env(): shutil.rmtree(staging_directory, True) |