Mercurial > kallithea
comparison 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 |
comparison
equal
deleted
inserted
replaced
832:634596f81cfd | 833:9753e0907827 |
---|---|
1 #!/usr/bin/env python | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 import shutil | |
5 import warnings | |
6 import logging | |
7 from StringIO import StringIO | |
8 | |
9 import migrate | |
10 from migrate.versioning import genmodel, schemadiff | |
11 from migrate.versioning.config import operations | |
12 from migrate.versioning.template import Template | |
13 from migrate.versioning.script import base | |
14 from migrate.versioning.util import import_path, load_model, with_engine | |
15 from migrate.exceptions import MigrateDeprecationWarning, InvalidScriptError, ScriptError | |
16 | |
17 log = logging.getLogger(__name__) | |
18 __all__ = ['PythonScript'] | |
19 | |
20 | |
21 class PythonScript(base.BaseScript): | |
22 """Base for Python scripts""" | |
23 | |
24 @classmethod | |
25 def create(cls, path, **opts): | |
26 """Create an empty migration script at specified path | |
27 | |
28 :returns: :class:`PythonScript instance <migrate.versioning.script.py.PythonScript>`""" | |
29 cls.require_notfound(path) | |
30 | |
31 src = Template(opts.pop('templates_path', None)).get_script(theme=opts.pop('templates_theme', None)) | |
32 shutil.copy(src, path) | |
33 | |
34 return cls(path) | |
35 | |
36 @classmethod | |
37 def make_update_script_for_model(cls, engine, oldmodel, | |
38 model, repository, **opts): | |
39 """Create a migration script based on difference between two SA models. | |
40 | |
41 :param repository: path to migrate repository | |
42 :param oldmodel: dotted.module.name:SAClass or SAClass object | |
43 :param model: dotted.module.name:SAClass or SAClass object | |
44 :param engine: SQLAlchemy engine | |
45 :type repository: string or :class:`Repository instance <migrate.versioning.repository.Repository>` | |
46 :type oldmodel: string or Class | |
47 :type model: string or Class | |
48 :type engine: Engine instance | |
49 :returns: Upgrade / Downgrade script | |
50 :rtype: string | |
51 """ | |
52 | |
53 if isinstance(repository, basestring): | |
54 # oh dear, an import cycle! | |
55 from migrate.versioning.repository import Repository | |
56 repository = Repository(repository) | |
57 | |
58 oldmodel = load_model(oldmodel) | |
59 model = load_model(model) | |
60 | |
61 # Compute differences. | |
62 diff = schemadiff.getDiffOfModelAgainstModel( | |
63 oldmodel, | |
64 model, | |
65 excludeTables=[repository.version_table]) | |
66 # TODO: diff can be False (there is no difference?) | |
67 decls, upgradeCommands, downgradeCommands = \ | |
68 genmodel.ModelGenerator(diff,engine).toUpgradeDowngradePython() | |
69 | |
70 # Store differences into file. | |
71 src = Template(opts.pop('templates_path', None)).get_script(opts.pop('templates_theme', None)) | |
72 f = open(src) | |
73 contents = f.read() | |
74 f.close() | |
75 | |
76 # generate source | |
77 search = 'def upgrade(migrate_engine):' | |
78 contents = contents.replace(search, '\n\n'.join((decls, search)), 1) | |
79 if upgradeCommands: | |
80 contents = contents.replace(' pass', upgradeCommands, 1) | |
81 if downgradeCommands: | |
82 contents = contents.replace(' pass', downgradeCommands, 1) | |
83 return contents | |
84 | |
85 @classmethod | |
86 def verify_module(cls, path): | |
87 """Ensure path is a valid script | |
88 | |
89 :param path: Script location | |
90 :type path: string | |
91 :raises: :exc:`InvalidScriptError <migrate.exceptions.InvalidScriptError>` | |
92 :returns: Python module | |
93 """ | |
94 # Try to import and get the upgrade() func | |
95 module = import_path(path) | |
96 try: | |
97 assert callable(module.upgrade) | |
98 except Exception, e: | |
99 raise InvalidScriptError(path + ': %s' % str(e)) | |
100 return module | |
101 | |
102 def preview_sql(self, url, step, **args): | |
103 """Mocks SQLAlchemy Engine to store all executed calls in a string | |
104 and runs :meth:`PythonScript.run <migrate.versioning.script.py.PythonScript.run>` | |
105 | |
106 :returns: SQL file | |
107 """ | |
108 buf = StringIO() | |
109 args['engine_arg_strategy'] = 'mock' | |
110 args['engine_arg_executor'] = lambda s, p = '': buf.write(str(s) + p) | |
111 | |
112 @with_engine | |
113 def go(url, step, **kw): | |
114 engine = kw.pop('engine') | |
115 self.run(engine, step) | |
116 return buf.getvalue() | |
117 | |
118 return go(url, step, **args) | |
119 | |
120 def run(self, engine, step): | |
121 """Core method of Script file. | |
122 Exectues :func:`update` or :func:`downgrade` functions | |
123 | |
124 :param engine: SQLAlchemy Engine | |
125 :param step: Operation to run | |
126 :type engine: string | |
127 :type step: int | |
128 """ | |
129 if step > 0: | |
130 op = 'upgrade' | |
131 elif step < 0: | |
132 op = 'downgrade' | |
133 else: | |
134 raise ScriptError("%d is not a valid step" % step) | |
135 | |
136 funcname = base.operations[op] | |
137 script_func = self._func(funcname) | |
138 | |
139 try: | |
140 script_func(engine) | |
141 except TypeError: | |
142 warnings.warn("upgrade/downgrade functions must accept engine" | |
143 " parameter (since version > 0.5.4)", MigrateDeprecationWarning) | |
144 raise | |
145 | |
146 @property | |
147 def module(self): | |
148 """Calls :meth:`migrate.versioning.script.py.verify_module` | |
149 and returns it. | |
150 """ | |
151 if not hasattr(self, '_module'): | |
152 self._module = self.verify_module(self.path) | |
153 return self._module | |
154 | |
155 def _func(self, funcname): | |
156 if not hasattr(self.module, funcname): | |
157 msg = "Function '%s' is not defined in this script" | |
158 raise ScriptError(msg % funcname) | |
159 return getattr(self.module, funcname) |