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