comparison rhodecode/lib/dbmigrate/migrate/versioning/util/__init__.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 """.. currentmodule:: migrate.versioning.util"""
4
5 import warnings
6 import logging
7 from decorator import decorator
8 from pkg_resources import EntryPoint
9
10 from sqlalchemy import create_engine
11 from sqlalchemy.engine import Engine
12 from sqlalchemy.pool import StaticPool
13
14 from migrate import exceptions
15 from migrate.versioning.util.keyedinstance import KeyedInstance
16 from migrate.versioning.util.importpath import import_path
17
18
19 log = logging.getLogger(__name__)
20
21 def load_model(dotted_name):
22 """Import module and use module-level variable".
23
24 :param dotted_name: path to model in form of string: ``some.python.module:Class``
25
26 .. versionchanged:: 0.5.4
27
28 """
29 if isinstance(dotted_name, basestring):
30 if ':' not in dotted_name:
31 # backwards compatibility
32 warnings.warn('model should be in form of module.model:User '
33 'and not module.model.User', exceptions.MigrateDeprecationWarning)
34 dotted_name = ':'.join(dotted_name.rsplit('.', 1))
35 return EntryPoint.parse('x=%s' % dotted_name).load(False)
36 else:
37 # Assume it's already loaded.
38 return dotted_name
39
40 def asbool(obj):
41 """Do everything to use object as bool"""
42 if isinstance(obj, basestring):
43 obj = obj.strip().lower()
44 if obj in ['true', 'yes', 'on', 'y', 't', '1']:
45 return True
46 elif obj in ['false', 'no', 'off', 'n', 'f', '0']:
47 return False
48 else:
49 raise ValueError("String is not true/false: %r" % obj)
50 if obj in (True, False):
51 return bool(obj)
52 else:
53 raise ValueError("String is not true/false: %r" % obj)
54
55 def guess_obj_type(obj):
56 """Do everything to guess object type from string
57
58 Tries to convert to `int`, `bool` and finally returns if not succeded.
59
60 .. versionadded: 0.5.4
61 """
62
63 result = None
64
65 try:
66 result = int(obj)
67 except:
68 pass
69
70 if result is None:
71 try:
72 result = asbool(obj)
73 except:
74 pass
75
76 if result is not None:
77 return result
78 else:
79 return obj
80
81 @decorator
82 def catch_known_errors(f, *a, **kw):
83 """Decorator that catches known api errors
84
85 .. versionadded: 0.5.4
86 """
87
88 try:
89 return f(*a, **kw)
90 except exceptions.PathFoundError, e:
91 raise exceptions.KnownError("The path %s already exists" % e.args[0])
92
93 def construct_engine(engine, **opts):
94 """.. versionadded:: 0.5.4
95
96 Constructs and returns SQLAlchemy engine.
97
98 Currently, there are 2 ways to pass create_engine options to :mod:`migrate.versioning.api` functions:
99
100 :param engine: connection string or a existing engine
101 :param engine_dict: python dictionary of options to pass to `create_engine`
102 :param engine_arg_*: keyword parameters to pass to `create_engine` (evaluated with :func:`migrate.versioning.util.guess_obj_type`)
103 :type engine_dict: dict
104 :type engine: string or Engine instance
105 :type engine_arg_*: string
106 :returns: SQLAlchemy Engine
107
108 .. note::
109
110 keyword parameters override ``engine_dict`` values.
111
112 """
113 if isinstance(engine, Engine):
114 return engine
115 elif not isinstance(engine, basestring):
116 raise ValueError("you need to pass either an existing engine or a database uri")
117
118 # get options for create_engine
119 if opts.get('engine_dict') and isinstance(opts['engine_dict'], dict):
120 kwargs = opts['engine_dict']
121 else:
122 kwargs = dict()
123
124 # DEPRECATED: handle echo the old way
125 echo = asbool(opts.get('echo', False))
126 if echo:
127 warnings.warn('echo=True parameter is deprecated, pass '
128 'engine_arg_echo=True or engine_dict={"echo": True}',
129 exceptions.MigrateDeprecationWarning)
130 kwargs['echo'] = echo
131
132 # parse keyword arguments
133 for key, value in opts.iteritems():
134 if key.startswith('engine_arg_'):
135 kwargs[key[11:]] = guess_obj_type(value)
136
137 log.debug('Constructing engine')
138 # TODO: return create_engine(engine, poolclass=StaticPool, **kwargs)
139 # seems like 0.5.x branch does not work with engine.dispose and staticpool
140 return create_engine(engine, **kwargs)
141
142 @decorator
143 def with_engine(f, *a, **kw):
144 """Decorator for :mod:`migrate.versioning.api` functions
145 to safely close resources after function usage.
146
147 Passes engine parameters to :func:`construct_engine` and
148 resulting parameter is available as kw['engine'].
149
150 Engine is disposed after wrapped function is executed.
151
152 .. versionadded: 0.6.0
153 """
154 url = a[0]
155 engine = construct_engine(url, **kw)
156
157 try:
158 kw['engine'] = engine
159 return f(*a, **kw)
160 finally:
161 if isinstance(engine, Engine):
162 log.debug('Disposing SQLAlchemy engine %s', engine)
163 engine.dispose()
164
165
166 class Memoize:
167 """Memoize(fn) - an instance which acts like fn but memoizes its arguments
168 Will only work on functions with non-mutable arguments
169
170 ActiveState Code 52201
171 """
172 def __init__(self, fn):
173 self.fn = fn
174 self.memo = {}
175
176 def __call__(self, *args):
177 if not self.memo.has_key(args):
178 self.memo[args] = self.fn(*args)
179 return self.memo[args]