Exemplo n.º 1
0
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
Exemplo n.º 2
0
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"
    )
Exemplo n.º 3
0
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')
Exemplo n.º 4
0
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)
Exemplo n.º 5
0
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')
Exemplo n.º 6
0
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)