def create_with_editor(config, directory, migration_source): editor = utils.get_editor(config) tmpfile = NamedTemporaryFile(dir=directory, prefix=tempfile_prefix, suffix=".py", delete=False) try: with io.open(tmpfile.name, "w", encoding="UTF-8") as f: f.write(migration_source) editor = [part.format(tmpfile.name) for part in shlex.split(editor)] if not any(tmpfile.name in part for part in editor): editor.append(tmpfile.name) mtime = stat(tmpfile.name).st_mtime sys.path.insert(0, directory) while True: try: subprocess.call(editor) except OSError: print("Error: could not open editor!") else: if stat(tmpfile.name).st_mtime == mtime: print("abort: no changes made") return None try: migration = Migration(None, tmpfile.name) migration.load() message = migration.ns["__doc__"] break except Exception: message = "" print("Error loading migration") print(traceback.format_exc()) print() r = utils.prompt("Retry editing?", "Ynq?") if r == "q": return None elif r == "y": continue elif r == "n": break elif r == "?": print("") print("y: reopen the migration file in your editor") print("n: save the migration as-is, without re-editing") print("q: quit without saving the migration") print("") print("?: show this help") continue sys.path = sys.path[1:] filename = make_filename(config, directory, message) rename(tmpfile.name, filename) return filename finally: try: unlink(tmpfile.name) except OSError: pass
def prompt_migrations(backend, migrations, direction): """ Iterate through the list of migrations and prompt the user to apply/rollback each. Return a list of user selected migrations. direction one of 'apply' or 'rollback' """ class prompted_migration(object): def __init__(self, migration, default=None): super(prompted_migration, self).__init__() self.migration = migration self.choice = default to_prompt = [prompted_migration(m) for m in migrations] position = 0 while position < len(to_prompt): mig = to_prompt[position] choice = mig.choice if choice is None: is_applied = backend.is_applied(mig.migration) if direction == "apply": choice = "n" if is_applied else "y" else: choice = "y" if is_applied else "n" options = "".join( o.upper() if o == choice else o.lower() for o in "ynvdaqjk?" ) print("") print("[%s]" % (mig.migration.id,)) response = utils.prompt( "Shall I %s this migration?" % (direction,), options ) if response == "?": print("") print("y: %s this migration" % (direction,)) print("n: don't %s it" % (direction,)) print("") print("v: view this migration in full") print("") print( "d: %s the selected migrations, skipping any remaining" % (direction,) ) print("a: %s all the remaining migrations" % (direction,)) print("q: cancel without making any changes") print("") print("j: skip to next migration") print("k: back up to previous migration") print("") print("?: show this help") continue if response in "yn": mig.choice = response position += 1 continue if response == "v": print(mig.migration.source) continue if response == "j": position = min(len(to_prompt), position + 1) continue if response == "k": position = max(0, position - 1) if response == "d": break if response == "a": for mig in to_prompt[position:]: mig.choice = "y" break if response == "q": for mig in to_prompt: mig.choice = "n" break return migrations.replace( m.migration for m in to_prompt if m.choice == "y" )
def prompt_migrations(conn, paramstyle, migrations, direction): """ Iterate through the list of migrations and prompt the user to apply/rollback each. Return a list of user selected migrations. direction one of 'apply' or 'rollback' """ migrations = migrations.replace(prompted_migration(m) for m in migrations) position = 0 while position < len(migrations): mig = migrations[position] choice = mig.choice if choice is None: isapplied = mig.migration.isapplied(conn, paramstyle, migrations.migration_table) if direction == 'apply': choice = 'n' if isapplied else 'y' else: choice = 'y' if isapplied else 'n' options = ''.join(o.upper() if o == choice else o.lower() for o in 'ynvdaqjk?') print("") print('[%s]' % (mig.migration.id, )) response = prompt("Shall I %s this migration?" % (direction, ), options) if response == '?': print("") print("y: %s this migration" % (direction, )) print("n: don't %s it" % (direction, )) print("") print("v: view this migration in full") print("") print("d: %s the selected migrations, skipping any remaining" % (direction, )) print("a: %s all the remaining migrations" % (direction, )) print("q: cancel without making any changes") print("") print("j: skip to next migration") print("k: back up to previous migration") print("") print("?: show this help") continue if response in 'yn': mig.choice = response position += 1 continue if response == 'v': print(mig.migration.source) continue if response == 'j': position = min(len(migrations), position + 1) continue if response == 'k': position = max(0, position - 1) if response == 'd': break if response == 'a': for mig in migrations[position:]: mig.choice = 'y' break if response == 'q': for mig in migrations: mig.choice = 'n' break return migrations.replace(m.migration for m in migrations if m.choice == 'y')
def main(argv=None): argparser = make_argparser() args = argparser.parse_args(argv) if args.verbosity_level: verbosity_level = args.verbosity_level else: verbosity_level = args.verbose verbosity_level = min(verbosity_level, max(verbosity_levels)) verbosity_level = max(verbosity_level, min(verbosity_levels)) configure_logging(verbosity_level) command = args.command migrations_dir = os.path.normpath(os.path.abspath(args.migrations_dir)) dburi = args.database config_path = os.path.join(migrations_dir, '.yoyo-migrate') config = readconfig(config_path) if dburi is None and args.cache: try: logger.debug("Looking up connection string for %r", migrations_dir) dburi = config.get('DEFAULT', 'dburi') except (ValueError, NoSectionError, NoOptionError): pass if args.migration_table: migration_table = args.migration_table else: try: migration_table = config.get('DEFAULT', 'migration_table') except (ValueError, NoSectionError, NoOptionError): migration_table = None # Earlier versions had a bug where the migration_table could be set to the # string 'None'. if migration_table in (None, 'None'): migration_table = default_migration_table config.set('DEFAULT', 'migration_table', migration_table) if dburi is None: argparser.error("Please specify command, migrations directory and " "database connection string arguments") if args.prompt_password: password = getpass('Password for %s: ' % dburi) scheme, username, _, host, port, database, db_params = parse_uri(dburi) dburi = unparse_uri( (scheme, username, password, host, port, database, db_params)) # Cache the database this migration set is applied to so that subsequent # runs don't need the dburi argument. Don't cache anything in batch mode - # we can't prompt to find the user's preference. if args.cache and not args.batch: if not config.has_option('DEFAULT', 'dburi'): response = prompt( "Save connection string to %s for future migrations?\n" "This is saved in plain text and " "contains your database password." % (config_path, ), "yn") if response == 'y': config.set('DEFAULT', 'dburi', dburi) elif config.get('DEFAULT', 'dburi') != dburi: response = prompt( "Specified connection string differs from that saved in %s. " "Update saved connection string?" % (config_path, ), "yn") if response == 'y': config.set('DEFAULT', 'dburi', dburi) config.set('DEFAULT', 'migration_table', migration_table) saveconfig(config, config_path) conn, paramstyle = connect(dburi) migrations = read_migrations(conn, paramstyle, migrations_dir, migration_table=migration_table) if args.match: migrations = migrations.filter( lambda m: re.search(args.match, m.id) is not None) if not args.all: if command in ['apply']: migrations = migrations.to_apply() elif command in ['reapply', 'rollback']: migrations = migrations.to_rollback() if not args.batch: migrations = prompt_migrations(conn, paramstyle, migrations, command) if not args.batch and migrations: if prompt( command.title() + plural(len(migrations), " %d migration", " %d migrations") + " to %s?" % dburi, "Yn") != 'y': return 0 if command == 'reapply': migrations.rollback(args.force) migrations.apply(args.force) elif command == 'apply': migrations.apply(args.force) elif command == 'rollback': migrations.rollback(args.force)
def prompt_migrations(conn, paramstyle, migrations, direction): """ Iterate through the list of migrations and prompt the user to apply/rollback each. Return a list of user selected migrations. direction one of 'apply' or 'rollback' """ migrations = migrations.replace(prompted_migration(m) for m in migrations) position = 0 while position < len(migrations): mig = migrations[position] choice = mig.choice if choice is None: isapplied = mig.migration.isapplied(conn, paramstyle, migrations.migration_table) if direction == 'apply': choice = 'n' if isapplied else 'y' else: choice = 'y' if isapplied else 'n' options = ''.join(o.upper() if o == choice else o.lower() for o in 'ynvdaqjk?') print("") print('[%s]' % (mig.migration.id,)) response = prompt("Shall I %s this migration?" % (direction,), options) if response == '?': print("") print("y: %s this migration" % (direction,)) print("n: don't %s it" % (direction,)) print("") print("v: view this migration in full") print("") print("d: %s the selected migrations, skipping any remaining" % (direction,)) print("a: %s all the remaining migrations" % (direction,)) print("q: cancel without making any changes") print("") print("j: skip to next migration") print("k: back up to previous migration") print("") print("?: show this help") continue if response in 'yn': mig.choice = response position += 1 continue if response == 'v': print(mig.migration.source) continue if response == 'j': position = min(len(migrations), position + 1) continue if response == 'k': position = max(0, position - 1) if response == 'd': break if response == 'a': for mig in migrations[position:]: mig.choice = 'y' break if response == 'q': for mig in migrations: mig.choice = 'n' break return migrations.replace(m.migration for m in migrations if m.choice == 'y')
def main(argv=None): argparser = make_argparser() args = argparser.parse_args(argv) if args.verbosity_level: verbosity_level = args.verbosity_level else: verbosity_level = args.verbose verbosity_level = min(verbosity_level, max(verbosity_levels)) verbosity_level = max(verbosity_level, min(verbosity_levels)) configure_logging(verbosity_level) command = args.command migrations_dir = os.path.normpath(os.path.abspath(args.migrations_dir)) dburi = args.database config_path = os.path.join(migrations_dir, '.yoyo-migrate') config = readconfig(config_path) if dburi is None and args.cache: try: logger.debug("Looking up connection string for %r", migrations_dir) dburi = config.get('DEFAULT', 'dburi') except (ValueError, NoSectionError, NoOptionError): pass if args.migration_table: migration_table = args.migration_table else: try: migration_table = config.get('DEFAULT', 'migration_table') except (ValueError, NoSectionError, NoOptionError): migration_table = None # Earlier versions had a bug where the migration_table could be set to the # string 'None'. if migration_table in (None, 'None'): migration_table = default_migration_table config.set('DEFAULT', 'migration_table', migration_table) if dburi is None: argparser.error( "Please specify command, migrations directory and " "database connection string arguments" ) if args.prompt_password: password = getpass('Password for %s: ' % dburi) scheme, username, _, host, port, database, db_params = parse_uri(dburi) dburi = unparse_uri((scheme, username, password, host, port, database, db_params)) # Cache the database this migration set is applied to so that subsequent # runs don't need the dburi argument. Don't cache anything in batch mode - # we can't prompt to find the user's preference. if args.cache and not args.batch: if not config.has_option('DEFAULT', 'dburi'): response = prompt( "Save connection string to %s for future migrations?\n" "This is saved in plain text and " "contains your database password." % (config_path,), "yn" ) if response == 'y': config.set('DEFAULT', 'dburi', dburi) elif config.get('DEFAULT', 'dburi') != dburi: response = prompt( "Specified connection string differs from that saved in %s. " "Update saved connection string?" % (config_path,), "yn" ) if response == 'y': config.set('DEFAULT', 'dburi', dburi) config.set('DEFAULT', 'migration_table', migration_table) saveconfig(config, config_path) conn, paramstyle = connect(dburi) migrations = read_migrations(conn, paramstyle, migrations_dir, migration_table=migration_table) if args.match: migrations = migrations.filter( lambda m: re.search(args.match, m.id) is not None) if not args.all: if command in ['apply']: migrations = migrations.to_apply() elif command in ['reapply', 'rollback']: migrations = migrations.to_rollback() if not args.batch: migrations = prompt_migrations(conn, paramstyle, migrations, command) if not args.batch and migrations: if prompt(command.title() + plural(len(migrations), " %d migration", " %d migrations") + " to %s?" % dburi, "Yn") != 'y': return 0 if command == 'reapply': migrations.rollback(args.force) migrations.apply(args.force) elif command == 'apply': migrations.apply(args.force) elif command == 'rollback': migrations.rollback(args.force)