Mercurial > kallithea
diff rhodecode/lib/dbmigrate/migrate/versioning/script/py.py @ 833:9753e0907827 beta
added dbmigrate package, added model changes
moved out upgrade db command to that package
author | Marcin Kuzminski <marcin@python-works.com> |
---|---|
date | Sat, 11 Dec 2010 01:54:12 +0100 |
parents | |
children | 08d2dcd71666 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/lib/dbmigrate/migrate/versioning/script/py.py Sat Dec 11 01:54:12 2010 +0100 @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import shutil +import warnings +import logging +from StringIO import StringIO + +import migrate +from migrate.versioning import genmodel, schemadiff +from migrate.versioning.config import operations +from migrate.versioning.template import Template +from migrate.versioning.script import base +from migrate.versioning.util import import_path, load_model, with_engine +from migrate.exceptions import MigrateDeprecationWarning, InvalidScriptError, ScriptError + +log = logging.getLogger(__name__) +__all__ = ['PythonScript'] + + +class PythonScript(base.BaseScript): + """Base for Python scripts""" + + @classmethod + def create(cls, path, **opts): + """Create an empty migration script at specified path + + :returns: :class:`PythonScript instance <migrate.versioning.script.py.PythonScript>`""" + cls.require_notfound(path) + + src = Template(opts.pop('templates_path', None)).get_script(theme=opts.pop('templates_theme', None)) + shutil.copy(src, path) + + return cls(path) + + @classmethod + def make_update_script_for_model(cls, engine, oldmodel, + model, repository, **opts): + """Create a migration script based on difference between two SA models. + + :param repository: path to migrate repository + :param oldmodel: dotted.module.name:SAClass or SAClass object + :param model: dotted.module.name:SAClass or SAClass object + :param engine: SQLAlchemy engine + :type repository: string or :class:`Repository instance <migrate.versioning.repository.Repository>` + :type oldmodel: string or Class + :type model: string or Class + :type engine: Engine instance + :returns: Upgrade / Downgrade script + :rtype: string + """ + + if isinstance(repository, basestring): + # oh dear, an import cycle! + from migrate.versioning.repository import Repository + repository = Repository(repository) + + oldmodel = load_model(oldmodel) + model = load_model(model) + + # Compute differences. + diff = schemadiff.getDiffOfModelAgainstModel( + oldmodel, + model, + excludeTables=[repository.version_table]) + # TODO: diff can be False (there is no difference?) + decls, upgradeCommands, downgradeCommands = \ + genmodel.ModelGenerator(diff,engine).toUpgradeDowngradePython() + + # Store differences into file. + src = Template(opts.pop('templates_path', None)).get_script(opts.pop('templates_theme', None)) + f = open(src) + contents = f.read() + f.close() + + # generate source + search = 'def upgrade(migrate_engine):' + contents = contents.replace(search, '\n\n'.join((decls, search)), 1) + if upgradeCommands: + contents = contents.replace(' pass', upgradeCommands, 1) + if downgradeCommands: + contents = contents.replace(' pass', downgradeCommands, 1) + return contents + + @classmethod + def verify_module(cls, path): + """Ensure path is a valid script + + :param path: Script location + :type path: string + :raises: :exc:`InvalidScriptError <migrate.exceptions.InvalidScriptError>` + :returns: Python module + """ + # Try to import and get the upgrade() func + module = import_path(path) + try: + assert callable(module.upgrade) + except Exception, e: + raise InvalidScriptError(path + ': %s' % str(e)) + return module + + def preview_sql(self, url, step, **args): + """Mocks SQLAlchemy Engine to store all executed calls in a string + and runs :meth:`PythonScript.run <migrate.versioning.script.py.PythonScript.run>` + + :returns: SQL file + """ + buf = StringIO() + args['engine_arg_strategy'] = 'mock' + args['engine_arg_executor'] = lambda s, p = '': buf.write(str(s) + p) + + @with_engine + def go(url, step, **kw): + engine = kw.pop('engine') + self.run(engine, step) + return buf.getvalue() + + return go(url, step, **args) + + def run(self, engine, step): + """Core method of Script file. + Exectues :func:`update` or :func:`downgrade` functions + + :param engine: SQLAlchemy Engine + :param step: Operation to run + :type engine: string + :type step: int + """ + if step > 0: + op = 'upgrade' + elif step < 0: + op = 'downgrade' + else: + raise ScriptError("%d is not a valid step" % step) + + funcname = base.operations[op] + script_func = self._func(funcname) + + try: + script_func(engine) + except TypeError: + warnings.warn("upgrade/downgrade functions must accept engine" + " parameter (since version > 0.5.4)", MigrateDeprecationWarning) + raise + + @property + def module(self): + """Calls :meth:`migrate.versioning.script.py.verify_module` + and returns it. + """ + if not hasattr(self, '_module'): + self._module = self.verify_module(self.path) + return self._module + + def _func(self, funcname): + if not hasattr(self.module, funcname): + msg = "Function '%s' is not defined in this script" + raise ScriptError(msg % funcname) + return getattr(self.module, funcname)