summaryrefslogtreecommitdiff
path: root/lib/python2.7/site-packages/south/migration
diff options
context:
space:
mode:
authorttt2017-05-13 00:29:47 +0530
committerttt2017-05-13 00:29:47 +0530
commitabf599be33b383a6a5baf9493093b2126a622ac8 (patch)
tree4c5ab6e0d935d5e65fabcf0258e4a00dd20a5afa /lib/python2.7/site-packages/south/migration
downloadSBHS-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__.py235
-rw-r--r--lib/python2.7/site-packages/south/migration/base.py440
-rw-r--r--lib/python2.7/site-packages/south/migration/migrators.py379
-rw-r--r--lib/python2.7/site-packages/south/migration/utils.py94
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)