diff options
author | ttt | 2017-05-13 00:29:47 +0530 |
---|---|---|
committer | ttt | 2017-05-13 00:29:47 +0530 |
commit | abf599be33b383a6a5baf9493093b2126a622ac8 (patch) | |
tree | 4c5ab6e0d935d5e65fabcf0258e4a00dd20a5afa /lib/python2.7/site-packages/south/migration | |
download | SBHS-2018-Rpi-abf599be33b383a6a5baf9493093b2126a622ac8.tar.gz SBHS-2018-Rpi-abf599be33b383a6a5baf9493093b2126a622ac8.tar.bz2 SBHS-2018-Rpi-abf599be33b383a6a5baf9493093b2126a622ac8.zip |
added all server files
Diffstat (limited to 'lib/python2.7/site-packages/south/migration')
-rw-r--r-- | lib/python2.7/site-packages/south/migration/__init__.py | 235 | ||||
-rw-r--r-- | lib/python2.7/site-packages/south/migration/base.py | 440 | ||||
-rw-r--r-- | lib/python2.7/site-packages/south/migration/migrators.py | 379 | ||||
-rw-r--r-- | lib/python2.7/site-packages/south/migration/utils.py | 94 |
4 files changed, 1148 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/south/migration/__init__.py b/lib/python2.7/site-packages/south/migration/__init__.py new file mode 100644 index 0000000..1d91ddf --- /dev/null +++ b/lib/python2.7/site-packages/south/migration/__init__.py @@ -0,0 +1,235 @@ +""" +Main migration logic. +""" + +from __future__ import print_function + +import sys + +from django.core.exceptions import ImproperlyConfigured + +import south.db +from south import exceptions +from south.models import MigrationHistory +from south.db import db, DEFAULT_DB_ALIAS +from south.migration.migrators import (Backwards, Forwards, + DryRunMigrator, FakeMigrator, + LoadInitialDataMigrator) +from south.migration.base import Migration, Migrations +from south.migration.utils import SortedSet +from south.migration.base import all_migrations +from south.signals import pre_migrate, post_migrate + + +def to_apply(forwards, done): + return [m for m in forwards if m not in done] + +def to_unapply(backwards, done): + return [m for m in backwards if m in done] + +def problems(pending, done): + last = None + if not pending: + raise StopIteration() + for migration in pending: + if migration in done: + last = migration + continue + if last and migration not in done: + yield last, migration + +def forwards_problems(pending, done, verbosity): + """ + Takes the list of linearised pending migrations, and the set of done ones, + and returns the list of problems, if any. + """ + return inner_problem_check(problems(reversed(pending), done), done, verbosity) + +def backwards_problems(pending, done, verbosity): + return inner_problem_check(problems(pending, done), done, verbosity) + +def inner_problem_check(problems, done, verbosity): + "Takes a set of possible problems and gets the actual issues out of it." + result = [] + for last, migration in problems: + checked = set([]) + # 'Last' is the last applied migration. Step back from it until we + # either find nothing wrong, or we find something. + to_check = list(last.dependencies) + while to_check: + checking = to_check.pop() + if checking in checked: + continue + checked.add(checking) + + if checking not in done: + # That's bad. Error. + if verbosity: + print((" ! Migration %s should not have been applied " + "before %s but was." % (last, checking))) + result.append((last, checking)) + else: + to_check.extend(checking.dependencies) + return result + +def check_migration_histories(histories, delete_ghosts=False, ignore_ghosts=False): + "Checks that there's no 'ghost' migrations in the database." + exists = SortedSet() + ghosts = [] + for h in histories: + try: + m = h.get_migration() + m.migration() + except exceptions.UnknownMigration: + ghosts.append(h) + except ImproperlyConfigured: + pass # Ignore missing applications + else: + exists.add(m) + if ghosts: + # They may want us to delete ghosts. + if delete_ghosts: + for h in ghosts: + h.delete() + elif not ignore_ghosts: + raise exceptions.GhostMigrations(ghosts) + return exists + +def get_dependencies(target, migrations): + forwards = list + backwards = list + if target is None: + backwards = migrations[0].backwards_plan + else: + forwards = target.forwards_plan + # When migrating backwards we want to remove up to and + # including the next migration up in this app (not the next + # one, that includes other apps) + migration_before_here = target.next() + if migration_before_here: + backwards = migration_before_here.backwards_plan + return forwards, backwards + +def get_direction(target, applied, migrations, verbosity, interactive): + # Get the forwards and reverse dependencies for this target + forwards, backwards = get_dependencies(target, migrations) + # Is the whole forward branch applied? + problems = None + forwards = forwards() + workplan = to_apply(forwards, applied) + if not workplan: + # If they're all applied, we only know it's not backwards + direction = None + else: + # If the remaining migrations are strictly a right segment of + # the forwards trace, we just need to go forwards to our + # target (and check for badness) + problems = forwards_problems(forwards, applied, verbosity) + direction = Forwards(verbosity=verbosity, interactive=interactive) + if not problems: + # What about the whole backward trace then? + backwards = backwards() + missing_backwards = to_apply(backwards, applied) + if missing_backwards != backwards: + # If what's missing is a strict left segment of backwards (i.e. + # all the higher migrations) then we need to go backwards + workplan = to_unapply(backwards, applied) + problems = backwards_problems(backwards, applied, verbosity) + direction = Backwards(verbosity=verbosity, interactive=interactive) + return direction, problems, workplan + +def get_migrator(direction, db_dry_run, fake, load_initial_data): + if not direction: + return direction + if db_dry_run: + direction = DryRunMigrator(migrator=direction, ignore_fail=False) + elif fake: + direction = FakeMigrator(migrator=direction) + elif load_initial_data: + direction = LoadInitialDataMigrator(migrator=direction) + return direction + +def get_unapplied_migrations(migrations, applied_migrations): + applied_migration_names = ['%s.%s' % (mi.app_name,mi.migration) for mi in applied_migrations] + + for migration in migrations: + is_applied = '%s.%s' % (migration.app_label(), migration.name()) in applied_migration_names + if not is_applied: + yield migration + +def migrate_app(migrations, target_name=None, merge=False, fake=False, db_dry_run=False, yes=False, verbosity=0, load_initial_data=False, skip=False, database=DEFAULT_DB_ALIAS, delete_ghosts=False, ignore_ghosts=False, interactive=False): + app_label = migrations.app_label() + + verbosity = int(verbosity) + # Fire off the pre-migrate signal + pre_migrate.send(None, app=app_label, verbosity=verbosity, interactive=verbosity, db=database) + + # If there aren't any, quit quizically + if not migrations: + print("? You have no migrations for the '%s' app. You might want some." % app_label) + return + + # Load the entire dependency graph + Migrations.calculate_dependencies() + + # Check there's no strange ones in the database + applied_all = MigrationHistory.objects.filter(applied__isnull=False).order_by('applied').using(database) + applied = applied_all.filter(app_name=app_label).using(database) + south.db.db = south.db.dbs[database] + Migrations.invalidate_all_modules() + + south.db.db.debug = (verbosity > 1) + + if target_name == 'current-1': + if applied.count() > 1: + previous_migration = applied[applied.count() - 2] + if verbosity: + print('previous_migration: %s (applied: %s)' % (previous_migration.migration, previous_migration.applied)) + target_name = previous_migration.migration + else: + if verbosity: + print('previous_migration: zero') + target_name = 'zero' + elif target_name == 'current+1': + try: + first_unapplied_migration = get_unapplied_migrations(migrations, applied).next() + target_name = first_unapplied_migration.name() + except StopIteration: + target_name = None + + applied_all = check_migration_histories(applied_all, delete_ghosts, ignore_ghosts) + + # Guess the target_name + target = migrations.guess_migration(target_name) + if verbosity: + if target_name not in ('zero', None) and target.name() != target_name: + print(" - Soft matched migration %s to %s." % (target_name, + target.name())) + print("Running migrations for %s:" % app_label) + + # Get the forwards and reverse dependencies for this target + direction, problems, workplan = get_direction(target, applied_all, migrations, + verbosity, interactive) + if problems and not (merge or skip): + raise exceptions.InconsistentMigrationHistory(problems) + + # Perform the migration + migrator = get_migrator(direction, db_dry_run, fake, load_initial_data) + if migrator: + migrator.print_title(target) + success = migrator.migrate_many(target, workplan, database) + # Finally, fire off the post-migrate signal + if success: + post_migrate.send(None, app=app_label, verbosity=verbosity, interactive=verbosity, db=database) + else: + if verbosity: + # Say there's nothing. + print('- Nothing to migrate.') + # If we have initial data enabled, and we're at the most recent + # migration, do initial data. + # Note: We use a fake Forwards() migrator here. It's never used really. + if load_initial_data: + migrator = LoadInitialDataMigrator(migrator=Forwards(verbosity=verbosity)) + migrator.load_initial_data(target, db=database) + # Send signal. + post_migrate.send(None, app=app_label, verbosity=verbosity, interactive=verbosity, db=database) diff --git a/lib/python2.7/site-packages/south/migration/base.py b/lib/python2.7/site-packages/south/migration/base.py new file mode 100644 index 0000000..8bd6a5a --- /dev/null +++ b/lib/python2.7/site-packages/south/migration/base.py @@ -0,0 +1,440 @@ +from __future__ import print_function + +from collections import deque +import datetime +from imp import reload +import os +import re +import sys + +from django.core.exceptions import ImproperlyConfigured +from django.db import models +from django.conf import settings +from django.utils import importlib + +from south import exceptions +from south.migration.utils import depends, dfs, flatten, get_app_label +from south.orm import FakeORM +from south.utils import memoize, ask_for_it_by_name, datetime_utils +from south.migration.utils import app_label_to_app_module +from south.utils.py3 import string_types, with_metaclass + +def all_migrations(applications=None): + """ + Returns all Migrations for all `applications` that are migrated. + """ + if applications is None: + applications = models.get_apps() + for model_module in applications: + # The app they've passed is the models module - go up one level + app_path = ".".join(model_module.__name__.split(".")[:-1]) + app = ask_for_it_by_name(app_path) + try: + yield Migrations(app) + except exceptions.NoMigrations: + pass + + +def application_to_app_label(application): + "Works out the app label from either the app label, the app name, or the module" + if isinstance(application, string_types): + app_label = application.split('.')[-1] + else: + app_label = application.__name__.split('.')[-1] + return app_label + + +class MigrationsMetaclass(type): + + """ + Metaclass which ensures there is only one instance of a Migrations for + any given app. + """ + + def __init__(self, name, bases, dict): + super(MigrationsMetaclass, self).__init__(name, bases, dict) + self.instances = {} + + def __call__(self, application, **kwds): + + app_label = application_to_app_label(application) + + # If we don't already have an instance, make one + if app_label not in self.instances: + self.instances[app_label] = super(MigrationsMetaclass, self).__call__(app_label_to_app_module(app_label), **kwds) + + return self.instances[app_label] + + def _clear_cache(self): + "Clears the cache of Migration objects." + self.instances = {} + + +class Migrations(with_metaclass(MigrationsMetaclass, list)): + """ + Holds a list of Migration objects for a particular app. + """ + + if getattr(settings, "SOUTH_USE_PYC", False): + MIGRATION_FILENAME = re.compile(r'(?!__init__)' # Don't match __init__.py + r'[0-9a-zA-Z_]*' # Don't match dotfiles, or names with dots/invalid chars in them + r'(\.pyc?)?$') # Match .py or .pyc files, or module dirs + else: + MIGRATION_FILENAME = re.compile(r'(?!__init__)' # Don't match __init__.py + r'[0-9a-zA-Z_]*' # Don't match dotfiles, or names with dots/invalid chars in them + r'(\.py)?$') # Match only .py files, or module dirs + + def __init__(self, application, force_creation=False, verbose_creation=True): + "Constructor. Takes the module of the app, NOT its models (like get_app returns)" + self._cache = {} + self.set_application(application, force_creation, verbose_creation) + + def create_migrations_directory(self, verbose=True): + "Given an application, ensures that the migrations directory is ready." + migrations_dir = self.migrations_dir() + # Make the directory if it's not already there + if not os.path.isdir(migrations_dir): + if verbose: + print("Creating migrations directory at '%s'..." % migrations_dir) + os.mkdir(migrations_dir) + # Same for __init__.py + init_path = os.path.join(migrations_dir, "__init__.py") + if not os.path.isfile(init_path): + # Touch the init py file + if verbose: + print("Creating __init__.py in '%s'..." % migrations_dir) + open(init_path, "w").close() + + def migrations_dir(self): + """ + Returns the full path of the migrations directory. + If it doesn't exist yet, returns where it would exist, based on the + app's migrations module (defaults to app.migrations) + """ + module_path = self.migrations_module() + try: + module = importlib.import_module(module_path) + except ImportError: + # There's no migrations module made yet; guess! + try: + parent = importlib.import_module(".".join(module_path.split(".")[:-1])) + except ImportError: + # The parent doesn't even exist, that's an issue. + raise exceptions.InvalidMigrationModule( + application = self.application.__name__, + module = module_path, + ) + else: + # Good guess. + return os.path.join(os.path.dirname(parent.__file__), module_path.split(".")[-1]) + else: + # Get directory directly + return os.path.dirname(module.__file__) + + def migrations_module(self): + "Returns the module name of the migrations module for this" + app_label = application_to_app_label(self.application) + if hasattr(settings, "SOUTH_MIGRATION_MODULES"): + if app_label in settings.SOUTH_MIGRATION_MODULES: + # There's an override. + return settings.SOUTH_MIGRATION_MODULES[app_label] + return self._application.__name__ + '.migrations' + + def get_application(self): + return self._application + + def set_application(self, application, force_creation=False, verbose_creation=True): + """ + Called when the application for this Migrations is set. + Imports the migrations module object, and throws a paddy if it can't. + """ + self._application = application + if not hasattr(application, 'migrations'): + try: + module = importlib.import_module(self.migrations_module()) + self._migrations = application.migrations = module + except ImportError: + if force_creation: + self.create_migrations_directory(verbose_creation) + module = importlib.import_module(self.migrations_module()) + self._migrations = application.migrations = module + else: + raise exceptions.NoMigrations(application) + self._load_migrations_module(application.migrations) + + application = property(get_application, set_application) + + def _load_migrations_module(self, module): + self._migrations = module + filenames = [] + dirname = self.migrations_dir() + for f in os.listdir(dirname): + if self.MIGRATION_FILENAME.match(os.path.basename(f)): + full_path = os.path.join(dirname, f) + # If it's a .pyc file, only append if the .py isn't already around + if f.endswith(".pyc") and (os.path.isfile(full_path[:-1])): + continue + # If it's a module directory, only append if it contains __init__.py[c]. + if os.path.isdir(full_path): + if not (os.path.isfile(os.path.join(full_path, "__init__.py")) or \ + (getattr(settings, "SOUTH_USE_PYC", False) and \ + os.path.isfile(os.path.join(full_path, "__init__.pyc")))): + continue + filenames.append(f) + filenames.sort() + self.extend(self.migration(f) for f in filenames) + + def migration(self, filename): + name = Migration.strip_filename(filename) + if name not in self._cache: + self._cache[name] = Migration(self, name) + return self._cache[name] + + def __getitem__(self, value): + if isinstance(value, string_types): + return self.migration(value) + return super(Migrations, self).__getitem__(value) + + def _guess_migration(self, prefix): + prefix = Migration.strip_filename(prefix) + matches = [m for m in self if m.name().startswith(prefix)] + if len(matches) == 1: + return matches[0] + elif len(matches) > 1: + raise exceptions.MultiplePrefixMatches(prefix, matches) + else: + raise exceptions.UnknownMigration(prefix, None) + + def guess_migration(self, target_name): + if target_name == 'zero' or not self: + return + elif target_name is None: + return self[-1] + else: + return self._guess_migration(prefix=target_name) + + def app_label(self): + return self._application.__name__.split('.')[-1] + + def full_name(self): + return self._migrations.__name__ + + @classmethod + def calculate_dependencies(cls, force=False): + "Goes through all the migrations, and works out the dependencies." + if getattr(cls, "_dependencies_done", False) and not force: + return + for migrations in all_migrations(): + for migration in migrations: + migration.calculate_dependencies() + cls._dependencies_done = True + + @staticmethod + def invalidate_all_modules(): + "Goes through all the migrations, and invalidates all cached modules." + for migrations in all_migrations(): + for migration in migrations: + migration.invalidate_module() + + def next_filename(self, name): + "Returns the fully-formatted filename of what a new migration 'name' would be" + highest_number = 0 + for migration in self: + try: + number = int(migration.name().split("_")[0]) + highest_number = max(highest_number, number) + except ValueError: + pass + # Work out the new filename + return "%04i_%s.py" % ( + highest_number + 1, + name, + ) + + +class Migration(object): + + """ + Class which represents a particular migration file on-disk. + """ + + def __init__(self, migrations, filename): + """ + Returns the migration class implied by 'filename'. + """ + self.migrations = migrations + self.filename = filename + self.dependencies = set() + self.dependents = set() + + def __str__(self): + return self.app_label() + ':' + self.name() + + def __repr__(self): + return '<Migration: %s>' % str(self) + + def __eq__(self, other): + return self.app_label() == other.app_label() and self.name() == other.name() + + def __hash__(self): + return hash(str(self)) + + def app_label(self): + return self.migrations.app_label() + + @staticmethod + def strip_filename(filename): + return os.path.splitext(os.path.basename(filename))[0] + + def name(self): + return self.strip_filename(os.path.basename(self.filename)) + + def full_name(self): + return self.migrations.full_name() + '.' + self.name() + + def migration(self): + "Tries to load the actual migration module" + full_name = self.full_name() + try: + migration = sys.modules[full_name] + except KeyError: + try: + migration = __import__(full_name, {}, {}, ['Migration']) + except ImportError as e: + raise exceptions.UnknownMigration(self, sys.exc_info()) + except Exception as e: + raise exceptions.BrokenMigration(self, sys.exc_info()) + # Override some imports + migration._ = lambda x: x # Fake i18n + migration.datetime = datetime_utils + return migration + migration = memoize(migration) + + def migration_class(self): + "Returns the Migration class from the module" + return self.migration().Migration + + def migration_instance(self): + "Instantiates the migration_class" + return self.migration_class()() + migration_instance = memoize(migration_instance) + + def previous(self): + "Returns the migration that comes before this one in the sequence." + index = self.migrations.index(self) - 1 + if index < 0: + return None + return self.migrations[index] + previous = memoize(previous) + + def next(self): + "Returns the migration that comes after this one in the sequence." + index = self.migrations.index(self) + 1 + if index >= len(self.migrations): + return None + return self.migrations[index] + next = memoize(next) + + def _get_dependency_objects(self, attrname): + """ + Given the name of an attribute (depends_on or needed_by), either yields + a list of migration objects representing it, or errors out. + """ + for app, name in getattr(self.migration_class(), attrname, []): + try: + migrations = Migrations(app) + except ImproperlyConfigured: + raise exceptions.DependsOnUnmigratedApplication(self, app) + migration = migrations.migration(name) + try: + migration.migration() + except exceptions.UnknownMigration: + raise exceptions.DependsOnUnknownMigration(self, migration) + if migration.is_before(self) == False: + raise exceptions.DependsOnHigherMigration(self, migration) + yield migration + + def calculate_dependencies(self): + """ + Loads dependency info for this migration, and stores it in itself + and any other relevant migrations. + """ + # Normal deps first + for migration in self._get_dependency_objects("depends_on"): + self.dependencies.add(migration) + migration.dependents.add(self) + # And reverse deps + for migration in self._get_dependency_objects("needed_by"): + self.dependents.add(migration) + migration.dependencies.add(self) + # And implicit ordering deps + previous = self.previous() + if previous: + self.dependencies.add(previous) + previous.dependents.add(self) + + def invalidate_module(self): + """ + Removes the cached version of this migration's module import, so we + have to re-import it. Used when south.db.db changes. + """ + reload(self.migration()) + self.migration._invalidate() + + def forwards(self): + return self.migration_instance().forwards + + def backwards(self): + return self.migration_instance().backwards + + def forwards_plan(self): + """ + Returns a list of Migration objects to be applied, in order. + + This list includes `self`, which will be applied last. + """ + return depends(self, lambda x: x.dependencies) + + def _backwards_plan(self): + return depends(self, lambda x: x.dependents) + + def backwards_plan(self): + """ + Returns a list of Migration objects to be unapplied, in order. + + This list includes `self`, which will be unapplied last. + """ + return list(self._backwards_plan()) + + def is_before(self, other): + if self.migrations == other.migrations: + if self.filename < other.filename: + return True + return False + + def is_after(self, other): + if self.migrations == other.migrations: + if self.filename > other.filename: + return True + return False + + def prev_orm(self): + if getattr(self.migration_class(), 'symmetrical', False): + return self.orm() + previous = self.previous() + if previous is None: + # First migration? The 'previous ORM' is empty. + return FakeORM(None, self.app_label()) + return previous.orm() + prev_orm = memoize(prev_orm) + + def orm(self): + return FakeORM(self.migration_class(), self.app_label()) + orm = memoize(orm) + + def no_dry_run(self): + migration_class = self.migration_class() + try: + return migration_class.no_dry_run + except AttributeError: + return False diff --git a/lib/python2.7/site-packages/south/migration/migrators.py b/lib/python2.7/site-packages/south/migration/migrators.py new file mode 100644 index 0000000..f405a15 --- /dev/null +++ b/lib/python2.7/site-packages/south/migration/migrators.py @@ -0,0 +1,379 @@ +from __future__ import print_function + +from copy import copy, deepcopy +import datetime +import inspect +import sys +import traceback + +from django.core.management import call_command +from django.core.management.commands import loaddata +from django.db import models +from django import VERSION as DJANGO_VERSION + +import south.db +from south import exceptions +from south.db import DEFAULT_DB_ALIAS +from south.models import MigrationHistory +from south.signals import ran_migration +from south.utils.py3 import StringIO + + +class Migrator(object): + def __init__(self, verbosity=0, interactive=False): + self.verbosity = int(verbosity) + self.interactive = bool(interactive) + + @staticmethod + def title(target): + raise NotImplementedError() + + def print_title(self, target): + if self.verbosity: + print(self.title(target)) + + @staticmethod + def status(target): + raise NotImplementedError() + + def print_status(self, migration): + status = self.status(migration) + if self.verbosity and status: + print(status) + + @staticmethod + def orm(migration): + raise NotImplementedError() + + def backwards(self, migration): + return self._wrap_direction(migration.backwards(), migration.prev_orm()) + + def direction(self, migration): + raise NotImplementedError() + + @staticmethod + def _wrap_direction(direction, orm): + args = inspect.getargspec(direction) + if len(args[0]) == 1: + # Old migration, no ORM should be passed in + return direction + return (lambda: direction(orm)) + + @staticmethod + def record(migration, database): + raise NotImplementedError() + + def run_migration_error(self, migration, extra_info=''): + return ( + ' ! Error found during real run of migration! Aborting.\n' + '\n' + ' ! Since you have a database that does not support running\n' + ' ! schema-altering statements in transactions, we have had \n' + ' ! to leave it in an interim state between migrations.\n' + '%s\n' + ' ! The South developers regret this has happened, and would\n' + ' ! like to gently persuade you to consider a slightly\n' + ' ! easier-to-deal-with DBMS (one that supports DDL transactions)\n' + ' ! NOTE: The error which caused the migration to fail is further up.' + ) % extra_info + + def run_migration(self, migration, database): + migration_function = self.direction(migration) + south.db.db.start_transaction() + try: + migration_function() + south.db.db.execute_deferred_sql() + if not isinstance(getattr(self, '_wrapper', self), DryRunMigrator): + # record us as having done this in the same transaction, + # since we're not in a dry run + self.record(migration, database) + except: + south.db.db.rollback_transaction() + if not south.db.db.has_ddl_transactions: + print(self.run_migration_error(migration)) + print("Error in migration: %s" % migration) + raise + else: + try: + south.db.db.commit_transaction() + except: + print("Error during commit in migration: %s" % migration) + raise + + + def run(self, migration, database): + # Get the correct ORM. + south.db.db.current_orm = self.orm(migration) + # If we're not already in a dry run, and the database doesn't support + # running DDL inside a transaction, *cough*MySQL*cough* then do a dry + # run first. + if not isinstance(getattr(self, '_wrapper', self), DryRunMigrator): + if not south.db.db.has_ddl_transactions: + dry_run = DryRunMigrator(migrator=self, ignore_fail=False) + dry_run.run_migration(migration, database) + return self.run_migration(migration, database) + + + def send_ran_migration(self, migration, database): + ran_migration.send(None, + app=migration.app_label(), + migration=migration, + method=self.__class__.__name__.lower(), + verbosity=self.verbosity, + interactive=self.interactive, + db=database) + + def migrate(self, migration, database): + """ + Runs the specified migration forwards/backwards, in order. + """ + app = migration.migrations._migrations + migration_name = migration.name() + self.print_status(migration) + result = self.run(migration, database) + self.send_ran_migration(migration, database) + return result + + def migrate_many(self, target, migrations, database): + raise NotImplementedError() + + +class MigratorWrapper(object): + def __init__(self, migrator, *args, **kwargs): + self._migrator = copy(migrator) + attributes = dict([(k, getattr(self, k)) + for k in self.__class__.__dict__ + if not k.startswith('__')]) + self._migrator.__dict__.update(attributes) + self._migrator.__dict__['_wrapper'] = self + + def __getattr__(self, name): + return getattr(self._migrator, name) + + +class DryRunMigrator(MigratorWrapper): + def __init__(self, ignore_fail=True, *args, **kwargs): + super(DryRunMigrator, self).__init__(*args, **kwargs) + self._ignore_fail = ignore_fail + + def _run_migration(self, migration): + if migration.no_dry_run(): + if self.verbosity: + print(" - Migration '%s' is marked for no-dry-run." % migration) + return + south.db.db.dry_run = True + # preserve the constraint cache as it can be mutated by the dry run + constraint_cache = deepcopy(south.db.db._constraint_cache) + if self._ignore_fail: + south.db.db.debug, old_debug = False, south.db.db.debug + pending_creates = south.db.db.get_pending_creates() + south.db.db.start_transaction() + migration_function = self.direction(migration) + try: + try: + migration_function() + south.db.db.execute_deferred_sql() + except: + raise exceptions.FailedDryRun(migration, sys.exc_info()) + finally: + south.db.db.rollback_transactions_dry_run() + if self._ignore_fail: + south.db.db.debug = old_debug + south.db.db.clear_run_data(pending_creates) + south.db.db.dry_run = False + # restore the preserved constraint cache from before dry run was + # executed + south.db.db._constraint_cache = constraint_cache + + def run_migration(self, migration, database): + try: + self._run_migration(migration) + except exceptions.FailedDryRun: + if self._ignore_fail: + return False + raise + + def send_ran_migration(self, *args, **kwargs): + pass + + +class FakeMigrator(MigratorWrapper): + def run(self, migration, database): + # Don't actually run, just record as if ran + self.record(migration, database) + if self.verbosity: + print(' (faked)') + + def send_ran_migration(self, *args, **kwargs): + pass + + +class LoadInitialDataMigrator(MigratorWrapper): + + def load_initial_data(self, target, db='default'): + if target is None or target != target.migrations[-1]: + return + # Load initial data, if we ended up at target + if self.verbosity: + print(" - Loading initial data for %s." % target.app_label()) + if DJANGO_VERSION < (1, 6): + self.pre_1_6(target, db) + else: + self.post_1_6(target, db) + + def pre_1_6(self, target, db): + # Override Django's get_apps call temporarily to only load from the + # current app + old_get_apps = models.get_apps + new_get_apps = lambda: [models.get_app(target.app_label())] + models.get_apps = new_get_apps + loaddata.get_apps = new_get_apps + try: + call_command('loaddata', 'initial_data', verbosity=self.verbosity, database=db) + finally: + models.get_apps = old_get_apps + loaddata.get_apps = old_get_apps + + def post_1_6(self, target, db): + import django.db.models.loading + ## build a new 'AppCache' object with just the app we care about. + old_cache = django.db.models.loading.cache + new_cache = django.db.models.loading.AppCache() + new_cache.get_apps = lambda: [new_cache.get_app(target.app_label())] + + ## monkeypatch + django.db.models.loading.cache = new_cache + try: + call_command('loaddata', 'initial_data', verbosity=self.verbosity, database=db) + finally: + ## unmonkeypatch + django.db.models.loading.cache = old_cache + + def migrate_many(self, target, migrations, database): + migrator = self._migrator + result = migrator.__class__.migrate_many(migrator, target, migrations, database) + if result: + self.load_initial_data(target, db=database) + return True + + +class Forwards(Migrator): + """ + Runs the specified migration forwards, in order. + """ + torun = 'forwards' + + @staticmethod + def title(target): + if target is not None: + return " - Migrating forwards to %s." % target.name() + else: + assert False, "You cannot migrate forwards to zero." + + @staticmethod + def status(migration): + return ' > %s' % migration + + @staticmethod + def orm(migration): + return migration.orm() + + def forwards(self, migration): + return self._wrap_direction(migration.forwards(), migration.orm()) + + direction = forwards + + @staticmethod + def record(migration, database): + # Record us as having done this + record = MigrationHistory.for_migration(migration, database) + try: + from django.utils.timezone import now + record.applied = now() + except ImportError: + record.applied = datetime.datetime.utcnow() + if database != DEFAULT_DB_ALIAS: + record.save(using=database) + else: + # Django 1.1 and below always go down this branch. + record.save() + + def format_backwards(self, migration): + if migration.no_dry_run(): + return " (migration cannot be dry-run; cannot discover commands)" + old_debug, old_dry_run = south.db.db.debug, south.db.db.dry_run + south.db.db.debug = south.db.db.dry_run = True + stdout = sys.stdout + sys.stdout = StringIO() + try: + try: + self.backwards(migration)() + return sys.stdout.getvalue() + except: + raise + finally: + south.db.db.debug, south.db.db.dry_run = old_debug, old_dry_run + sys.stdout = stdout + + def run_migration_error(self, migration, extra_info=''): + extra_info = ('\n' + '! You *might* be able to recover with:' + '%s' + '%s' % + (self.format_backwards(migration), extra_info)) + return super(Forwards, self).run_migration_error(migration, extra_info) + + def migrate_many(self, target, migrations, database): + try: + for migration in migrations: + result = self.migrate(migration, database) + if result is False: # The migrations errored, but nicely. + return False + finally: + # Call any pending post_syncdb signals + south.db.db.send_pending_create_signals(verbosity=self.verbosity, + interactive=self.interactive) + return True + + +class Backwards(Migrator): + """ + Runs the specified migration backwards, in order. + """ + torun = 'backwards' + + @staticmethod + def title(target): + if target is None: + return " - Migrating backwards to zero state." + else: + return " - Migrating backwards to just after %s." % target.name() + + @staticmethod + def status(migration): + return ' < %s' % migration + + @staticmethod + def orm(migration): + return migration.prev_orm() + + direction = Migrator.backwards + + @staticmethod + def record(migration, database): + # Record us as having not done this + record = MigrationHistory.for_migration(migration, database) + if record.id is not None: + if database != DEFAULT_DB_ALIAS: + record.delete(using=database) + else: + # Django 1.1 always goes down here + record.delete() + + def migrate_many(self, target, migrations, database): + for migration in migrations: + self.migrate(migration, database) + return True + + + diff --git a/lib/python2.7/site-packages/south/migration/utils.py b/lib/python2.7/site-packages/south/migration/utils.py new file mode 100644 index 0000000..68b9164 --- /dev/null +++ b/lib/python2.7/site-packages/south/migration/utils.py @@ -0,0 +1,94 @@ +import sys +from collections import deque + +from django.utils.datastructures import SortedDict +from django.db import models + +from south import exceptions + + +class SortedSet(SortedDict): + def __init__(self, data=tuple()): + self.extend(data) + + def __str__(self): + return "SortedSet(%s)" % list(self) + + def add(self, value): + self[value] = True + + def remove(self, value): + del self[value] + + def extend(self, iterable): + [self.add(k) for k in iterable] + + +def get_app_label(app): + """ + Returns the _internal_ app label for the given app module. + i.e. for <module django.contrib.auth.models> will return 'auth' + """ + return app.__name__.split('.')[-2] + + +def app_label_to_app_module(app_label): + """ + Given the app label, returns the module of the app itself (unlike models.get_app, + which returns the models module) + """ + # Get the models module + app = models.get_app(app_label) + module_name = ".".join(app.__name__.split(".")[:-1]) + try: + module = sys.modules[module_name] + except KeyError: + __import__(module_name, {}, {}, ['']) + module = sys.modules[module_name] + return module + + +def flatten(*stack): + stack = deque(stack) + while stack: + try: + x = next(stack[0]) + except TypeError: + stack[0] = iter(stack[0]) + x = next(stack[0]) + except StopIteration: + stack.popleft() + continue + if hasattr(x, '__iter__') and not isinstance(x, str): + stack.appendleft(x) + else: + yield x + +dependency_cache = {} + +def _dfs(start, get_children, path): + if (start, get_children) in dependency_cache: + return dependency_cache[(start, get_children)] + + results = [] + if start in path: + raise exceptions.CircularDependency(path[path.index(start):] + [start]) + path.append(start) + results.append(start) + children = sorted(get_children(start), key=lambda x: str(x)) + + # We need to apply all the migrations this one depends on + for n in children: + results = _dfs(n, get_children, path) + results + + path.pop() + + results = list(SortedSet(results)) + dependency_cache[(start, get_children)] = results + return results + +def dfs(start, get_children): + return _dfs(start, get_children, []) + +def depends(start, get_children): + return dfs(start, get_children) |