##// END OF EJS Templates
fixes rhodecode upgrade problem that caused setuptool to crash on importing sqlalchemy models
marcink -
r1541:70e646b2 beta
parent child Browse files
Show More
@@ -1,56 +1,56 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.__init__
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 RhodeCode, a web based repository management based on pylons
7 7 versioning implementation: http://semver.org/
8 8
9 9 :created_on: Apr 9, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26 import platform
27 27
28 28 VERSION = (1, 3, 0, 'beta')
29 29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30 30 __dbversion__ = 4 #defines current db version for migrations
31 31 __platform__ = platform.system()
32 32 __license__ = 'GPLv3'
33 33
34 34 PLATFORM_WIN = ('Windows')
35 35 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
36 36
37 37 try:
38 from rhodecode.lib.utils import get_current_revision
38 from rhodecode.lib import get_current_revision
39 39 _rev = get_current_revision()
40 40 except ImportError:
41 41 #this is needed when doing some setup.py operations
42 42 _rev = False
43 43
44 44 if len(VERSION) > 3 and _rev:
45 45 __version__ += ' [rev:%s]' % _rev[0]
46 46
47 47
48 48 def get_version():
49 49 """Returns shorter version (digit parts only) as string."""
50 50
51 51 return '.'.join((str(each) for each in VERSION[:3]))
52 52
53 53 BACKENDS = {
54 54 'hg': 'Mercurial repository',
55 55 #'git': 'Git repository',
56 56 }
@@ -1,381 +1,404 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.__init__
4 4 ~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Some simple helper functions
7 7
8 8 :created_on: Jan 5, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 import os
27
26 28 def __get_lem():
27 29 from pygments import lexers
28 30 from string import lower
29 31 from collections import defaultdict
30 32
31 33 d = defaultdict(lambda: [])
32 34
33 35 def __clean(s):
34 36 s = s.lstrip('*')
35 37 s = s.lstrip('.')
36 38
37 39 if s.find('[') != -1:
38 40 exts = []
39 41 start, stop = s.find('['), s.find(']')
40 42
41 43 for suffix in s[start + 1:stop]:
42 44 exts.append(s[:s.find('[')] + suffix)
43 45 return map(lower, exts)
44 46 else:
45 47 return map(lower, [s])
46 48
47 49 for lx, t in sorted(lexers.LEXERS.items()):
48 50 m = map(__clean, t[-2])
49 51 if m:
50 52 m = reduce(lambda x, y: x + y, m)
51 53 for ext in m:
52 54 desc = lx.replace('Lexer', '')
53 55 d[ext].append(desc)
54 56
55 57 return dict(d)
56 58
57 59 # language map is also used by whoosh indexer, which for those specified
58 60 # extensions will index it's content
59 61 LANGUAGES_EXTENSIONS_MAP = __get_lem()
60 62
61 63 # Additional mappings that are not present in the pygments lexers
62 64 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
63 65 ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
64 66
65 67 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
66 68
67 69
68 70 def str2bool(_str):
69 71 """
70 72 returs True/False value from given string, it tries to translate the
71 73 string into boolean
72 74
73 75 :param _str: string value to translate into boolean
74 76 :rtype: boolean
75 77 :returns: boolean from given string
76 78 """
77 79 if _str is None:
78 80 return False
79 81 if _str in (True, False):
80 82 return _str
81 83 _str = str(_str).strip().lower()
82 84 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
83 85
84 86
85 87 def convert_line_endings(line, mode):
86 88 """
87 89 Converts a given line "line end" accordingly to given mode
88 90
89 91 Available modes are::
90 92 0 - Unix
91 93 1 - Mac
92 94 2 - DOS
93 95
94 96 :param line: given line to convert
95 97 :param mode: mode to convert to
96 98 :rtype: str
97 99 :return: converted line according to mode
98 100 """
99 101 from string import replace
100 102
101 103 if mode == 0:
102 104 line = replace(line, '\r\n', '\n')
103 105 line = replace(line, '\r', '\n')
104 106 elif mode == 1:
105 107 line = replace(line, '\r\n', '\r')
106 108 line = replace(line, '\n', '\r')
107 109 elif mode == 2:
108 110 import re
109 111 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
110 112 return line
111 113
112 114
113 115 def detect_mode(line, default):
114 116 """
115 117 Detects line break for given line, if line break couldn't be found
116 118 given default value is returned
117 119
118 120 :param line: str line
119 121 :param default: default
120 122 :rtype: int
121 123 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
122 124 """
123 125 if line.endswith('\r\n'):
124 126 return 2
125 127 elif line.endswith('\n'):
126 128 return 0
127 129 elif line.endswith('\r'):
128 130 return 1
129 131 else:
130 132 return default
131 133
132 134
133 135 def generate_api_key(username, salt=None):
134 136 """
135 137 Generates unique API key for given username, if salt is not given
136 138 it'll be generated from some random string
137 139
138 140 :param username: username as string
139 141 :param salt: salt to hash generate KEY
140 142 :rtype: str
141 143 :returns: sha1 hash from username+salt
142 144 """
143 145 from tempfile import _RandomNameSequence
144 146 import hashlib
145 147
146 148 if salt is None:
147 149 salt = _RandomNameSequence().next()
148 150
149 151 return hashlib.sha1(username + salt).hexdigest()
150 152
151 153
152 154 def safe_unicode(str_, from_encoding='utf8'):
153 155 """
154 156 safe unicode function. Does few trick to turn str_ into unicode
155 157
156 158 In case of UnicodeDecode error we try to return it with encoding detected
157 159 by chardet library if it fails fallback to unicode with errors replaced
158 160
159 161 :param str_: string to decode
160 162 :rtype: unicode
161 163 :returns: unicode object
162 164 """
163 165 if isinstance(str_, unicode):
164 166 return str_
165 167
166 168 try:
167 169 return unicode(str_)
168 170 except UnicodeDecodeError:
169 171 pass
170 172
171 173 try:
172 174 return unicode(str_, from_encoding)
173 175 except UnicodeDecodeError:
174 176 pass
175 177
176 178 try:
177 179 import chardet
178 180 encoding = chardet.detect(str_)['encoding']
179 181 if encoding is None:
180 182 raise Exception()
181 183 return str_.decode(encoding)
182 184 except (ImportError, UnicodeDecodeError, Exception):
183 185 return unicode(str_, from_encoding, 'replace')
184 186
185 187 def safe_str(unicode_, to_encoding='utf8'):
186 188 """
187 189 safe str function. Does few trick to turn unicode_ into string
188 190
189 191 In case of UnicodeEncodeError we try to return it with encoding detected
190 192 by chardet library if it fails fallback to string with errors replaced
191 193
192 194 :param unicode_: unicode to encode
193 195 :rtype: str
194 196 :returns: str object
195 197 """
196 198
197 199 if isinstance(unicode_, str):
198 200 return unicode_
199 201
200 202 try:
201 203 return unicode_.encode(to_encoding)
202 204 except UnicodeEncodeError:
203 205 pass
204 206
205 207 try:
206 208 import chardet
207 209 encoding = chardet.detect(unicode_)['encoding']
208 210 print encoding
209 211 if encoding is None:
210 212 raise UnicodeEncodeError()
211 213
212 214 return unicode_.encode(encoding)
213 215 except (ImportError, UnicodeEncodeError):
214 216 return unicode_.encode(to_encoding, 'replace')
215 217
216 218 return safe_str
217 219
218 220
219 221
220 222 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
221 223 """
222 224 Custom engine_from_config functions that makes sure we use NullPool for
223 225 file based sqlite databases. This prevents errors on sqlite. This only
224 226 applies to sqlalchemy versions < 0.7.0
225 227
226 228 """
227 229 import sqlalchemy
228 230 from sqlalchemy import engine_from_config as efc
229 231 import logging
230 232
231 233 if int(sqlalchemy.__version__.split('.')[1]) < 7:
232 234
233 235 # This solution should work for sqlalchemy < 0.7.0, and should use
234 236 # proxy=TimerProxy() for execution time profiling
235 237
236 238 from sqlalchemy.pool import NullPool
237 239 url = configuration[prefix + 'url']
238 240
239 241 if url.startswith('sqlite'):
240 242 kwargs.update({'poolclass': NullPool})
241 243 return efc(configuration, prefix, **kwargs)
242 244 else:
243 245 import time
244 246 from sqlalchemy import event
245 247 from sqlalchemy.engine import Engine
246 248
247 249 log = logging.getLogger('sqlalchemy.engine')
248 250 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
249 251 engine = efc(configuration, prefix, **kwargs)
250 252
251 253 def color_sql(sql):
252 254 COLOR_SEQ = "\033[1;%dm"
253 255 COLOR_SQL = YELLOW
254 256 normal = '\x1b[0m'
255 257 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
256 258
257 259 if configuration['debug']:
258 260 #attach events only for debug configuration
259 261
260 262 def before_cursor_execute(conn, cursor, statement,
261 263 parameters, context, executemany):
262 264 context._query_start_time = time.time()
263 265 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
264 266
265 267
266 268 def after_cursor_execute(conn, cursor, statement,
267 269 parameters, context, executemany):
268 270 total = time.time() - context._query_start_time
269 271 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
270 272
271 273 event.listen(engine, "before_cursor_execute",
272 274 before_cursor_execute)
273 275 event.listen(engine, "after_cursor_execute",
274 276 after_cursor_execute)
275 277
276 278 return engine
277 279
278 280
279 281 def age(curdate):
280 282 """
281 283 turns a datetime into an age string.
282 284
283 285 :param curdate: datetime object
284 286 :rtype: unicode
285 287 :returns: unicode words describing age
286 288 """
287 289
288 290 from datetime import datetime
289 291 from webhelpers.date import time_ago_in_words
290 292
291 293 _ = lambda s:s
292 294
293 295 if not curdate:
294 296 return ''
295 297
296 298 agescales = [(_(u"year"), 3600 * 24 * 365),
297 299 (_(u"month"), 3600 * 24 * 30),
298 300 (_(u"day"), 3600 * 24),
299 301 (_(u"hour"), 3600),
300 302 (_(u"minute"), 60),
301 303 (_(u"second"), 1), ]
302 304
303 305 age = datetime.now() - curdate
304 306 age_seconds = (age.days * agescales[2][1]) + age.seconds
305 307 pos = 1
306 308 for scale in agescales:
307 309 if scale[1] <= age_seconds:
308 310 if pos == 6:pos = 5
309 311 return '%s %s' % (time_ago_in_words(curdate,
310 312 agescales[pos][0]), _('ago'))
311 313 pos += 1
312 314
313 315 return _(u'just now')
314 316
315 317
316 318 def uri_filter(uri):
317 319 """
318 320 Removes user:password from given url string
319 321
320 322 :param uri:
321 323 :rtype: unicode
322 324 :returns: filtered list of strings
323 325 """
324 326 if not uri:
325 327 return ''
326 328
327 329 proto = ''
328 330
329 331 for pat in ('https://', 'http://'):
330 332 if uri.startswith(pat):
331 333 uri = uri[len(pat):]
332 334 proto = pat
333 335 break
334 336
335 337 # remove passwords and username
336 338 uri = uri[uri.find('@') + 1:]
337 339
338 340 # get the port
339 341 cred_pos = uri.find(':')
340 342 if cred_pos == -1:
341 343 host, port = uri, None
342 344 else:
343 345 host, port = uri[:cred_pos], uri[cred_pos + 1:]
344 346
345 347 return filter(None, [proto, host, port])
346 348
347 349
348 350 def credentials_filter(uri):
349 351 """
350 352 Returns a url with removed credentials
351 353
352 354 :param uri:
353 355 """
354 356
355 357 uri = uri_filter(uri)
356 358 #check if we have port
357 359 if len(uri) > 2 and uri[2]:
358 360 uri[2] = ':' + uri[2]
359 361
360 362 return ''.join(uri)
361 363
362 364 def get_changeset_safe(repo, rev):
363 365 """
364 366 Safe version of get_changeset if this changeset doesn't exists for a
365 367 repo it returns a Dummy one instead
366 368
367 369 :param repo:
368 370 :param rev:
369 371 """
370 372 from vcs.backends.base import BaseRepository
371 373 from vcs.exceptions import RepositoryError
372 374 if not isinstance(repo, BaseRepository):
373 375 raise Exception('You must pass an Repository '
374 376 'object as first argument got %s', type(repo))
375 377
376 378 try:
377 379 cs = repo.get_changeset(rev)
378 380 except RepositoryError:
379 381 from rhodecode.lib.utils import EmptyChangeset
380 382 cs = EmptyChangeset(requested_revision=rev)
381 383 return cs
384
385
386 def get_current_revision():
387 """
388 Returns tuple of (number, id) from repository containing this package
389 or None if repository could not be found.
390 """
391
392 try:
393 from vcs import get_repo
394 from vcs.utils.helpers import get_scm
395 from vcs.exceptions import RepositoryError, VCSError
396 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
397 scm = get_scm(repopath)[0]
398 repo = get_repo(path=repopath, alias=scm)
399 tip = repo.get_changeset()
400 return (tip.revision, tip.short_id)
401 except (ImportError, RepositoryError, VCSError), err:
402 print ("Cannot retrieve rhodecode's revision. Original error "
403 "was: %s" % err)
404 return None
@@ -1,614 +1,594 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.utils
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Utilities library for RhodeCode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import datetime
29 29 import traceback
30 30 import paste
31 31 import beaker
32 32 from os.path import dirname as dn, join as jn
33 33
34 34 from paste.script.command import Command, BadCommand
35 35
36 36 from mercurial import ui, config
37 37
38 38 from webhelpers.text import collapse, remove_formatting, strip_tags
39 39
40 40 from vcs import get_backend
41 41 from vcs.backends.base import BaseChangeset
42 42 from vcs.utils.lazy import LazyProperty
43 43 from vcs.utils.helpers import get_scm
44 44 from vcs.exceptions import VCSError
45 45
46 46 from rhodecode.model import meta
47 47 from rhodecode.model.caching_query import FromCache
48 48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
49 49 RhodeCodeSettings
50 50 from rhodecode.model.repo import RepoModel
51 51
52 52 log = logging.getLogger(__name__)
53 53
54 54
55 55 def recursive_replace(str, replace=' '):
56 56 """Recursive replace of given sign to just one instance
57 57
58 58 :param str: given string
59 59 :param replace: char to find and replace multiple instances
60 60
61 61 Examples::
62 62 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
63 63 'Mighty-Mighty-Bo-sstones'
64 64 """
65 65
66 66 if str.find(replace * 2) == -1:
67 67 return str
68 68 else:
69 69 str = str.replace(replace * 2, replace)
70 70 return recursive_replace(str, replace)
71 71
72 72
73 73 def repo_name_slug(value):
74 74 """Return slug of name of repository
75 75 This function is called on each creation/modification
76 76 of repository to prevent bad names in repo
77 77 """
78 78
79 79 slug = remove_formatting(value)
80 80 slug = strip_tags(slug)
81 81
82 82 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
83 83 slug = slug.replace(c, '-')
84 84 slug = recursive_replace(slug, '-')
85 85 slug = collapse(slug, '-')
86 86 return slug
87 87
88 88
89 89 def get_repo_slug(request):
90 90 return request.environ['pylons.routes_dict'].get('repo_name')
91 91
92 92
93 93 def action_logger(user, action, repo, ipaddr='', sa=None):
94 94 """
95 95 Action logger for various actions made by users
96 96
97 97 :param user: user that made this action, can be a unique username string or
98 98 object containing user_id attribute
99 99 :param action: action to log, should be on of predefined unique actions for
100 100 easy translations
101 101 :param repo: string name of repository or object containing repo_id,
102 102 that action was made on
103 103 :param ipaddr: optional ip address from what the action was made
104 104 :param sa: optional sqlalchemy session
105 105
106 106 """
107 107
108 108 if not sa:
109 109 sa = meta.Session()
110 110
111 111 try:
112 112 if hasattr(user, 'user_id'):
113 113 user_obj = user
114 114 elif isinstance(user, basestring):
115 115 user_obj = User.get_by_username(user)
116 116 else:
117 117 raise Exception('You have to provide user object or username')
118 118
119 119 rm = RepoModel()
120 120 if hasattr(repo, 'repo_id'):
121 121 repo_obj = rm.get(repo.repo_id, cache=False)
122 122 repo_name = repo_obj.repo_name
123 123 elif isinstance(repo, basestring):
124 124 repo_name = repo.lstrip('/')
125 125 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
126 126 else:
127 127 raise Exception('You have to provide repository to action logger')
128 128
129 129 user_log = UserLog()
130 130 user_log.user_id = user_obj.user_id
131 131 user_log.action = action
132 132
133 133 user_log.repository_id = repo_obj.repo_id
134 134 user_log.repository_name = repo_name
135 135
136 136 user_log.action_date = datetime.datetime.now()
137 137 user_log.user_ip = ipaddr
138 138 sa.add(user_log)
139 139 sa.commit()
140 140
141 141 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
142 142 except:
143 143 log.error(traceback.format_exc())
144 144 sa.rollback()
145 145
146 146
147 147 def get_repos(path, recursive=False):
148 148 """
149 149 Scans given path for repos and return (name,(type,path)) tuple
150 150
151 151 :param path: path to scann for repositories
152 152 :param recursive: recursive search and return names with subdirs in front
153 153 """
154 154 from vcs.utils.helpers import get_scm
155 155 from vcs.exceptions import VCSError
156 156
157 157 if path.endswith(os.sep):
158 158 #remove ending slash for better results
159 159 path = path[:-1]
160 160
161 161 def _get_repos(p):
162 162 if not os.access(p, os.W_OK):
163 163 return
164 164 for dirpath in os.listdir(p):
165 165 if os.path.isfile(os.path.join(p, dirpath)):
166 166 continue
167 167 cur_path = os.path.join(p, dirpath)
168 168 try:
169 169 scm_info = get_scm(cur_path)
170 170 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
171 171 except VCSError:
172 172 if not recursive:
173 173 continue
174 174 #check if this dir containts other repos for recursive scan
175 175 rec_path = os.path.join(p, dirpath)
176 176 if os.path.isdir(rec_path):
177 177 for inner_scm in _get_repos(rec_path):
178 178 yield inner_scm
179 179
180 180 return _get_repos(path)
181 181
182 182
183 183 def is_valid_repo(repo_name, base_path):
184 184 """
185 185 Returns True if given path is a valid repository False otherwise
186 186 :param repo_name:
187 187 :param base_path:
188 188
189 189 :return True: if given path is a valid repository
190 190 """
191 191 full_path = os.path.join(base_path, repo_name)
192 192
193 193 try:
194 194 get_scm(full_path)
195 195 return True
196 196 except VCSError:
197 197 return False
198 198
199 199 def is_valid_repos_group(repos_group_name, base_path):
200 200 """
201 201 Returns True if given path is a repos group False otherwise
202 202
203 203 :param repo_name:
204 204 :param base_path:
205 205 """
206 206 full_path = os.path.join(base_path, repos_group_name)
207 207
208 208 # check if it's not a repo
209 209 if is_valid_repo(repos_group_name, base_path):
210 210 return False
211 211
212 212 # check if it's a valid path
213 213 if os.path.isdir(full_path):
214 214 return True
215 215
216 216 return False
217 217
218 218 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
219 219 while True:
220 220 ok = raw_input(prompt)
221 221 if ok in ('y', 'ye', 'yes'):
222 222 return True
223 223 if ok in ('n', 'no', 'nop', 'nope'):
224 224 return False
225 225 retries = retries - 1
226 226 if retries < 0:
227 227 raise IOError
228 228 print complaint
229 229
230 230 #propagated from mercurial documentation
231 231 ui_sections = ['alias', 'auth',
232 232 'decode/encode', 'defaults',
233 233 'diff', 'email',
234 234 'extensions', 'format',
235 235 'merge-patterns', 'merge-tools',
236 236 'hooks', 'http_proxy',
237 237 'smtp', 'patch',
238 238 'paths', 'profiling',
239 239 'server', 'trusted',
240 240 'ui', 'web', ]
241 241
242 242
243 243 def make_ui(read_from='file', path=None, checkpaths=True):
244 244 """A function that will read python rc files or database
245 245 and make an mercurial ui object from read options
246 246
247 247 :param path: path to mercurial config file
248 248 :param checkpaths: check the path
249 249 :param read_from: read from 'file' or 'db'
250 250 """
251 251
252 252 baseui = ui.ui()
253 253
254 254 #clean the baseui object
255 255 baseui._ocfg = config.config()
256 256 baseui._ucfg = config.config()
257 257 baseui._tcfg = config.config()
258 258
259 259 if read_from == 'file':
260 260 if not os.path.isfile(path):
261 261 log.warning('Unable to read config file %s' % path)
262 262 return False
263 263 log.debug('reading hgrc from %s', path)
264 264 cfg = config.config()
265 265 cfg.read(path)
266 266 for section in ui_sections:
267 267 for k, v in cfg.items(section):
268 268 log.debug('settings ui from file[%s]%s:%s', section, k, v)
269 269 baseui.setconfig(section, k, v)
270 270
271 271 elif read_from == 'db':
272 272 sa = meta.Session()
273 273 ret = sa.query(RhodeCodeUi)\
274 274 .options(FromCache("sql_cache_short",
275 275 "get_hg_ui_settings")).all()
276 276
277 277 hg_ui = ret
278 278 for ui_ in hg_ui:
279 279 if ui_.ui_active:
280 280 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
281 281 ui_.ui_key, ui_.ui_value)
282 282 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
283 283
284 284 meta.Session.remove()
285 285 return baseui
286 286
287 287
288 288 def set_rhodecode_config(config):
289 289 """Updates pylons config with new settings from database
290 290
291 291 :param config:
292 292 """
293 293 hgsettings = RhodeCodeSettings.get_app_settings()
294 294
295 295 for k, v in hgsettings.items():
296 296 config[k] = v
297 297
298 298
299 299 def invalidate_cache(cache_key, *args):
300 300 """Puts cache invalidation task into db for
301 301 further global cache invalidation
302 302 """
303 303
304 304 from rhodecode.model.scm import ScmModel
305 305
306 306 if cache_key.startswith('get_repo_cached_'):
307 307 name = cache_key.split('get_repo_cached_')[-1]
308 308 ScmModel().mark_for_invalidation(name)
309 309
310 310
311 311 class EmptyChangeset(BaseChangeset):
312 312 """
313 313 An dummy empty changeset. It's possible to pass hash when creating
314 314 an EmptyChangeset
315 315 """
316 316
317 317 def __init__(self, cs='0' * 40, repo=None, requested_revision=None, alias=None):
318 318 self._empty_cs = cs
319 319 self.revision = -1
320 320 self.message = ''
321 321 self.author = ''
322 322 self.date = ''
323 323 self.repository = repo
324 324 self.requested_revision = requested_revision
325 325 self.alias = alias
326 326
327 327 @LazyProperty
328 328 def raw_id(self):
329 329 """Returns raw string identifying this changeset, useful for web
330 330 representation.
331 331 """
332 332
333 333 return self._empty_cs
334 334
335 335 @LazyProperty
336 336 def branch(self):
337 337 return get_backend(self.alias).DEFAULT_BRANCH_NAME
338 338
339 339 @LazyProperty
340 340 def short_id(self):
341 341 return self.raw_id[:12]
342 342
343 343 def get_file_changeset(self, path):
344 344 return self
345 345
346 346 def get_file_content(self, path):
347 347 return u''
348 348
349 349 def get_file_size(self, path):
350 350 return 0
351 351
352 352
353 353 def map_groups(groups):
354 354 """Checks for groups existence, and creates groups structures.
355 355 It returns last group in structure
356 356
357 357 :param groups: list of groups structure
358 358 """
359 359 sa = meta.Session()
360 360
361 361 parent = None
362 362 group = None
363 363
364 364 # last element is repo in nested groups structure
365 365 groups = groups[:-1]
366 366
367 367 for lvl, group_name in enumerate(groups):
368 368 group_name = '/'.join(groups[:lvl] + [group_name])
369 369 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
370 370
371 371 if group is None:
372 372 group = Group(group_name, parent)
373 373 sa.add(group)
374 374 sa.commit()
375 375 parent = group
376 376 return group
377 377
378 378
379 379 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
380 380 """maps all repos given in initial_repo_list, non existing repositories
381 381 are created, if remove_obsolete is True it also check for db entries
382 382 that are not in initial_repo_list and removes them.
383 383
384 384 :param initial_repo_list: list of repositories found by scanning methods
385 385 :param remove_obsolete: check for obsolete entries in database
386 386 """
387 387
388 388 sa = meta.Session()
389 389 rm = RepoModel()
390 390 user = sa.query(User).filter(User.admin == True).first()
391 391 added = []
392 392 for name, repo in initial_repo_list.items():
393 393 group = map_groups(name.split(os.sep))
394 394 if not rm.get_by_repo_name(name, cache=False):
395 395 log.info('repository %s not found creating default', name)
396 396 added.append(name)
397 397 form_data = {
398 398 'repo_name': name,
399 399 'repo_name_full': name,
400 400 'repo_type': repo.alias,
401 401 'description': repo.description \
402 402 if repo.description != 'unknown' else \
403 403 '%s repository' % name,
404 404 'private': False,
405 405 'group_id': getattr(group, 'group_id', None)
406 406 }
407 407 rm.create(form_data, user, just_db=True)
408 408
409 409 removed = []
410 410 if remove_obsolete:
411 411 #remove from database those repositories that are not in the filesystem
412 412 for repo in sa.query(Repository).all():
413 413 if repo.repo_name not in initial_repo_list.keys():
414 414 removed.append(repo.repo_name)
415 415 sa.delete(repo)
416 416 sa.commit()
417 417
418 418 return added, removed
419 419
420 420 #set cache regions for beaker so celery can utilise it
421 421 def add_cache(settings):
422 422 cache_settings = {'regions': None}
423 423 for key in settings.keys():
424 424 for prefix in ['beaker.cache.', 'cache.']:
425 425 if key.startswith(prefix):
426 426 name = key.split(prefix)[1].strip()
427 427 cache_settings[name] = settings[key].strip()
428 428 if cache_settings['regions']:
429 429 for region in cache_settings['regions'].split(','):
430 430 region = region.strip()
431 431 region_settings = {}
432 432 for key, value in cache_settings.items():
433 433 if key.startswith(region):
434 434 region_settings[key.split('.')[1]] = value
435 435 region_settings['expire'] = int(region_settings.get('expire',
436 436 60))
437 437 region_settings.setdefault('lock_dir',
438 438 cache_settings.get('lock_dir'))
439 439 region_settings.setdefault('data_dir',
440 440 cache_settings.get('data_dir'))
441 441
442 442 if 'type' not in region_settings:
443 443 region_settings['type'] = cache_settings.get('type',
444 444 'memory')
445 445 beaker.cache.cache_regions[region] = region_settings
446 446
447 447
448 def get_current_revision():
449 """Returns tuple of (number, id) from repository containing this package
450 or None if repository could not be found.
451 """
452
453 try:
454 from vcs import get_repo
455 from vcs.utils.helpers import get_scm
456 from vcs.exceptions import RepositoryError, VCSError
457 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
458 scm = get_scm(repopath)[0]
459 repo = get_repo(path=repopath, alias=scm)
460 tip = repo.get_changeset()
461 return (tip.revision, tip.short_id)
462 except (ImportError, RepositoryError, VCSError), err:
463 logging.debug("Cannot retrieve rhodecode's revision. Original error "
464 "was: %s" % err)
465 return None
466
467
468 448 #==============================================================================
469 449 # TEST FUNCTIONS AND CREATORS
470 450 #==============================================================================
471 451 def create_test_index(repo_location, config, full_index):
472 452 """
473 453 Makes default test index
474 454
475 455 :param config: test config
476 456 :param full_index:
477 457 """
478 458
479 459 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
480 460 from rhodecode.lib.pidlock import DaemonLock, LockHeld
481 461
482 462 repo_location = repo_location
483 463
484 464 index_location = os.path.join(config['app_conf']['index_dir'])
485 465 if not os.path.exists(index_location):
486 466 os.makedirs(index_location)
487 467
488 468 try:
489 469 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
490 470 WhooshIndexingDaemon(index_location=index_location,
491 471 repo_location=repo_location)\
492 472 .run(full_index=full_index)
493 473 l.release()
494 474 except LockHeld:
495 475 pass
496 476
497 477
498 478 def create_test_env(repos_test_path, config):
499 479 """Makes a fresh database and
500 480 install test repository into tmp dir
501 481 """
502 482 from rhodecode.lib.db_manage import DbManage
503 483 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
504 484 HG_FORK, GIT_FORK, TESTS_TMP_PATH
505 485 import tarfile
506 486 import shutil
507 487 from os.path import abspath
508 488
509 489 # PART ONE create db
510 490 dbconf = config['sqlalchemy.db1.url']
511 491 log.debug('making test db %s', dbconf)
512 492
513 493 # create test dir if it doesn't exist
514 494 if not os.path.isdir(repos_test_path):
515 495 log.debug('Creating testdir %s' % repos_test_path)
516 496 os.makedirs(repos_test_path)
517 497
518 498 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
519 499 tests=True)
520 500 dbmanage.create_tables(override=True)
521 501 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
522 502 dbmanage.create_default_user()
523 503 dbmanage.admin_prompt()
524 504 dbmanage.create_permissions()
525 505 dbmanage.populate_default_permissions()
526 506
527 507 # PART TWO make test repo
528 508 log.debug('making test vcs repositories')
529 509
530 510 idx_path = config['app_conf']['index_dir']
531 511 data_path = config['app_conf']['cache_dir']
532 512
533 513 #clean index and data
534 514 if idx_path and os.path.exists(idx_path):
535 515 log.debug('remove %s' % idx_path)
536 516 shutil.rmtree(idx_path)
537 517
538 518 if data_path and os.path.exists(data_path):
539 519 log.debug('remove %s' % data_path)
540 520 shutil.rmtree(data_path)
541 521
542 522 #CREATE DEFAULT HG REPOSITORY
543 523 cur_dir = dn(dn(abspath(__file__)))
544 524 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
545 525 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
546 526 tar.close()
547 527
548 528
549 529 #==============================================================================
550 530 # PASTER COMMANDS
551 531 #==============================================================================
552 532 class BasePasterCommand(Command):
553 533 """
554 534 Abstract Base Class for paster commands.
555 535
556 536 The celery commands are somewhat aggressive about loading
557 537 celery.conf, and since our module sets the `CELERY_LOADER`
558 538 environment variable to our loader, we have to bootstrap a bit and
559 539 make sure we've had a chance to load the pylons config off of the
560 540 command line, otherwise everything fails.
561 541 """
562 542 min_args = 1
563 543 min_args_error = "Please provide a paster config file as an argument."
564 544 takes_config_file = 1
565 545 requires_config_file = True
566 546
567 547 def notify_msg(self, msg, log=False):
568 548 """Make a notification to user, additionally if logger is passed
569 549 it logs this action using given logger
570 550
571 551 :param msg: message that will be printed to user
572 552 :param log: logging instance, to use to additionally log this message
573 553
574 554 """
575 555 if log and isinstance(log, logging):
576 556 log(msg)
577 557
578 558 def run(self, args):
579 559 """
580 560 Overrides Command.run
581 561
582 562 Checks for a config file argument and loads it.
583 563 """
584 564 if len(args) < self.min_args:
585 565 raise BadCommand(
586 566 self.min_args_error % {'min_args': self.min_args,
587 567 'actual_args': len(args)})
588 568
589 569 # Decrement because we're going to lob off the first argument.
590 570 # @@ This is hacky
591 571 self.min_args -= 1
592 572 self.bootstrap_config(args[0])
593 573 self.update_parser()
594 574 return super(BasePasterCommand, self).run(args[1:])
595 575
596 576 def update_parser(self):
597 577 """
598 578 Abstract method. Allows for the class's parser to be updated
599 579 before the superclass's `run` method is called. Necessary to
600 580 allow options/arguments to be passed through to the underlying
601 581 celery command.
602 582 """
603 583 raise NotImplementedError("Abstract Method.")
604 584
605 585 def bootstrap_config(self, conf):
606 586 """
607 587 Loads the pylons configuration.
608 588 """
609 589 from pylons import config as pylonsconfig
610 590
611 591 path_to_ini_file = os.path.realpath(conf)
612 592 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
613 593 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
614 594
General Comments 0
You need to be logged in to leave comments. Login now