Flask-Migrate-2.5.2/0000755000076500000240000000000013472311554016300 5ustar miguelgrinbergstaff00000000000000Flask-Migrate-2.5.2/Flask_Migrate.egg-info/0000755000076500000240000000000013472311554022502 5ustar miguelgrinbergstaff00000000000000Flask-Migrate-2.5.2/Flask_Migrate.egg-info/PKG-INFO0000644000076500000240000000160113472311554023575 0ustar miguelgrinbergstaff00000000000000Metadata-Version: 1.1 Name: Flask-Migrate Version: 2.5.2 Summary: SQLAlchemy database migrations for Flask applications using Alembic Home-page: http://github.com/miguelgrinberg/flask-migrate/ Author: Miguel Grinberg Author-email: miguelgrinberg50@gmail.com License: MIT Description: Flask-Migrate -------------- SQLAlchemy database migrations for Flask applications using Alembic. Platform: any Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules Flask-Migrate-2.5.2/Flask_Migrate.egg-info/SOURCES.txt0000644000076500000240000000174013472311554024370 0ustar miguelgrinbergstaff00000000000000LICENSE MANIFEST.in README.md __version__ setup.py Flask_Migrate.egg-info/PKG-INFO Flask_Migrate.egg-info/SOURCES.txt Flask_Migrate.egg-info/dependency_links.txt Flask_Migrate.egg-info/entry_points.txt Flask_Migrate.egg-info/not-zip-safe Flask_Migrate.egg-info/requires.txt Flask_Migrate.egg-info/top_level.txt flask_migrate/__init__.py flask_migrate/cli.py flask_migrate/templates/flask/README flask_migrate/templates/flask/alembic.ini.mako flask_migrate/templates/flask/env.py flask_migrate/templates/flask/script.py.mako flask_migrate/templates/flask-multidb/README flask_migrate/templates/flask-multidb/alembic.ini.mako flask_migrate/templates/flask-multidb/env.py flask_migrate/templates/flask-multidb/script.py.mako tests/__init__.py tests/app.db tests/app.py tests/app_compare_type1.py tests/app_compare_type2.py tests/app_custom_directory.py tests/app_multidb.py tests/test_migrate.py tests/test_migrate_flaskcli.py tests/test_multidb_migrate.py tests/test_multidb_migrate_flaskcli.pyFlask-Migrate-2.5.2/Flask_Migrate.egg-info/dependency_links.txt0000644000076500000240000000000113472311554026550 0ustar miguelgrinbergstaff00000000000000 Flask-Migrate-2.5.2/Flask_Migrate.egg-info/entry_points.txt0000644000076500000240000000005413472311554025777 0ustar miguelgrinbergstaff00000000000000[flask.commands] db = flask_migrate.cli:db Flask-Migrate-2.5.2/Flask_Migrate.egg-info/not-zip-safe0000644000076500000240000000000113333657452024736 0ustar miguelgrinbergstaff00000000000000 Flask-Migrate-2.5.2/Flask_Migrate.egg-info/requires.txt0000644000076500000240000000005613472311554025103 0ustar miguelgrinbergstaff00000000000000Flask>=0.9 Flask-SQLAlchemy>=1.0 alembic>=0.7 Flask-Migrate-2.5.2/Flask_Migrate.egg-info/top_level.txt0000644000076500000240000000001613472311554025231 0ustar miguelgrinbergstaff00000000000000flask_migrate Flask-Migrate-2.5.2/LICENSE0000644000076500000240000000207213333657347017317 0ustar miguelgrinbergstaff00000000000000The MIT License (MIT) Copyright (c) 2013 Miguel Grinberg Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Flask-Migrate-2.5.2/MANIFEST.in0000644000076500000240000000017113470464271020040 0ustar miguelgrinbergstaff00000000000000include __version__ README.md LICENSE flask_migrate/templates/flask/* \ flask_migrate/templates/flask-multidb/* tests/* Flask-Migrate-2.5.2/PKG-INFO0000644000076500000240000000160113472311554017373 0ustar miguelgrinbergstaff00000000000000Metadata-Version: 1.1 Name: Flask-Migrate Version: 2.5.2 Summary: SQLAlchemy database migrations for Flask applications using Alembic Home-page: http://github.com/miguelgrinberg/flask-migrate/ Author: Miguel Grinberg Author-email: miguelgrinberg50@gmail.com License: MIT Description: Flask-Migrate -------------- SQLAlchemy database migrations for Flask applications using Alembic. Platform: any Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules Flask-Migrate-2.5.2/README.md0000644000076500000240000000441613472303616017564 0ustar miguelgrinbergstaff00000000000000Flask-Migrate ============= [![Build Status](https://travis-ci.org/miguelgrinberg/Flask-Migrate.png?branch=master)](https://travis-ci.org/miguelgrinberg/Flask-Migrate) Flask-Migrate is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic. The database operations are provided as command-line arguments under the `flask db` command. Installation ------------ Install Flask-Migrate with `pip`: pip install Flask-Migrate Example ------- This is an example application that handles database migrations through Flask-Migrate: ```python from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' db = SQLAlchemy(app) migrate = Migrate(app, db) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) ``` With the above application you can create the database or enable migrations if the database already exists with the following command: $ flask db init Note that the `FLASK_APP` environment variable must be set according to the Flask documentation for this command to work. This will add a `migrations` folder to your application. The contents of this folder need to be added to version control along with your other source files. You can then generate an initial migration: $ flask db migrate The migration script needs to be reviewed and edited, as Alembic currently does not detect every change you make to your models. In particular, Alembic is currently unable to detect indexes. Once finalized, the migration script also needs to be added to version control. Then you can apply the migration to the database: $ flask db upgrade Then each time the database models change repeat the `migrate` and `upgrade` commands. To sync the database in another system just refresh the `migrations` folder from source control and run the `upgrade` command. To see all the commands that are available run this command: $ flask db --help Resources --------- - [Documentation](http://flask-migrate.readthedocs.io/en/latest/) - [pypi](https://pypi.python.org/pypi/Flask-Migrate) - [Change Log](https://github.com/miguelgrinberg/Flask-Migrate/blob/master/CHANGES.md) Flask-Migrate-2.5.2/__version__0000644000076500000240000000000613472311544020477 0ustar miguelgrinbergstaff000000000000002.5.2 Flask-Migrate-2.5.2/flask_migrate/0000755000076500000240000000000013472311554021110 5ustar miguelgrinbergstaff00000000000000Flask-Migrate-2.5.2/flask_migrate/__init__.py0000755000076500000240000004555413355631637023250 0ustar miguelgrinbergstaff00000000000000import argparse from functools import wraps import logging import os import sys from flask import current_app try: from flask_script import Manager except ImportError: Manager = None from alembic import __version__ as __alembic_version__ from alembic.config import Config as AlembicConfig from alembic import command from alembic.util import CommandError alembic_version = tuple([int(v) for v in __alembic_version__.split('.')[0:3]]) log = logging.getLogger() class _MigrateConfig(object): def __init__(self, migrate, db, **kwargs): self.migrate = migrate self.db = db self.directory = migrate.directory self.configure_args = kwargs @property def metadata(self): """ Backwards compatibility, in old releases app.extensions['migrate'] was set to db, and env.py accessed app.extensions['migrate'].metadata """ return self.db.metadata class Config(AlembicConfig): def get_template_directory(self): package_dir = os.path.abspath(os.path.dirname(__file__)) return os.path.join(package_dir, 'templates') class Migrate(object): def __init__(self, app=None, db=None, directory='migrations', **kwargs): self.configure_callbacks = [] self.db = db self.directory = directory self.alembic_ctx_kwargs = kwargs if app is not None and db is not None: self.init_app(app, db, directory) def init_app(self, app, db=None, directory=None, **kwargs): self.db = db or self.db self.directory = directory or self.directory self.alembic_ctx_kwargs.update(kwargs) if not hasattr(app, 'extensions'): app.extensions = {} app.extensions['migrate'] = _MigrateConfig(self, self.db, **self.alembic_ctx_kwargs) def configure(self, f): self.configure_callbacks.append(f) return f def call_configure_callbacks(self, config): for f in self.configure_callbacks: config = f(config) return config def get_config(self, directory=None, x_arg=None, opts=None): if directory is None: directory = self.directory config = Config(os.path.join(directory, 'alembic.ini')) config.set_main_option('script_location', directory) if config.cmd_opts is None: config.cmd_opts = argparse.Namespace() for opt in opts or []: setattr(config.cmd_opts, opt, True) if not hasattr(config.cmd_opts, 'x'): if x_arg is not None: setattr(config.cmd_opts, 'x', []) if isinstance(x_arg, list) or isinstance(x_arg, tuple): for x in x_arg: config.cmd_opts.x.append(x) else: config.cmd_opts.x.append(x_arg) else: setattr(config.cmd_opts, 'x', None) return self.call_configure_callbacks(config) def catch_errors(f): @wraps(f) def wrapped(*args, **kwargs): try: f(*args, **kwargs) except (CommandError, RuntimeError) as exc: log.error('Error: ' + str(exc)) sys.exit(1) return wrapped if Manager is not None: MigrateCommand = Manager(usage='Perform database migrations') else: class FakeCommand(object): def option(self, *args, **kwargs): def decorator(f): return f return decorator MigrateCommand = FakeCommand() @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @MigrateCommand.option('--multidb', dest='multidb', action='store_true', default=False, help=("Multiple databases migraton (default is " "False)")) @catch_errors def init(directory=None, multidb=False): """Creates a new migration repository""" if directory is None: directory = current_app.extensions['migrate'].directory config = Config() config.set_main_option('script_location', directory) config.config_file_name = os.path.join(directory, 'alembic.ini') config = current_app.extensions['migrate'].\ migrate.call_configure_callbacks(config) if multidb: command.init(config, directory, 'flask-multidb') else: command.init(config, directory, 'flask') @MigrateCommand.option('--rev-id', dest='rev_id', default=None, help=('Specify a hardcoded revision id instead of ' 'generating one')) @MigrateCommand.option('--version-path', dest='version_path', default=None, help=('Specify specific path from config for version ' 'file')) @MigrateCommand.option('--branch-label', dest='branch_label', default=None, help=('Specify a branch label to apply to the new ' 'revision')) @MigrateCommand.option('--splice', dest='splice', action='store_true', default=False, help=('Allow a non-head revision as the "head" to ' 'splice onto')) @MigrateCommand.option('--head', dest='head', default='head', help=('Specify head revision or @head to ' 'base new revision on')) @MigrateCommand.option('--sql', dest='sql', action='store_true', default=False, help=("Don't emit SQL to database - dump to standard " "output instead")) @MigrateCommand.option('--autogenerate', dest='autogenerate', action='store_true', default=False, help=('Populate revision script with candidate ' 'migration operations, based on comparison of ' 'database to model')) @MigrateCommand.option('-m', '--message', dest='message', default=None, help='Revision message') @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @catch_errors def revision(directory=None, message=None, autogenerate=False, sql=False, head='head', splice=False, branch_label=None, version_path=None, rev_id=None): """Create a new revision file.""" config = current_app.extensions['migrate'].migrate.get_config(directory) if alembic_version >= (0, 7, 0): command.revision(config, message, autogenerate=autogenerate, sql=sql, head=head, splice=splice, branch_label=branch_label, version_path=version_path, rev_id=rev_id) else: command.revision(config, message, autogenerate=autogenerate, sql=sql) @MigrateCommand.option('--rev-id', dest='rev_id', default=None, help=('Specify a hardcoded revision id instead of ' 'generating one')) @MigrateCommand.option('--version-path', dest='version_path', default=None, help=('Specify specific path from config for version ' 'file')) @MigrateCommand.option('--branch-label', dest='branch_label', default=None, help=('Specify a branch label to apply to the new ' 'revision')) @MigrateCommand.option('--splice', dest='splice', action='store_true', default=False, help=('Allow a non-head revision as the "head" to ' 'splice onto')) @MigrateCommand.option('--head', dest='head', default='head', help=('Specify head revision or @head to ' 'base new revision on')) @MigrateCommand.option('--sql', dest='sql', action='store_true', default=False, help=("Don't emit SQL to database - dump to standard " "output instead")) @MigrateCommand.option('-m', '--message', dest='message', default=None) @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @MigrateCommand.option('-x', '--x-arg', dest='x_arg', default=None, action='append', help=("Additional arguments consumed " "by custom env.py scripts")) @catch_errors def migrate(directory=None, message=None, sql=False, head='head', splice=False, branch_label=None, version_path=None, rev_id=None, x_arg=None): """Alias for 'revision --autogenerate'""" config = current_app.extensions['migrate'].migrate.get_config( directory, opts=['autogenerate'], x_arg=x_arg) if alembic_version >= (0, 7, 0): command.revision(config, message, autogenerate=True, sql=sql, head=head, splice=splice, branch_label=branch_label, version_path=version_path, rev_id=rev_id) else: command.revision(config, message, autogenerate=True, sql=sql) @MigrateCommand.option('revision', nargs='?', default='head', help="revision identifier") @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @catch_errors def edit(directory=None, revision='current'): """Edit current revision.""" if alembic_version >= (0, 8, 0): config = current_app.extensions['migrate'].migrate.get_config( directory) command.edit(config, revision) else: raise RuntimeError('Alembic 0.8.0 or greater is required') @MigrateCommand.option('--rev-id', dest='rev_id', default=None, help=('Specify a hardcoded revision id instead of ' 'generating one')) @MigrateCommand.option('--branch-label', dest='branch_label', default=None, help=('Specify a branch label to apply to the new ' 'revision')) @MigrateCommand.option('-m', '--message', dest='message', default=None) @MigrateCommand.option('revisions', nargs='+', help='one or more revisions, or "heads" for all heads') @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @catch_errors def merge(directory=None, revisions='', message=None, branch_label=None, rev_id=None): """Merge two revisions together. Creates a new migration file""" if alembic_version >= (0, 7, 0): config = current_app.extensions['migrate'].migrate.get_config( directory) command.merge(config, revisions, message=message, branch_label=branch_label, rev_id=rev_id) else: raise RuntimeError('Alembic 0.7.0 or greater is required') @MigrateCommand.option('--tag', dest='tag', default=None, help=("Arbitrary 'tag' name - can be used by custom " "env.py scripts")) @MigrateCommand.option('--sql', dest='sql', action='store_true', default=False, help=("Don't emit SQL to database - dump to standard " "output instead")) @MigrateCommand.option('revision', nargs='?', default='head', help="revision identifier") @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @MigrateCommand.option('-x', '--x-arg', dest='x_arg', default=None, action='append', help=("Additional arguments consumed " "by custom env.py scripts")) @catch_errors def upgrade(directory=None, revision='head', sql=False, tag=None, x_arg=None): """Upgrade to a later version""" config = current_app.extensions['migrate'].migrate.get_config(directory, x_arg=x_arg) command.upgrade(config, revision, sql=sql, tag=tag) @MigrateCommand.option('--tag', dest='tag', default=None, help=("Arbitrary 'tag' name - can be used by custom " "env.py scripts")) @MigrateCommand.option('--sql', dest='sql', action='store_true', default=False, help=("Don't emit SQL to database - dump to standard " "output instead")) @MigrateCommand.option('revision', nargs='?', default="-1", help="revision identifier") @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @MigrateCommand.option('-x', '--x-arg', dest='x_arg', default=None, action='append', help=("Additional arguments consumed " "by custom env.py scripts")) @catch_errors def downgrade(directory=None, revision='-1', sql=False, tag=None, x_arg=None): """Revert to a previous version""" config = current_app.extensions['migrate'].migrate.get_config(directory, x_arg=x_arg) if sql and revision == '-1': revision = 'head:-1' command.downgrade(config, revision, sql=sql, tag=tag) @MigrateCommand.option('revision', nargs='?', default="head", help="revision identifier") @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @catch_errors def show(directory=None, revision='head'): """Show the revision denoted by the given symbol.""" if alembic_version >= (0, 7, 0): config = current_app.extensions['migrate'].migrate.get_config( directory) command.show(config, revision) else: raise RuntimeError('Alembic 0.7.0 or greater is required') @MigrateCommand.option('-i', '--indicate-current', dest='indicate_current', action='store_true', default=False, help='Indicate current version (Alembic 0.9.9 or greater is required)') @MigrateCommand.option('-v', '--verbose', dest='verbose', action='store_true', default=False, help='Use more verbose output') @MigrateCommand.option('-r', '--rev-range', dest='rev_range', default=None, help='Specify a revision range; format is [start]:[end]') @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @catch_errors def history(directory=None, rev_range=None, verbose=False, indicate_current=False): """List changeset scripts in chronological order.""" config = current_app.extensions['migrate'].migrate.get_config(directory) if alembic_version >= (0, 9, 9): command.history(config, rev_range, verbose=verbose, indicate_current=indicate_current) elif alembic_version >= (0, 7, 0): command.history(config, rev_range, verbose=verbose) else: command.history(config, rev_range) @MigrateCommand.option('--resolve-dependencies', dest='resolve_dependencies', action='store_true', default=False, help='Treat dependency versions as down revisions') @MigrateCommand.option('-v', '--verbose', dest='verbose', action='store_true', default=False, help='Use more verbose output') @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @catch_errors def heads(directory=None, verbose=False, resolve_dependencies=False): """Show current available heads in the script directory""" if alembic_version >= (0, 7, 0): config = current_app.extensions['migrate'].migrate.get_config( directory) command.heads(config, verbose=verbose, resolve_dependencies=resolve_dependencies) else: raise RuntimeError('Alembic 0.7.0 or greater is required') @MigrateCommand.option('-v', '--verbose', dest='verbose', action='store_true', default=False, help='Use more verbose output') @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @catch_errors def branches(directory=None, verbose=False): """Show current branch points""" config = current_app.extensions['migrate'].migrate.get_config(directory) if alembic_version >= (0, 7, 0): command.branches(config, verbose=verbose) else: command.branches(config) @MigrateCommand.option('--head-only', dest='head_only', action='store_true', default=False, help='Deprecated. Use --verbose for additional output') @MigrateCommand.option('-v', '--verbose', dest='verbose', action='store_true', default=False, help='Use more verbose output') @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @catch_errors def current(directory=None, verbose=False, head_only=False): """Display the current revision for each database.""" config = current_app.extensions['migrate'].migrate.get_config(directory) if alembic_version >= (0, 7, 0): command.current(config, verbose=verbose, head_only=head_only) else: command.current(config) @MigrateCommand.option('--tag', dest='tag', default=None, help=("Arbitrary 'tag' name - can be used by custom " "env.py scripts")) @MigrateCommand.option('--sql', dest='sql', action='store_true', default=False, help=("Don't emit SQL to database - dump to standard " "output instead")) @MigrateCommand.option('revision', default=None, help="revision identifier") @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("migration script directory (default is " "'migrations')")) @catch_errors def stamp(directory=None, revision='head', sql=False, tag=None): """'stamp' the revision table with the given revision; don't run any migrations""" config = current_app.extensions['migrate'].migrate.get_config(directory) command.stamp(config, revision, sql=sql, tag=tag) Flask-Migrate-2.5.2/flask_migrate/cli.py0000644000076500000240000002257613355630627022252 0ustar miguelgrinbergstaff00000000000000import click from flask.cli import with_appcontext from flask_migrate import init as _init from flask_migrate import revision as _revision from flask_migrate import migrate as _migrate from flask_migrate import edit as _edit from flask_migrate import merge as _merge from flask_migrate import upgrade as _upgrade from flask_migrate import downgrade as _downgrade from flask_migrate import show as _show from flask_migrate import history as _history from flask_migrate import heads as _heads from flask_migrate import branches as _branches from flask_migrate import current as _current from flask_migrate import stamp as _stamp @click.group() def db(): """Perform database migrations.""" pass @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.option('--multidb', is_flag=True, help=('Support multiple databases')) @with_appcontext def init(directory, multidb): """Creates a new migration repository.""" _init(directory, multidb) @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.option('-m', '--message', default=None, help='Revision message') @click.option('--autogenerate', is_flag=True, help=('Populate revision script with candidate migration ' 'operations, based on comparison of database to model')) @click.option('--sql', is_flag=True, help=('Don\'t emit SQL to database - dump to standard output ' 'instead')) @click.option('--head', default='head', help=('Specify head revision or @head to base new ' 'revision on')) @click.option('--splice', is_flag=True, help=('Allow a non-head revision as the "head" to splice onto')) @click.option('--branch-label', default=None, help=('Specify a branch label to apply to the new revision')) @click.option('--version-path', default=None, help=('Specify specific path from config for version file')) @click.option('--rev-id', default=None, help=('Specify a hardcoded revision id instead of generating ' 'one')) @with_appcontext def revision(directory, message, autogenerate, sql, head, splice, branch_label, version_path, rev_id): """Create a new revision file.""" _revision(directory, message, autogenerate, sql, head, splice, branch_label, version_path, rev_id) @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.option('-m', '--message', default=None, help='Revision message') @click.option('--sql', is_flag=True, help=('Don\'t emit SQL to database - dump to standard output ' 'instead')) @click.option('--head', default='head', help=('Specify head revision or @head to base new ' 'revision on')) @click.option('--splice', is_flag=True, help=('Allow a non-head revision as the "head" to splice onto')) @click.option('--branch-label', default=None, help=('Specify a branch label to apply to the new revision')) @click.option('--version-path', default=None, help=('Specify specific path from config for version file')) @click.option('--rev-id', default=None, help=('Specify a hardcoded revision id instead of generating ' 'one')) @click.option('-x', '--x-arg', multiple=True, help='Additional arguments consumed by custom env.py scripts') @with_appcontext def migrate(directory, message, sql, head, splice, branch_label, version_path, rev_id, x_arg): """Autogenerate a new revision file (Alias for 'revision --autogenerate')""" _migrate(directory, message, sql, head, splice, branch_label, version_path, rev_id, x_arg) @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.argument('revision', default='head') @with_appcontext def edit(directory, revision): """Edit a revision file""" _edit(directory, revision) @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.option('-m', '--message', default=None, help='Merge revision message') @click.option('--branch-label', default=None, help=('Specify a branch label to apply to the new revision')) @click.option('--rev-id', default=None, help=('Specify a hardcoded revision id instead of generating ' 'one')) @click.argument('revisions', nargs=-1) @with_appcontext def merge(directory, message, branch_label, rev_id, revisions): """Merge two revisions together, creating a new revision file""" _merge(directory, revisions, message, branch_label, rev_id) @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.option('--sql', is_flag=True, help=('Don\'t emit SQL to database - dump to standard output ' 'instead')) @click.option('--tag', default=None, help=('Arbitrary "tag" name - can be used by custom "env.py ' 'scripts')) @click.option('-x', '--x-arg', multiple=True, help='Additional arguments consumed by custom env.py scripts') @click.argument('revision', default='head') @with_appcontext def upgrade(directory, sql, tag, x_arg, revision): """Upgrade to a later version""" _upgrade(directory, revision, sql, tag, x_arg) @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.option('--sql', is_flag=True, help=('Don\'t emit SQL to database - dump to standard output ' 'instead')) @click.option('--tag', default=None, help=('Arbitrary "tag" name - can be used by custom "env.py ' 'scripts')) @click.option('-x', '--x-arg', multiple=True, help='Additional arguments consumed by custom env.py scripts') @click.argument('revision', default='-1') @with_appcontext def downgrade(directory, sql, tag, x_arg, revision): """Revert to a previous version""" _downgrade(directory, revision, sql, tag, x_arg) @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.argument('revision', default='head') @with_appcontext def show(directory, revision): """Show the revision denoted by the given symbol.""" _show(directory, revision) @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.option('-r', '--rev-range', default=None, help='Specify a revision range; format is [start]:[end]') @click.option('-v', '--verbose', is_flag=True, help='Use more verbose output') @click.option('-i', '--indicate-current', is_flag=True, help='Indicate current version (Alembic 0.9.9 or greater is required)') @with_appcontext def history(directory, rev_range, verbose, indicate_current): """List changeset scripts in chronological order.""" _history(directory, rev_range, verbose, indicate_current) @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.option('-v', '--verbose', is_flag=True, help='Use more verbose output') @click.option('--resolve-dependencies', is_flag=True, help='Treat dependency versions as down revisions') @with_appcontext def heads(directory, verbose, resolve_dependencies): """Show current available heads in the script directory""" _heads(directory, verbose, resolve_dependencies) @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.option('-v', '--verbose', is_flag=True, help='Use more verbose output') @with_appcontext def branches(directory, verbose): """Show current branch points""" _branches(directory, verbose) @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.option('-v', '--verbose', is_flag=True, help='Use more verbose output') @click.option('--head-only', is_flag=True, help='Deprecated. Use --verbose for additional output') @with_appcontext def current(directory, verbose, head_only): """Display the current revision for each database.""" _current(directory, verbose, head_only) @db.command() @click.option('-d', '--directory', default=None, help=('migration script directory (default is "migrations")')) @click.option('--sql', is_flag=True, help=('Don\'t emit SQL to database - dump to standard output ' 'instead')) @click.option('--tag', default=None, help=('Arbitrary "tag" name - can be used by custom "env.py ' 'scripts')) @click.argument('revision', default='head') @with_appcontext def stamp(directory, sql, tag, revision): """'stamp' the revision table with the given revision; don't run any migrations""" _stamp(directory, revision, sql, tag) Flask-Migrate-2.5.2/flask_migrate/templates/0000755000076500000240000000000013472311554023106 5ustar miguelgrinbergstaff00000000000000Flask-Migrate-2.5.2/flask_migrate/templates/flask/0000755000076500000240000000000013472311554024206 5ustar miguelgrinbergstaff00000000000000Flask-Migrate-2.5.2/flask_migrate/templates/flask/README0000644000076500000240000000004613333657347025077 0ustar miguelgrinbergstaff00000000000000Generic single-database configuration.Flask-Migrate-2.5.2/flask_migrate/templates/flask/alembic.ini.mako0000644000076500000240000000140213333657347027237 0ustar miguelgrinbergstaff00000000000000# A generic, single database configuration. [alembic] # template used to generate migration files # file_template = %%(rev)s_%%(slug)s # set to 'true' to run the environment during # the 'revision' command, regardless of autogenerate # revision_environment = false # Logging configuration [loggers] keys = root,sqlalchemy,alembic [handlers] keys = console [formatters] keys = generic [logger_root] level = WARN handlers = console qualname = [logger_sqlalchemy] level = WARN handlers = qualname = sqlalchemy.engine [logger_alembic] level = INFO handlers = qualname = alembic [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(levelname)-5.5s [%(name)s] %(message)s datefmt = %H:%M:%S Flask-Migrate-2.5.2/flask_migrate/templates/flask/env.py0000644000076500000240000000554613470271071025357 0ustar miguelgrinbergstaff00000000000000from __future__ import with_statement import logging from logging.config import fileConfig from sqlalchemy import engine_from_config from sqlalchemy import pool from alembic import context # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config # Interpret the config file for Python logging. # This line sets up loggers basically. fileConfig(config.config_file_name) logger = logging.getLogger('alembic.env') # add your model's MetaData object here # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata from flask import current_app config.set_main_option( 'sqlalchemy.url', current_app.config.get( 'SQLALCHEMY_DATABASE_URI').replace('%', '%%')) target_metadata = current_app.extensions['migrate'].db.metadata # other values from the config, defined by the needs of env.py, # can be acquired: # my_important_option = config.get_main_option("my_important_option") # ... etc. def run_migrations_offline(): """Run migrations in 'offline' mode. This configures the context with just a URL and not an Engine, though an Engine is acceptable here as well. By skipping the Engine creation we don't even need a DBAPI to be available. Calls to context.execute() here emit the given string to the script output. """ url = config.get_main_option("sqlalchemy.url") context.configure( url=url, target_metadata=target_metadata, literal_binds=True ) with context.begin_transaction(): context.run_migrations() def run_migrations_online(): """Run migrations in 'online' mode. In this scenario we need to create an Engine and associate a connection with the context. """ # this callback is used to prevent an auto-migration from being generated # when there are no changes to the schema # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html def process_revision_directives(context, revision, directives): if getattr(config.cmd_opts, 'autogenerate', False): script = directives[0] if script.upgrade_ops.is_empty(): directives[:] = [] logger.info('No changes in schema detected.') connectable = engine_from_config( config.get_section(config.config_ini_section), prefix='sqlalchemy.', poolclass=pool.NullPool, ) with connectable.connect() as connection: context.configure( connection=connection, target_metadata=target_metadata, process_revision_directives=process_revision_directives, **current_app.extensions['migrate'].configure_args ) with context.begin_transaction(): context.run_migrations() if context.is_offline_mode(): run_migrations_offline() else: run_migrations_online() Flask-Migrate-2.5.2/flask_migrate/templates/flask/script.py.mako0000644000076500000240000000075613333657347027033 0ustar miguelgrinbergstaff00000000000000"""${message} Revision ID: ${up_revision} Revises: ${down_revision | comma,n} Create Date: ${create_date} """ from alembic import op import sqlalchemy as sa ${imports if imports else ""} # revision identifiers, used by Alembic. revision = ${repr(up_revision)} down_revision = ${repr(down_revision)} branch_labels = ${repr(branch_labels)} depends_on = ${repr(depends_on)} def upgrade(): ${upgrades if upgrades else "pass"} def downgrade(): ${downgrades if downgrades else "pass"} Flask-Migrate-2.5.2/flask_migrate/templates/flask-multidb/0000755000076500000240000000000013472311554025644 5ustar miguelgrinbergstaff00000000000000Flask-Migrate-2.5.2/flask_migrate/templates/flask-multidb/README0000644000076500000240000000004613333657347026535 0ustar miguelgrinbergstaff00000000000000Generic single-database configuration.Flask-Migrate-2.5.2/flask_migrate/templates/flask-multidb/alembic.ini.mako0000644000076500000240000000140213333657347030675 0ustar miguelgrinbergstaff00000000000000# A generic, single database configuration. [alembic] # template used to generate migration files # file_template = %%(rev)s_%%(slug)s # set to 'true' to run the environment during # the 'revision' command, regardless of autogenerate # revision_environment = false # Logging configuration [loggers] keys = root,sqlalchemy,alembic [handlers] keys = console [formatters] keys = generic [logger_root] level = WARN handlers = console qualname = [logger_sqlalchemy] level = WARN handlers = qualname = sqlalchemy.engine [logger_alembic] level = INFO handlers = qualname = alembic [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(levelname)-5.5s [%(name)s] %(message)s datefmt = %H:%M:%S Flask-Migrate-2.5.2/flask_migrate/templates/flask-multidb/env.py0000644000076500000240000001276213470271017027013 0ustar miguelgrinbergstaff00000000000000from __future__ import with_statement import logging from logging.config import fileConfig import re from sqlalchemy import engine_from_config from sqlalchemy import MetaData from sqlalchemy import pool from flask import current_app from alembic import context USE_TWOPHASE = False # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config # Interpret the config file for Python logging. # This line sets up loggers basically. fileConfig(config.config_file_name) logger = logging.getLogger('alembic.env') # add your model's MetaData object here # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata config.set_main_option( 'sqlalchemy.url', current_app.config.get( 'SQLALCHEMY_DATABASE_URI').replace('%', '%%')) bind_names = [] for name, url in current_app.config.get("SQLALCHEMY_BINDS").items(): context.config.set_section_option(name, "sqlalchemy.url", url.replace( '%', '%%')) bind_names.append(name) target_metadata = current_app.extensions['migrate'].db.metadata # other values from the config, defined by the needs of env.py, # can be acquired: # my_important_option = config.get_main_option("my_important_option") # ... etc. def get_metadata(bind): """Return the metadata for a bind.""" if bind == '': bind = None m = MetaData() for t in target_metadata.tables.values(): if t.info.get('bind_key') == bind: t.tometadata(m) return m def run_migrations_offline(): """Run migrations in 'offline' mode. This configures the context with just a URL and not an Engine, though an Engine is acceptable here as well. By skipping the Engine creation we don't even need a DBAPI to be available. Calls to context.execute() here emit the given string to the script output. """ # for the --sql use case, run migrations for each URL into # individual files. engines = { '': { 'url': context.config.get_main_option('sqlalchemy.url') } } for name in bind_names: engines[name] = rec = {} rec['url'] = context.config.get_section_option(name, "sqlalchemy.url") for name, rec in engines.items(): logger.info("Migrating database %s" % (name or '')) file_ = "%s.sql" % name logger.info("Writing output to %s" % file_) with open(file_, 'w') as buffer: context.configure( url=rec['url'], output_buffer=buffer, target_metadata=get_metadata(name), literal_binds=True, ) with context.begin_transaction(): context.run_migrations(engine_name=name) def run_migrations_online(): """Run migrations in 'online' mode. In this scenario we need to create an Engine and associate a connection with the context. """ # this callback is used to prevent an auto-migration from being generated # when there are no changes to the schema # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html def process_revision_directives(context, revision, directives): if getattr(config.cmd_opts, 'autogenerate', False): script = directives[0] if len(script.upgrade_ops_list) >= len(bind_names) + 1: empty = True for upgrade_ops in script.upgrade_ops_list: if not upgrade_ops.is_empty(): empty = False if empty: directives[:] = [] logger.info('No changes in schema detected.') # for the direct-to-DB use case, start a transaction on all # engines, then run all migrations, then commit all transactions. engines = { '': { 'engine': engine_from_config( config.get_section(config.config_ini_section), prefix='sqlalchemy.', poolclass=pool.NullPool, ) } } for name in bind_names: engines[name] = rec = {} rec['engine'] = engine_from_config( context.config.get_section(name), prefix='sqlalchemy.', poolclass=pool.NullPool) for name, rec in engines.items(): engine = rec['engine'] rec['connection'] = conn = engine.connect() if USE_TWOPHASE: rec['transaction'] = conn.begin_twophase() else: rec['transaction'] = conn.begin() try: for name, rec in engines.items(): logger.info("Migrating database %s" % (name or '')) context.configure( connection=rec['connection'], upgrade_token="%s_upgrades" % name, downgrade_token="%s_downgrades" % name, target_metadata=get_metadata(name), process_revision_directives=process_revision_directives, **current_app.extensions['migrate'].configure_args ) context.run_migrations(engine_name=name) if USE_TWOPHASE: for rec in engines.values(): rec['transaction'].prepare() for rec in engines.values(): rec['transaction'].commit() except: for rec in engines.values(): rec['transaction'].rollback() raise finally: for rec in engines.values(): rec['connection'].close() if context.is_offline_mode(): run_migrations_offline() else: run_migrations_online() Flask-Migrate-2.5.2/flask_migrate/templates/flask-multidb/script.py.mako0000644000076500000240000000170513333657347030464 0ustar miguelgrinbergstaff00000000000000<%! import re %>"""${message} Revision ID: ${up_revision} Revises: ${down_revision | comma,n} Create Date: ${create_date} """ from alembic import op import sqlalchemy as sa ${imports if imports else ""} # revision identifiers, used by Alembic. revision = ${repr(up_revision)} down_revision = ${repr(down_revision)} branch_labels = ${repr(branch_labels)} depends_on = ${repr(depends_on)} def upgrade(engine_name): globals()["upgrade_%s" % engine_name]() def downgrade(engine_name): globals()["downgrade_%s" % engine_name]() <% from flask import current_app db_names = [''] + list(current_app.config.get("SQLALCHEMY_BINDS").keys()) %> ## generate an "upgrade_() / downgrade_()" function ## for each database name in the ini file. % for db_name in db_names: def upgrade_${db_name}(): ${context.get("%s_upgrades" % db_name, "pass")} def downgrade_${db_name}(): ${context.get("%s_downgrades" % db_name, "pass")} % endfor Flask-Migrate-2.5.2/setup.cfg0000644000076500000240000000004613472311554020121 0ustar miguelgrinbergstaff00000000000000[egg_info] tag_build = tag_date = 0 Flask-Migrate-2.5.2/setup.py0000644000076500000240000000256013470273153020015 0ustar miguelgrinbergstaff00000000000000""" Flask-Migrate -------------- SQLAlchemy database migrations for Flask applications using Alembic. """ from setuptools import setup VERSION = open('__version__').read() setup( name='Flask-Migrate', version=VERSION, url='http://github.com/miguelgrinberg/flask-migrate/', license='MIT', author='Miguel Grinberg', author_email='miguelgrinberg50@gmail.com', description=('SQLAlchemy database migrations for Flask applications ' 'using Alembic'), long_description=__doc__, packages=['flask_migrate'], zip_safe=False, include_package_data=True, platforms='any', install_requires=[ 'Flask>=0.9', 'Flask-SQLAlchemy>=1.0', 'alembic>=0.7' ], tests_require=[ 'Flask-Script>=0.6' ], entry_points={ 'flask.commands': [ 'db=flask_migrate.cli:db' ], }, test_suite="tests", classifiers=[ 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Libraries :: Python Modules' ] ) Flask-Migrate-2.5.2/tests/0000755000076500000240000000000013472311554017442 5ustar miguelgrinbergstaff00000000000000Flask-Migrate-2.5.2/tests/__init__.py0000644000076500000240000000000113333657347021553 0ustar miguelgrinbergstaff00000000000000 Flask-Migrate-2.5.2/tests/app.db0000644000076500000240000003000013471612053020517 0ustar miguelgrinbergstaff00000000000000SQLite format 3@ .(k   )++ tablealembic_versionalembic_versionCREATE TABLE alembic_version ( version_num VARCHAR(32) NOT NULL, CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num) )=Q+indexsqlite_autoindex_alembic_version_1alembic_version  Flask-Migrate-2.5.2/tests/app.py0000755000076500000240000000107313470272262020600 0ustar miguelgrinbergstaff00000000000000#!/bin/env python from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_script import Manager from flask_migrate import Migrate, MigrateCommand app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) migrate = Migrate(app, db) manager = Manager(app) manager.add_command('db', MigrateCommand) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) if __name__ == '__main__': manager.run() Flask-Migrate-2.5.2/tests/app_compare_type1.py0000755000076500000240000000107413333657347023442 0ustar miguelgrinbergstaff00000000000000from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_script import Manager from flask_migrate import Migrate, MigrateCommand app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) migrate = Migrate(app, db, compare_type=True) manager = Manager(app) manager.add_command('db', MigrateCommand) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) if __name__ == '__main__': manager.run() Flask-Migrate-2.5.2/tests/app_compare_type2.py0000755000076500000240000000107313333657347023442 0ustar miguelgrinbergstaff00000000000000from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_script import Manager from flask_migrate import Migrate, MigrateCommand app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) migrate = Migrate(app, db, compare_type=True) manager = Manager(app) manager.add_command('db', MigrateCommand) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(10)) if __name__ == '__main__': manager.run() Flask-Migrate-2.5.2/tests/app_custom_directory.py0000755000076500000240000000112313333657347024263 0ustar miguelgrinbergstaff00000000000000from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_script import Manager from flask_migrate import Migrate, MigrateCommand app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) migrate = Migrate(app, db, directory='temp_folder/temp_migrations') manager = Manager(app) manager.add_command('db', MigrateCommand) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) if __name__ == '__main__': manager.run() Flask-Migrate-2.5.2/tests/app_multidb.py0000755000076500000240000000141313333657347022327 0ustar miguelgrinbergstaff00000000000000#!/bin/env python from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_script import Manager from flask_migrate import Migrate, MigrateCommand app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app1.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_BINDS'] = { "db1": "sqlite:///app2.db", } db = SQLAlchemy(app) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) class Group(db.Model): __bind_key__ = 'db1' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) migrate = Migrate(app, db) manager = Manager(app) manager.add_command('db', MigrateCommand) if __name__ == '__main__': manager.run() Flask-Migrate-2.5.2/tests/test_migrate.py0000644000076500000240000000542113470272622022505 0ustar miguelgrinbergstaff00000000000000import os import shutil import unittest import subprocess import shlex def run_cmd(cmd): """Run a command and return a tuple with (stdout, stderr, exit_code)""" process = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = process.communicate() print('\n$ ' + cmd) print(stdout.decode('utf-8')) print(stderr.decode('utf-8')) return stdout, stderr, process.wait() class TestMigrate(unittest.TestCase): def setUp(self): os.chdir(os.path.split(os.path.abspath(__file__))[0]) try: os.remove('app.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass try: shutil.rmtree('temp_folder') except OSError: pass def tearDown(self): try: os.remove('app.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass try: shutil.rmtree('temp_folder') except OSError: pass def test_alembic_version(self): from flask_migrate import alembic_version self.assertEqual(len(alembic_version), 3) for v in alembic_version: self.assertTrue(isinstance(v, int)) def test_migrate_upgrade(self): (o, e, s) = run_cmd('python app.py db init') self.assertTrue(s == 0) (o, e, s) = run_cmd('python app.py db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('python app.py db upgrade') self.assertTrue(s == 0) from .app import db, User db.session.add(User(name='test')) db.session.commit() def test_custom_directory(self): (o, e, s) = run_cmd('python app_custom_directory.py db init') self.assertTrue(s == 0) (o, e, s) = run_cmd('python app_custom_directory.py db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('python app_custom_directory.py db upgrade') self.assertTrue(s == 0) from .app_custom_directory import db, User db.session.add(User(name='test')) db.session.commit() def test_compare_type(self): (o, e, s) = run_cmd('python app_compare_type1.py db init') self.assertTrue(s == 0) (o, e, s) = run_cmd('python app_compare_type1.py db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('python app_compare_type1.py db upgrade') self.assertTrue(s == 0) (o, e, s) = run_cmd('python app_compare_type2.py db migrate') self.assertTrue(s == 0) self.assertTrue(b'Detected type change from VARCHAR(length=128) ' b'to String(length=10)' in e) Flask-Migrate-2.5.2/tests/test_migrate_flaskcli.py0000644000076500000240000000551413470272640024360 0ustar miguelgrinbergstaff00000000000000import os import shutil import unittest import subprocess import shlex def run_cmd(app, cmd): """Run a command and return a tuple with (stdout, stderr, exit_code)""" os.environ['FLASK_APP'] = app process = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = process.communicate() print('\n$ ' + cmd) print(stdout.decode('utf-8')) print(stderr.decode('utf-8')) return stdout, stderr, process.wait() class TestMigrate(unittest.TestCase): def setUp(self): os.chdir(os.path.split(os.path.abspath(__file__))[0]) try: os.remove('app.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass try: shutil.rmtree('temp_folder') except OSError: pass def tearDown(self): try: os.remove('app.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass try: shutil.rmtree('temp_folder') except OSError: pass def test_alembic_version(self): from flask_migrate import alembic_version self.assertEqual(len(alembic_version), 3) for v in alembic_version: self.assertTrue(isinstance(v, int)) def test_migrate_upgrade(self): (o, e, s) = run_cmd('app.py', 'flask db init') self.assertTrue(s == 0) (o, e, s) = run_cmd('app.py', 'flask db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('app.py', 'flask db upgrade') self.assertTrue(s == 0) from .app import db, User db.session.add(User(name='test')) db.session.commit() def test_custom_directory(self): (o, e, s) = run_cmd('app_custom_directory.py', 'flask db init') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_custom_directory.py', 'flask db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_custom_directory.py', 'flask db upgrade') self.assertTrue(s == 0) from .app_custom_directory import db, User db.session.add(User(name='test')) db.session.commit() def test_compare_type(self): (o, e, s) = run_cmd('app_compare_type1.py', 'flask db init') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_compare_type1.py', 'flask db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_compare_type1.py', 'flask db upgrade') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_compare_type2.py', 'flask db migrate') self.assertTrue(s == 0) self.assertTrue(b'Detected type change from VARCHAR(length=128) ' b'to String(length=10)' in e) Flask-Migrate-2.5.2/tests/test_multidb_migrate.py0000644000076500000240000000535713333657347024246 0ustar miguelgrinbergstaff00000000000000import os import shutil import unittest import subprocess import shlex import sqlite3 def run_cmd(cmd): """Run a command and return a tuple with (stdout, stderr, exit_code)""" process = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = process.communicate() return stdout, stderr, process.wait() class TestMigrate(unittest.TestCase): def setUp(self): os.chdir(os.path.split(os.path.abspath(__file__))[0]) try: os.remove('app1.db') os.remove('app2.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass def tearDown(self): try: os.remove('app1.db') os.remove('app2.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass def test_multidb_migrate_upgrade(self): (o, e, s) = run_cmd('python app_multidb.py db init --multidb') self.assertTrue(s == 0) (o, e, s) = run_cmd('python app_multidb.py db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('python app_multidb.py db upgrade') self.assertTrue(s == 0) # ensure the tables are in the correct databases conn1 = sqlite3.connect('app1.db') c = conn1.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn1.close() self.assertIn(('alembic_version',), tables) self.assertIn(('user',), tables) conn2 = sqlite3.connect('app2.db') c = conn2.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn2.close() self.assertIn(('alembic_version',), tables) self.assertIn(('group',), tables) # ensure the databases can be written to from .app_multidb import db, User, Group db.session.add(User(name='test')) db.session.add(Group(name='group')) db.session.commit() # ensure the downgrade works (o, e, s) = run_cmd('python app_multidb.py db downgrade') self.assertTrue(s == 0) conn1 = sqlite3.connect('app1.db') c = conn1.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn1.close() self.assertIn(('alembic_version',), tables) self.assertNotIn(('user',), tables) conn2 = sqlite3.connect('app2.db') c = conn2.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn2.close() self.assertIn(('alembic_version',), tables) self.assertNotIn(('group',), tables) Flask-Migrate-2.5.2/tests/test_multidb_migrate_flaskcli.py0000644000076500000240000000543613333657347026114 0ustar miguelgrinbergstaff00000000000000import os import shutil import unittest import subprocess import shlex import sqlite3 def run_cmd(app, cmd): """Run a command and return a tuple with (stdout, stderr, exit_code)""" os.environ['FLASK_APP'] = app process = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = process.communicate() return stdout, stderr, process.wait() class TestMigrate(unittest.TestCase): def setUp(self): os.chdir(os.path.split(os.path.abspath(__file__))[0]) try: os.remove('app1.db') os.remove('app2.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass def tearDown(self): try: os.remove('app1.db') os.remove('app2.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass def test_multidb_migrate_upgrade(self): (o, e, s) = run_cmd('app_multidb.py', 'flask db init --multidb') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_multidb.py', 'flask db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_multidb.py', 'flask db upgrade') self.assertTrue(s == 0) # ensure the tables are in the correct databases conn1 = sqlite3.connect('app1.db') c = conn1.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn1.close() self.assertIn(('alembic_version',), tables) self.assertIn(('user',), tables) conn2 = sqlite3.connect('app2.db') c = conn2.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn2.close() self.assertIn(('alembic_version',), tables) self.assertIn(('group',), tables) # ensure the databases can be written to from .app_multidb import db, User, Group db.session.add(User(name='test')) db.session.add(Group(name='group')) db.session.commit() # ensure the downgrade works (o, e, s) = run_cmd('app_multidb.py', 'flask db downgrade') self.assertTrue(s == 0) conn1 = sqlite3.connect('app1.db') c = conn1.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn1.close() self.assertIn(('alembic_version',), tables) self.assertNotIn(('user',), tables) conn2 = sqlite3.connect('app2.db') c = conn2.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn2.close() self.assertIn(('alembic_version',), tables) self.assertNotIn(('group',), tables)