##// END OF EJS Templates
removed leftover print
marcink -
r1243:e82d6aaa beta
parent child Browse files
Show More
@@ -1,707 +1,706 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import paste
30 import paste
31 import beaker
31 import beaker
32
32
33 from paste.script.command import Command, BadCommand
33 from paste.script.command import Command, BadCommand
34
34
35 from UserDict import DictMixin
35 from UserDict import DictMixin
36
36
37 from mercurial import ui, config, hg
37 from mercurial import ui, config, hg
38 from mercurial.error import RepoError
38 from mercurial.error import RepoError
39
39
40 from webhelpers.text import collapse, remove_formatting, strip_tags
40 from webhelpers.text import collapse, remove_formatting, strip_tags
41
41
42 from vcs.backends.base import BaseChangeset
42 from vcs.backends.base import BaseChangeset
43 from vcs.utils.lazy import LazyProperty
43 from vcs.utils.lazy import LazyProperty
44
44
45 from rhodecode.model import meta
45 from rhodecode.model import meta
46 from rhodecode.model.caching_query import FromCache
46 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group
47 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group
48 from rhodecode.model.repo import RepoModel
48 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.user import UserModel
49 from rhodecode.model.user import UserModel
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 def recursive_replace(str, replace=' '):
54 def recursive_replace(str, replace=' '):
55 """Recursive replace of given sign to just one instance
55 """Recursive replace of given sign to just one instance
56
56
57 :param str: given string
57 :param str: given string
58 :param replace: char to find and replace multiple instances
58 :param replace: char to find and replace multiple instances
59
59
60 Examples::
60 Examples::
61 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
61 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
62 'Mighty-Mighty-Bo-sstones'
62 'Mighty-Mighty-Bo-sstones'
63 """
63 """
64
64
65 if str.find(replace * 2) == -1:
65 if str.find(replace * 2) == -1:
66 return str
66 return str
67 else:
67 else:
68 str = str.replace(replace * 2, replace)
68 str = str.replace(replace * 2, replace)
69 return recursive_replace(str, replace)
69 return recursive_replace(str, replace)
70
70
71
71
72 def repo_name_slug(value):
72 def repo_name_slug(value):
73 """Return slug of name of repository
73 """Return slug of name of repository
74 This function is called on each creation/modification
74 This function is called on each creation/modification
75 of repository to prevent bad names in repo
75 of repository to prevent bad names in repo
76 """
76 """
77
77
78 slug = remove_formatting(value)
78 slug = remove_formatting(value)
79 slug = strip_tags(slug)
79 slug = strip_tags(slug)
80
80
81 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
81 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
82 slug = slug.replace(c, '-')
82 slug = slug.replace(c, '-')
83 slug = recursive_replace(slug, '-')
83 slug = recursive_replace(slug, '-')
84 slug = collapse(slug, '-')
84 slug = collapse(slug, '-')
85 return slug
85 return slug
86
86
87
87
88 def get_repo_slug(request):
88 def get_repo_slug(request):
89 return request.environ['pylons.routes_dict'].get('repo_name')
89 return request.environ['pylons.routes_dict'].get('repo_name')
90
90
91
91
92 def action_logger(user, action, repo, ipaddr='', sa=None):
92 def action_logger(user, action, repo, ipaddr='', sa=None):
93 """
93 """
94 Action logger for various actions made by users
94 Action logger for various actions made by users
95
95
96 :param user: user that made this action, can be a unique username string or
96 :param user: user that made this action, can be a unique username string or
97 object containing user_id attribute
97 object containing user_id attribute
98 :param action: action to log, should be on of predefined unique actions for
98 :param action: action to log, should be on of predefined unique actions for
99 easy translations
99 easy translations
100 :param repo: string name of repository or object containing repo_id,
100 :param repo: string name of repository or object containing repo_id,
101 that action was made on
101 that action was made on
102 :param ipaddr: optional ip address from what the action was made
102 :param ipaddr: optional ip address from what the action was made
103 :param sa: optional sqlalchemy session
103 :param sa: optional sqlalchemy session
104
104
105 """
105 """
106
106
107 if not sa:
107 if not sa:
108 sa = meta.Session()
108 sa = meta.Session()
109
109
110 try:
110 try:
111 um = UserModel()
111 um = UserModel()
112 if hasattr(user, 'user_id'):
112 if hasattr(user, 'user_id'):
113 user_obj = user
113 user_obj = user
114 elif isinstance(user, basestring):
114 elif isinstance(user, basestring):
115 user_obj = um.get_by_username(user, cache=False)
115 user_obj = um.get_by_username(user, cache=False)
116 else:
116 else:
117 raise Exception('You have to provide user object or username')
117 raise Exception('You have to provide user object or username')
118
118
119 rm = RepoModel()
119 rm = RepoModel()
120 if hasattr(repo, 'repo_id'):
120 if hasattr(repo, 'repo_id'):
121 repo_obj = rm.get(repo.repo_id, cache=False)
121 repo_obj = rm.get(repo.repo_id, cache=False)
122 repo_name = repo_obj.repo_name
122 repo_name = repo_obj.repo_name
123 elif isinstance(repo, basestring):
123 elif isinstance(repo, basestring):
124 repo_name = repo.lstrip('/')
124 repo_name = repo.lstrip('/')
125 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
125 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
126 else:
126 else:
127 raise Exception('You have to provide repository to action logger')
127 raise Exception('You have to provide repository to action logger')
128
128
129 user_log = UserLog()
129 user_log = UserLog()
130 user_log.user_id = user_obj.user_id
130 user_log.user_id = user_obj.user_id
131 user_log.action = action
131 user_log.action = action
132
132
133 user_log.repository_id = repo_obj.repo_id
133 user_log.repository_id = repo_obj.repo_id
134 user_log.repository_name = repo_name
134 user_log.repository_name = repo_name
135
135
136 user_log.action_date = datetime.datetime.now()
136 user_log.action_date = datetime.datetime.now()
137 user_log.user_ip = ipaddr
137 user_log.user_ip = ipaddr
138 sa.add(user_log)
138 sa.add(user_log)
139 sa.commit()
139 sa.commit()
140
140
141 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
141 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
142 except:
142 except:
143 log.error(traceback.format_exc())
143 log.error(traceback.format_exc())
144 sa.rollback()
144 sa.rollback()
145
145
146
146
147 def get_repos(path, recursive=False):
147 def get_repos(path, recursive=False):
148 """
148 """
149 Scans given path for repos and return (name,(type,path)) tuple
149 Scans given path for repos and return (name,(type,path)) tuple
150
150
151 :param path: path to scann for repositories
151 :param path: path to scann for repositories
152 :param recursive: recursive search and return names with subdirs in front
152 :param recursive: recursive search and return names with subdirs in front
153 """
153 """
154 from vcs.utils.helpers import get_scm
154 from vcs.utils.helpers import get_scm
155 from vcs.exceptions import VCSError
155 from vcs.exceptions import VCSError
156
156
157 if path.endswith(os.sep):
157 if path.endswith(os.sep):
158 #remove ending slash for better results
158 #remove ending slash for better results
159 path = path[:-1]
159 path = path[:-1]
160
160
161 def _get_repos(p):
161 def _get_repos(p):
162 if not os.access(p, os.W_OK):
162 if not os.access(p, os.W_OK):
163 return
163 return
164 for dirpath in os.listdir(p):
164 for dirpath in os.listdir(p):
165 print dirpath
166 if os.path.isfile(os.path.join(p, dirpath)):
165 if os.path.isfile(os.path.join(p, dirpath)):
167 continue
166 continue
168 cur_path = os.path.join(p, dirpath)
167 cur_path = os.path.join(p, dirpath)
169 try:
168 try:
170 scm_info = get_scm(cur_path)
169 scm_info = get_scm(cur_path)
171 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
170 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
172 except VCSError:
171 except VCSError:
173 if not recursive:
172 if not recursive:
174 continue
173 continue
175 #check if this dir containts other repos for recursive scan
174 #check if this dir containts other repos for recursive scan
176 rec_path = os.path.join(p, dirpath)
175 rec_path = os.path.join(p, dirpath)
177 if os.path.isdir(rec_path):
176 if os.path.isdir(rec_path):
178 for inner_scm in _get_repos(rec_path):
177 for inner_scm in _get_repos(rec_path):
179 yield inner_scm
178 yield inner_scm
180
179
181 return _get_repos(path)
180 return _get_repos(path)
182
181
183
182
184 def check_repo_fast(repo_name, base_path):
183 def check_repo_fast(repo_name, base_path):
185 """
184 """
186 Check given path for existence of directory
185 Check given path for existence of directory
187 :param repo_name:
186 :param repo_name:
188 :param base_path:
187 :param base_path:
189
188
190 :return False: if this directory is present
189 :return False: if this directory is present
191 """
190 """
192 if os.path.isdir(os.path.join(base_path, repo_name)):
191 if os.path.isdir(os.path.join(base_path, repo_name)):
193 return False
192 return False
194 return True
193 return True
195
194
196
195
197 def check_repo(repo_name, base_path, verify=True):
196 def check_repo(repo_name, base_path, verify=True):
198
197
199 repo_path = os.path.join(base_path, repo_name)
198 repo_path = os.path.join(base_path, repo_name)
200
199
201 try:
200 try:
202 if not check_repo_fast(repo_name, base_path):
201 if not check_repo_fast(repo_name, base_path):
203 return False
202 return False
204 r = hg.repository(ui.ui(), repo_path)
203 r = hg.repository(ui.ui(), repo_path)
205 if verify:
204 if verify:
206 hg.verify(r)
205 hg.verify(r)
207 #here we hnow that repo exists it was verified
206 #here we hnow that repo exists it was verified
208 log.info('%s repo is already created', repo_name)
207 log.info('%s repo is already created', repo_name)
209 return False
208 return False
210 except RepoError:
209 except RepoError:
211 #it means that there is no valid repo there...
210 #it means that there is no valid repo there...
212 log.info('%s repo is free for creation', repo_name)
211 log.info('%s repo is free for creation', repo_name)
213 return True
212 return True
214
213
215
214
216 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
215 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
217 while True:
216 while True:
218 ok = raw_input(prompt)
217 ok = raw_input(prompt)
219 if ok in ('y', 'ye', 'yes'):
218 if ok in ('y', 'ye', 'yes'):
220 return True
219 return True
221 if ok in ('n', 'no', 'nop', 'nope'):
220 if ok in ('n', 'no', 'nop', 'nope'):
222 return False
221 return False
223 retries = retries - 1
222 retries = retries - 1
224 if retries < 0:
223 if retries < 0:
225 raise IOError
224 raise IOError
226 print complaint
225 print complaint
227
226
228 #propagated from mercurial documentation
227 #propagated from mercurial documentation
229 ui_sections = ['alias', 'auth',
228 ui_sections = ['alias', 'auth',
230 'decode/encode', 'defaults',
229 'decode/encode', 'defaults',
231 'diff', 'email',
230 'diff', 'email',
232 'extensions', 'format',
231 'extensions', 'format',
233 'merge-patterns', 'merge-tools',
232 'merge-patterns', 'merge-tools',
234 'hooks', 'http_proxy',
233 'hooks', 'http_proxy',
235 'smtp', 'patch',
234 'smtp', 'patch',
236 'paths', 'profiling',
235 'paths', 'profiling',
237 'server', 'trusted',
236 'server', 'trusted',
238 'ui', 'web', ]
237 'ui', 'web', ]
239
238
240
239
241 def make_ui(read_from='file', path=None, checkpaths=True):
240 def make_ui(read_from='file', path=None, checkpaths=True):
242 """A function that will read python rc files or database
241 """A function that will read python rc files or database
243 and make an mercurial ui object from read options
242 and make an mercurial ui object from read options
244
243
245 :param path: path to mercurial config file
244 :param path: path to mercurial config file
246 :param checkpaths: check the path
245 :param checkpaths: check the path
247 :param read_from: read from 'file' or 'db'
246 :param read_from: read from 'file' or 'db'
248 """
247 """
249
248
250 baseui = ui.ui()
249 baseui = ui.ui()
251
250
252 #clean the baseui object
251 #clean the baseui object
253 baseui._ocfg = config.config()
252 baseui._ocfg = config.config()
254 baseui._ucfg = config.config()
253 baseui._ucfg = config.config()
255 baseui._tcfg = config.config()
254 baseui._tcfg = config.config()
256
255
257 if read_from == 'file':
256 if read_from == 'file':
258 if not os.path.isfile(path):
257 if not os.path.isfile(path):
259 log.warning('Unable to read config file %s' % path)
258 log.warning('Unable to read config file %s' % path)
260 return False
259 return False
261 log.debug('reading hgrc from %s', path)
260 log.debug('reading hgrc from %s', path)
262 cfg = config.config()
261 cfg = config.config()
263 cfg.read(path)
262 cfg.read(path)
264 for section in ui_sections:
263 for section in ui_sections:
265 for k, v in cfg.items(section):
264 for k, v in cfg.items(section):
266 log.debug('settings ui from file[%s]%s:%s', section, k, v)
265 log.debug('settings ui from file[%s]%s:%s', section, k, v)
267 baseui.setconfig(section, k, v)
266 baseui.setconfig(section, k, v)
268
267
269 elif read_from == 'db':
268 elif read_from == 'db':
270 sa = meta.Session()
269 sa = meta.Session()
271 ret = sa.query(RhodeCodeUi)\
270 ret = sa.query(RhodeCodeUi)\
272 .options(FromCache("sql_cache_short",
271 .options(FromCache("sql_cache_short",
273 "get_hg_ui_settings")).all()
272 "get_hg_ui_settings")).all()
274
273
275 hg_ui = ret
274 hg_ui = ret
276 for ui_ in hg_ui:
275 for ui_ in hg_ui:
277 if ui_.ui_active:
276 if ui_.ui_active:
278 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
277 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
279 ui_.ui_key, ui_.ui_value)
278 ui_.ui_key, ui_.ui_value)
280 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
279 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
281
280
282 meta.Session.remove()
281 meta.Session.remove()
283 return baseui
282 return baseui
284
283
285
284
286 def set_rhodecode_config(config):
285 def set_rhodecode_config(config):
287 """Updates pylons config with new settings from database
286 """Updates pylons config with new settings from database
288
287
289 :param config:
288 :param config:
290 """
289 """
291 from rhodecode.model.settings import SettingsModel
290 from rhodecode.model.settings import SettingsModel
292 hgsettings = SettingsModel().get_app_settings()
291 hgsettings = SettingsModel().get_app_settings()
293
292
294 for k, v in hgsettings.items():
293 for k, v in hgsettings.items():
295 config[k] = v
294 config[k] = v
296
295
297
296
298 def invalidate_cache(cache_key, *args):
297 def invalidate_cache(cache_key, *args):
299 """Puts cache invalidation task into db for
298 """Puts cache invalidation task into db for
300 further global cache invalidation
299 further global cache invalidation
301 """
300 """
302
301
303 from rhodecode.model.scm import ScmModel
302 from rhodecode.model.scm import ScmModel
304
303
305 if cache_key.startswith('get_repo_cached_'):
304 if cache_key.startswith('get_repo_cached_'):
306 name = cache_key.split('get_repo_cached_')[-1]
305 name = cache_key.split('get_repo_cached_')[-1]
307 ScmModel().mark_for_invalidation(name)
306 ScmModel().mark_for_invalidation(name)
308
307
309
308
310 class EmptyChangeset(BaseChangeset):
309 class EmptyChangeset(BaseChangeset):
311 """
310 """
312 An dummy empty changeset. It's possible to pass hash when creating
311 An dummy empty changeset. It's possible to pass hash when creating
313 an EmptyChangeset
312 an EmptyChangeset
314 """
313 """
315
314
316 def __init__(self, cs='0' * 40, repo=None):
315 def __init__(self, cs='0' * 40, repo=None):
317 self._empty_cs = cs
316 self._empty_cs = cs
318 self.revision = -1
317 self.revision = -1
319 self.message = ''
318 self.message = ''
320 self.author = ''
319 self.author = ''
321 self.date = ''
320 self.date = ''
322 self.repository = repo
321 self.repository = repo
323
322
324 @LazyProperty
323 @LazyProperty
325 def raw_id(self):
324 def raw_id(self):
326 """Returns raw string identifying this changeset, useful for web
325 """Returns raw string identifying this changeset, useful for web
327 representation.
326 representation.
328 """
327 """
329
328
330 return self._empty_cs
329 return self._empty_cs
331
330
332 @LazyProperty
331 @LazyProperty
333 def short_id(self):
332 def short_id(self):
334 return self.raw_id[:12]
333 return self.raw_id[:12]
335
334
336 def get_file_changeset(self, path):
335 def get_file_changeset(self, path):
337 return self
336 return self
338
337
339 def get_file_content(self, path):
338 def get_file_content(self, path):
340 return u''
339 return u''
341
340
342 def get_file_size(self, path):
341 def get_file_size(self, path):
343 return 0
342 return 0
344
343
345
344
346 def map_groups(groups):
345 def map_groups(groups):
347 """Checks for groups existence, and creates groups structures.
346 """Checks for groups existence, and creates groups structures.
348 It returns last group in structure
347 It returns last group in structure
349
348
350 :param groups: list of groups structure
349 :param groups: list of groups structure
351 """
350 """
352 sa = meta.Session()
351 sa = meta.Session()
353
352
354 parent = None
353 parent = None
355 group = None
354 group = None
356 for lvl, group_name in enumerate(groups[:-1]):
355 for lvl, group_name in enumerate(groups[:-1]):
357 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
356 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
358
357
359 if group is None:
358 if group is None:
360 group = Group(group_name, parent)
359 group = Group(group_name, parent)
361 sa.add(group)
360 sa.add(group)
362 sa.commit()
361 sa.commit()
363
362
364 parent = group
363 parent = group
365
364
366 return group
365 return group
367
366
368
367
369 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
368 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
370 """maps all repos given in initial_repo_list, non existing repositories
369 """maps all repos given in initial_repo_list, non existing repositories
371 are created, if remove_obsolete is True it also check for db entries
370 are created, if remove_obsolete is True it also check for db entries
372 that are not in initial_repo_list and removes them.
371 that are not in initial_repo_list and removes them.
373
372
374 :param initial_repo_list: list of repositories found by scanning methods
373 :param initial_repo_list: list of repositories found by scanning methods
375 :param remove_obsolete: check for obsolete entries in database
374 :param remove_obsolete: check for obsolete entries in database
376 """
375 """
377
376
378 sa = meta.Session()
377 sa = meta.Session()
379 rm = RepoModel()
378 rm = RepoModel()
380 user = sa.query(User).filter(User.admin == True).first()
379 user = sa.query(User).filter(User.admin == True).first()
381 added = []
380 added = []
382 for name, repo in initial_repo_list.items():
381 for name, repo in initial_repo_list.items():
383 group = map_groups(name.split('/'))
382 group = map_groups(name.split('/'))
384 if not rm.get_by_repo_name(name, cache=False):
383 if not rm.get_by_repo_name(name, cache=False):
385 log.info('repository %s not found creating default', name)
384 log.info('repository %s not found creating default', name)
386 added.append(name)
385 added.append(name)
387 form_data = {
386 form_data = {
388 'repo_name': name,
387 'repo_name': name,
389 'repo_type': repo.alias,
388 'repo_type': repo.alias,
390 'description': repo.description \
389 'description': repo.description \
391 if repo.description != 'unknown' else \
390 if repo.description != 'unknown' else \
392 '%s repository' % name,
391 '%s repository' % name,
393 'private': False,
392 'private': False,
394 'group_id': getattr(group, 'group_id', None)
393 'group_id': getattr(group, 'group_id', None)
395 }
394 }
396 rm.create(form_data, user, just_db=True)
395 rm.create(form_data, user, just_db=True)
397
396
398 removed = []
397 removed = []
399 if remove_obsolete:
398 if remove_obsolete:
400 #remove from database those repositories that are not in the filesystem
399 #remove from database those repositories that are not in the filesystem
401 for repo in sa.query(Repository).all():
400 for repo in sa.query(Repository).all():
402 if repo.repo_name not in initial_repo_list.keys():
401 if repo.repo_name not in initial_repo_list.keys():
403 removed.append(repo.repo_name)
402 removed.append(repo.repo_name)
404 sa.delete(repo)
403 sa.delete(repo)
405 sa.commit()
404 sa.commit()
406
405
407 return added, removed
406 return added, removed
408
407
409
408
410 class OrderedDict(dict, DictMixin):
409 class OrderedDict(dict, DictMixin):
411
410
412 def __init__(self, *args, **kwds):
411 def __init__(self, *args, **kwds):
413 if len(args) > 1:
412 if len(args) > 1:
414 raise TypeError('expected at most 1 arguments, got %d' % len(args))
413 raise TypeError('expected at most 1 arguments, got %d' % len(args))
415 try:
414 try:
416 self.__end
415 self.__end
417 except AttributeError:
416 except AttributeError:
418 self.clear()
417 self.clear()
419 self.update(*args, **kwds)
418 self.update(*args, **kwds)
420
419
421 def clear(self):
420 def clear(self):
422 self.__end = end = []
421 self.__end = end = []
423 end += [None, end, end] # sentinel node for doubly linked list
422 end += [None, end, end] # sentinel node for doubly linked list
424 self.__map = {} # key --> [key, prev, next]
423 self.__map = {} # key --> [key, prev, next]
425 dict.clear(self)
424 dict.clear(self)
426
425
427 def __setitem__(self, key, value):
426 def __setitem__(self, key, value):
428 if key not in self:
427 if key not in self:
429 end = self.__end
428 end = self.__end
430 curr = end[1]
429 curr = end[1]
431 curr[2] = end[1] = self.__map[key] = [key, curr, end]
430 curr[2] = end[1] = self.__map[key] = [key, curr, end]
432 dict.__setitem__(self, key, value)
431 dict.__setitem__(self, key, value)
433
432
434 def __delitem__(self, key):
433 def __delitem__(self, key):
435 dict.__delitem__(self, key)
434 dict.__delitem__(self, key)
436 key, prev, next = self.__map.pop(key)
435 key, prev, next = self.__map.pop(key)
437 prev[2] = next
436 prev[2] = next
438 next[1] = prev
437 next[1] = prev
439
438
440 def __iter__(self):
439 def __iter__(self):
441 end = self.__end
440 end = self.__end
442 curr = end[2]
441 curr = end[2]
443 while curr is not end:
442 while curr is not end:
444 yield curr[0]
443 yield curr[0]
445 curr = curr[2]
444 curr = curr[2]
446
445
447 def __reversed__(self):
446 def __reversed__(self):
448 end = self.__end
447 end = self.__end
449 curr = end[1]
448 curr = end[1]
450 while curr is not end:
449 while curr is not end:
451 yield curr[0]
450 yield curr[0]
452 curr = curr[1]
451 curr = curr[1]
453
452
454 def popitem(self, last=True):
453 def popitem(self, last=True):
455 if not self:
454 if not self:
456 raise KeyError('dictionary is empty')
455 raise KeyError('dictionary is empty')
457 if last:
456 if last:
458 key = reversed(self).next()
457 key = reversed(self).next()
459 else:
458 else:
460 key = iter(self).next()
459 key = iter(self).next()
461 value = self.pop(key)
460 value = self.pop(key)
462 return key, value
461 return key, value
463
462
464 def __reduce__(self):
463 def __reduce__(self):
465 items = [[k, self[k]] for k in self]
464 items = [[k, self[k]] for k in self]
466 tmp = self.__map, self.__end
465 tmp = self.__map, self.__end
467 del self.__map, self.__end
466 del self.__map, self.__end
468 inst_dict = vars(self).copy()
467 inst_dict = vars(self).copy()
469 self.__map, self.__end = tmp
468 self.__map, self.__end = tmp
470 if inst_dict:
469 if inst_dict:
471 return (self.__class__, (items,), inst_dict)
470 return (self.__class__, (items,), inst_dict)
472 return self.__class__, (items,)
471 return self.__class__, (items,)
473
472
474 def keys(self):
473 def keys(self):
475 return list(self)
474 return list(self)
476
475
477 setdefault = DictMixin.setdefault
476 setdefault = DictMixin.setdefault
478 update = DictMixin.update
477 update = DictMixin.update
479 pop = DictMixin.pop
478 pop = DictMixin.pop
480 values = DictMixin.values
479 values = DictMixin.values
481 items = DictMixin.items
480 items = DictMixin.items
482 iterkeys = DictMixin.iterkeys
481 iterkeys = DictMixin.iterkeys
483 itervalues = DictMixin.itervalues
482 itervalues = DictMixin.itervalues
484 iteritems = DictMixin.iteritems
483 iteritems = DictMixin.iteritems
485
484
486 def __repr__(self):
485 def __repr__(self):
487 if not self:
486 if not self:
488 return '%s()' % (self.__class__.__name__,)
487 return '%s()' % (self.__class__.__name__,)
489 return '%s(%r)' % (self.__class__.__name__, self.items())
488 return '%s(%r)' % (self.__class__.__name__, self.items())
490
489
491 def copy(self):
490 def copy(self):
492 return self.__class__(self)
491 return self.__class__(self)
493
492
494 @classmethod
493 @classmethod
495 def fromkeys(cls, iterable, value=None):
494 def fromkeys(cls, iterable, value=None):
496 d = cls()
495 d = cls()
497 for key in iterable:
496 for key in iterable:
498 d[key] = value
497 d[key] = value
499 return d
498 return d
500
499
501 def __eq__(self, other):
500 def __eq__(self, other):
502 if isinstance(other, OrderedDict):
501 if isinstance(other, OrderedDict):
503 return len(self) == len(other) and self.items() == other.items()
502 return len(self) == len(other) and self.items() == other.items()
504 return dict.__eq__(self, other)
503 return dict.__eq__(self, other)
505
504
506 def __ne__(self, other):
505 def __ne__(self, other):
507 return not self == other
506 return not self == other
508
507
509
508
510 #set cache regions for beaker so celery can utilise it
509 #set cache regions for beaker so celery can utilise it
511 def add_cache(settings):
510 def add_cache(settings):
512 cache_settings = {'regions': None}
511 cache_settings = {'regions': None}
513 for key in settings.keys():
512 for key in settings.keys():
514 for prefix in ['beaker.cache.', 'cache.']:
513 for prefix in ['beaker.cache.', 'cache.']:
515 if key.startswith(prefix):
514 if key.startswith(prefix):
516 name = key.split(prefix)[1].strip()
515 name = key.split(prefix)[1].strip()
517 cache_settings[name] = settings[key].strip()
516 cache_settings[name] = settings[key].strip()
518 if cache_settings['regions']:
517 if cache_settings['regions']:
519 for region in cache_settings['regions'].split(','):
518 for region in cache_settings['regions'].split(','):
520 region = region.strip()
519 region = region.strip()
521 region_settings = {}
520 region_settings = {}
522 for key, value in cache_settings.items():
521 for key, value in cache_settings.items():
523 if key.startswith(region):
522 if key.startswith(region):
524 region_settings[key.split('.')[1]] = value
523 region_settings[key.split('.')[1]] = value
525 region_settings['expire'] = int(region_settings.get('expire',
524 region_settings['expire'] = int(region_settings.get('expire',
526 60))
525 60))
527 region_settings.setdefault('lock_dir',
526 region_settings.setdefault('lock_dir',
528 cache_settings.get('lock_dir'))
527 cache_settings.get('lock_dir'))
529 region_settings.setdefault('data_dir',
528 region_settings.setdefault('data_dir',
530 cache_settings.get('data_dir'))
529 cache_settings.get('data_dir'))
531
530
532 if 'type' not in region_settings:
531 if 'type' not in region_settings:
533 region_settings['type'] = cache_settings.get('type',
532 region_settings['type'] = cache_settings.get('type',
534 'memory')
533 'memory')
535 beaker.cache.cache_regions[region] = region_settings
534 beaker.cache.cache_regions[region] = region_settings
536
535
537
536
538 def get_current_revision():
537 def get_current_revision():
539 """Returns tuple of (number, id) from repository containing this package
538 """Returns tuple of (number, id) from repository containing this package
540 or None if repository could not be found.
539 or None if repository could not be found.
541 """
540 """
542
541
543 try:
542 try:
544 from vcs import get_repo
543 from vcs import get_repo
545 from vcs.utils.helpers import get_scm
544 from vcs.utils.helpers import get_scm
546 from vcs.exceptions import RepositoryError, VCSError
545 from vcs.exceptions import RepositoryError, VCSError
547 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
546 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
548 scm = get_scm(repopath)[0]
547 scm = get_scm(repopath)[0]
549 repo = get_repo(path=repopath, alias=scm)
548 repo = get_repo(path=repopath, alias=scm)
550 tip = repo.get_changeset()
549 tip = repo.get_changeset()
551 return (tip.revision, tip.short_id)
550 return (tip.revision, tip.short_id)
552 except (ImportError, RepositoryError, VCSError), err:
551 except (ImportError, RepositoryError, VCSError), err:
553 logging.debug("Cannot retrieve rhodecode's revision. Original error "
552 logging.debug("Cannot retrieve rhodecode's revision. Original error "
554 "was: %s" % err)
553 "was: %s" % err)
555 return None
554 return None
556
555
557
556
558 #==============================================================================
557 #==============================================================================
559 # TEST FUNCTIONS AND CREATORS
558 # TEST FUNCTIONS AND CREATORS
560 #==============================================================================
559 #==============================================================================
561 def create_test_index(repo_location, full_index):
560 def create_test_index(repo_location, full_index):
562 """Makes default test index
561 """Makes default test index
563 :param repo_location:
562 :param repo_location:
564 :param full_index:
563 :param full_index:
565 """
564 """
566 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
565 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
567 from rhodecode.lib.pidlock import DaemonLock, LockHeld
566 from rhodecode.lib.pidlock import DaemonLock, LockHeld
568 import shutil
567 import shutil
569
568
570 index_location = os.path.join(repo_location, 'index')
569 index_location = os.path.join(repo_location, 'index')
571 if os.path.exists(index_location):
570 if os.path.exists(index_location):
572 shutil.rmtree(index_location)
571 shutil.rmtree(index_location)
573
572
574 try:
573 try:
575 l = DaemonLock()
574 l = DaemonLock()
576 WhooshIndexingDaemon(index_location=index_location,
575 WhooshIndexingDaemon(index_location=index_location,
577 repo_location=repo_location)\
576 repo_location=repo_location)\
578 .run(full_index=full_index)
577 .run(full_index=full_index)
579 l.release()
578 l.release()
580 except LockHeld:
579 except LockHeld:
581 pass
580 pass
582
581
583
582
584 def create_test_env(repos_test_path, config):
583 def create_test_env(repos_test_path, config):
585 """Makes a fresh database and
584 """Makes a fresh database and
586 install test repository into tmp dir
585 install test repository into tmp dir
587 """
586 """
588 from rhodecode.lib.db_manage import DbManage
587 from rhodecode.lib.db_manage import DbManage
589 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
588 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
590 HG_FORK, GIT_FORK, TESTS_TMP_PATH
589 HG_FORK, GIT_FORK, TESTS_TMP_PATH
591 import tarfile
590 import tarfile
592 import shutil
591 import shutil
593 from os.path import dirname as dn, join as jn, abspath
592 from os.path import dirname as dn, join as jn, abspath
594
593
595 log = logging.getLogger('TestEnvCreator')
594 log = logging.getLogger('TestEnvCreator')
596 # create logger
595 # create logger
597 log.setLevel(logging.DEBUG)
596 log.setLevel(logging.DEBUG)
598 log.propagate = True
597 log.propagate = True
599 # create console handler and set level to debug
598 # create console handler and set level to debug
600 ch = logging.StreamHandler()
599 ch = logging.StreamHandler()
601 ch.setLevel(logging.DEBUG)
600 ch.setLevel(logging.DEBUG)
602
601
603 # create formatter
602 # create formatter
604 formatter = logging.Formatter("%(asctime)s - %(name)s -"
603 formatter = logging.Formatter("%(asctime)s - %(name)s -"
605 " %(levelname)s - %(message)s")
604 " %(levelname)s - %(message)s")
606
605
607 # add formatter to ch
606 # add formatter to ch
608 ch.setFormatter(formatter)
607 ch.setFormatter(formatter)
609
608
610 # add ch to logger
609 # add ch to logger
611 log.addHandler(ch)
610 log.addHandler(ch)
612
611
613 #PART ONE create db
612 #PART ONE create db
614 dbconf = config['sqlalchemy.db1.url']
613 dbconf = config['sqlalchemy.db1.url']
615 log.debug('making test db %s', dbconf)
614 log.debug('making test db %s', dbconf)
616
615
617 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
616 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
618 tests=True)
617 tests=True)
619 dbmanage.create_tables(override=True)
618 dbmanage.create_tables(override=True)
620 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
619 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
621 dbmanage.create_default_user()
620 dbmanage.create_default_user()
622 dbmanage.admin_prompt()
621 dbmanage.admin_prompt()
623 dbmanage.create_permissions()
622 dbmanage.create_permissions()
624 dbmanage.populate_default_permissions()
623 dbmanage.populate_default_permissions()
625
624
626 #PART TWO make test repo
625 #PART TWO make test repo
627 log.debug('making test vcs repositories')
626 log.debug('making test vcs repositories')
628
627
629 #remove old one from previos tests
628 #remove old one from previos tests
630 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
629 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
631
630
632 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
631 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
633 log.debug('removing %s', r)
632 log.debug('removing %s', r)
634 shutil.rmtree(jn(TESTS_TMP_PATH, r))
633 shutil.rmtree(jn(TESTS_TMP_PATH, r))
635
634
636 #CREATE DEFAULT HG REPOSITORY
635 #CREATE DEFAULT HG REPOSITORY
637 cur_dir = dn(dn(abspath(__file__)))
636 cur_dir = dn(dn(abspath(__file__)))
638 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
637 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
639 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
638 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
640 tar.close()
639 tar.close()
641
640
642
641
643 #==============================================================================
642 #==============================================================================
644 # PASTER COMMANDS
643 # PASTER COMMANDS
645 #==============================================================================
644 #==============================================================================
646 class BasePasterCommand(Command):
645 class BasePasterCommand(Command):
647 """
646 """
648 Abstract Base Class for paster commands.
647 Abstract Base Class for paster commands.
649
648
650 The celery commands are somewhat aggressive about loading
649 The celery commands are somewhat aggressive about loading
651 celery.conf, and since our module sets the `CELERY_LOADER`
650 celery.conf, and since our module sets the `CELERY_LOADER`
652 environment variable to our loader, we have to bootstrap a bit and
651 environment variable to our loader, we have to bootstrap a bit and
653 make sure we've had a chance to load the pylons config off of the
652 make sure we've had a chance to load the pylons config off of the
654 command line, otherwise everything fails.
653 command line, otherwise everything fails.
655 """
654 """
656 min_args = 1
655 min_args = 1
657 min_args_error = "Please provide a paster config file as an argument."
656 min_args_error = "Please provide a paster config file as an argument."
658 takes_config_file = 1
657 takes_config_file = 1
659 requires_config_file = True
658 requires_config_file = True
660
659
661 def notify_msg(self, msg, log=False):
660 def notify_msg(self, msg, log=False):
662 """Make a notification to user, additionally if logger is passed
661 """Make a notification to user, additionally if logger is passed
663 it logs this action using given logger
662 it logs this action using given logger
664
663
665 :param msg: message that will be printed to user
664 :param msg: message that will be printed to user
666 :param log: logging instance, to use to additionally log this message
665 :param log: logging instance, to use to additionally log this message
667
666
668 """
667 """
669 if log and isinstance(log, logging):
668 if log and isinstance(log, logging):
670 log(msg)
669 log(msg)
671
670
672 def run(self, args):
671 def run(self, args):
673 """
672 """
674 Overrides Command.run
673 Overrides Command.run
675
674
676 Checks for a config file argument and loads it.
675 Checks for a config file argument and loads it.
677 """
676 """
678 if len(args) < self.min_args:
677 if len(args) < self.min_args:
679 raise BadCommand(
678 raise BadCommand(
680 self.min_args_error % {'min_args': self.min_args,
679 self.min_args_error % {'min_args': self.min_args,
681 'actual_args': len(args)})
680 'actual_args': len(args)})
682
681
683 # Decrement because we're going to lob off the first argument.
682 # Decrement because we're going to lob off the first argument.
684 # @@ This is hacky
683 # @@ This is hacky
685 self.min_args -= 1
684 self.min_args -= 1
686 self.bootstrap_config(args[0])
685 self.bootstrap_config(args[0])
687 self.update_parser()
686 self.update_parser()
688 return super(BasePasterCommand, self).run(args[1:])
687 return super(BasePasterCommand, self).run(args[1:])
689
688
690 def update_parser(self):
689 def update_parser(self):
691 """
690 """
692 Abstract method. Allows for the class's parser to be updated
691 Abstract method. Allows for the class's parser to be updated
693 before the superclass's `run` method is called. Necessary to
692 before the superclass's `run` method is called. Necessary to
694 allow options/arguments to be passed through to the underlying
693 allow options/arguments to be passed through to the underlying
695 celery command.
694 celery command.
696 """
695 """
697 raise NotImplementedError("Abstract Method.")
696 raise NotImplementedError("Abstract Method.")
698
697
699 def bootstrap_config(self, conf):
698 def bootstrap_config(self, conf):
700 """
699 """
701 Loads the pylons configuration.
700 Loads the pylons configuration.
702 """
701 """
703 from pylons import config as pylonsconfig
702 from pylons import config as pylonsconfig
704
703
705 path_to_ini_file = os.path.realpath(conf)
704 path_to_ini_file = os.path.realpath(conf)
706 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
705 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
707 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
706 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
General Comments 0
You need to be logged in to leave comments. Login now