##// END OF EJS Templates
moved out ui_sections out of make ui function
marcink -
r386:a9a607a5 default
parent child Browse files
Show More
@@ -1,334 +1,336 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Utilities for hg app
3 # Utilities for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19
19
20 """
20 """
21 Created on April 18, 2010
21 Created on April 18, 2010
22 Utilities for hg app
22 Utilities for hg app
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from mercurial import ui, config, hg
26 from mercurial import ui, config, hg
27 from mercurial.error import RepoError
27 from mercurial.error import RepoError
28 from pylons_app.model import meta
28 from pylons_app.model import meta
29 from pylons_app.model.db import Repository, User, HgAppUi, HgAppSettings
29 from pylons_app.model.db import Repository, User, HgAppUi, HgAppSettings
30 from vcs.backends.base import BaseChangeset
30 from vcs.backends.base import BaseChangeset
31 from vcs.utils.lazy import LazyProperty
31 from vcs.utils.lazy import LazyProperty
32 import logging
32 import logging
33 import os
33 import os
34 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36
36
37 def get_repo_slug(request):
37 def get_repo_slug(request):
38 return request.environ['pylons.routes_dict'].get('repo_name')
38 return request.environ['pylons.routes_dict'].get('repo_name')
39
39
40 def is_mercurial(environ):
40 def is_mercurial(environ):
41 """
41 """
42 Returns True if request's target is mercurial server - header
42 Returns True if request's target is mercurial server - header
43 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
43 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
44 """
44 """
45 http_accept = environ.get('HTTP_ACCEPT')
45 http_accept = environ.get('HTTP_ACCEPT')
46 if http_accept and http_accept.startswith('application/mercurial'):
46 if http_accept and http_accept.startswith('application/mercurial'):
47 return True
47 return True
48 return False
48 return False
49
49
50 def check_repo_dir(paths):
50 def check_repo_dir(paths):
51 repos_path = paths[0][1].split('/')
51 repos_path = paths[0][1].split('/')
52 if repos_path[-1] in ['*', '**']:
52 if repos_path[-1] in ['*', '**']:
53 repos_path = repos_path[:-1]
53 repos_path = repos_path[:-1]
54 if repos_path[0] != '/':
54 if repos_path[0] != '/':
55 repos_path[0] = '/'
55 repos_path[0] = '/'
56 if not os.path.isdir(os.path.join(*repos_path)):
56 if not os.path.isdir(os.path.join(*repos_path)):
57 raise Exception('Not a valid repository in %s' % paths[0][1])
57 raise Exception('Not a valid repository in %s' % paths[0][1])
58
58
59 def check_repo_fast(repo_name, base_path):
59 def check_repo_fast(repo_name, base_path):
60 if os.path.isdir(os.path.join(base_path, repo_name)):return False
60 if os.path.isdir(os.path.join(base_path, repo_name)):return False
61 return True
61 return True
62
62
63 def check_repo(repo_name, base_path, verify=True):
63 def check_repo(repo_name, base_path, verify=True):
64
64
65 repo_path = os.path.join(base_path, repo_name)
65 repo_path = os.path.join(base_path, repo_name)
66
66
67 try:
67 try:
68 if not check_repo_fast(repo_name, base_path):
68 if not check_repo_fast(repo_name, base_path):
69 return False
69 return False
70 r = hg.repository(ui.ui(), repo_path)
70 r = hg.repository(ui.ui(), repo_path)
71 if verify:
71 if verify:
72 hg.verify(r)
72 hg.verify(r)
73 #here we hnow that repo exists it was verified
73 #here we hnow that repo exists it was verified
74 log.info('%s repo is already created', repo_name)
74 log.info('%s repo is already created', repo_name)
75 return False
75 return False
76 except RepoError:
76 except RepoError:
77 #it means that there is no valid repo there...
77 #it means that there is no valid repo there...
78 log.info('%s repo is free for creation', repo_name)
78 log.info('%s repo is free for creation', repo_name)
79 return True
79 return True
80
80
81 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
81 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
82 while True:
82 while True:
83 ok = raw_input(prompt)
83 ok = raw_input(prompt)
84 if ok in ('y', 'ye', 'yes'): return True
84 if ok in ('y', 'ye', 'yes'): return True
85 if ok in ('n', 'no', 'nop', 'nope'): return False
85 if ok in ('n', 'no', 'nop', 'nope'): return False
86 retries = retries - 1
86 retries = retries - 1
87 if retries < 0: raise IOError
87 if retries < 0: raise IOError
88 print complaint
88 print complaint
89
89
90 @cache_region('super_short_term', 'cached_hg_ui')
90 @cache_region('super_short_term', 'cached_hg_ui')
91 def get_hg_ui_cached():
91 def get_hg_ui_cached():
92 try:
92 try:
93 sa = meta.Session
93 sa = meta.Session
94 ret = sa.query(HgAppUi).all()
94 ret = sa.query(HgAppUi).all()
95 finally:
95 finally:
96 meta.Session.remove()
96 meta.Session.remove()
97 return ret
97 return ret
98
98
99
99
100 def get_hg_settings():
100 def get_hg_settings():
101 try:
101 try:
102 sa = meta.Session
102 sa = meta.Session
103 ret = sa.query(HgAppSettings).all()
103 ret = sa.query(HgAppSettings).all()
104 finally:
104 finally:
105 meta.Session.remove()
105 meta.Session.remove()
106
106
107 if not ret:
107 if not ret:
108 raise Exception('Could not get application settings !')
108 raise Exception('Could not get application settings !')
109 settings = {}
109 settings = {}
110 for each in ret:
110 for each in ret:
111 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
111 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
112
112
113 return settings
113 return settings
114
114
115 ui_sections = ['alias', 'auth',
116 'decode/encode', 'defaults',
117 'diff', 'email',
118 'extensions', 'format',
119 'merge-patterns', 'merge-tools',
120 'hooks', 'http_proxy',
121 'smtp', 'patch',
122 'paths', 'profiling',
123 'server', 'trusted',
124 'ui', 'web', ]
125
115 def make_ui(read_from='file', path=None, checkpaths=True):
126 def make_ui(read_from='file', path=None, checkpaths=True):
116 """
127 """
117 A function that will read python rc files or database
128 A function that will read python rc files or database
118 and make an mercurial ui object from read options
129 and make an mercurial ui object from read options
119
130
120 @param path: path to mercurial config file
131 @param path: path to mercurial config file
121 @param checkpaths: check the path
132 @param checkpaths: check the path
122 @param read_from: read from 'file' or 'db'
133 @param read_from: read from 'file' or 'db'
123 """
134 """
124 #propagated from mercurial documentation
135 #propagated from mercurial documentation
125 sections = ['alias', 'auth',
136
126 'decode/encode', 'defaults',
127 'diff', 'email',
128 'extensions', 'format',
129 'merge-patterns', 'merge-tools',
130 'hooks', 'http_proxy',
131 'smtp', 'patch',
132 'paths', 'profiling',
133 'server', 'trusted',
134 'ui', 'web', ]
135 baseui = ui.ui()
137 baseui = ui.ui()
136
138
137
139
138 if read_from == 'file':
140 if read_from == 'file':
139 if not os.path.isfile(path):
141 if not os.path.isfile(path):
140 log.warning('Unable to read config file %s' % path)
142 log.warning('Unable to read config file %s' % path)
141 return False
143 return False
142
144
143 cfg = config.config()
145 cfg = config.config()
144 cfg.read(path)
146 cfg.read(path)
145 for section in sections:
147 for section in ui_sections:
146 for k, v in cfg.items(section):
148 for k, v in cfg.items(section):
147 baseui.setconfig(section, k, v)
149 baseui.setconfig(section, k, v)
148 if checkpaths:check_repo_dir(cfg.items('paths'))
150 if checkpaths:check_repo_dir(cfg.items('paths'))
149
151
150
152
151 elif read_from == 'db':
153 elif read_from == 'db':
152 hg_ui = get_hg_ui_cached()
154 hg_ui = get_hg_ui_cached()
153 for ui_ in hg_ui:
155 for ui_ in hg_ui:
154 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
156 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
155
157
156
158
157 return baseui
159 return baseui
158
160
159
161
160 def set_hg_app_config(config):
162 def set_hg_app_config(config):
161 hgsettings = get_hg_settings()
163 hgsettings = get_hg_settings()
162
164
163 for k, v in hgsettings.items():
165 for k, v in hgsettings.items():
164 config[k] = v
166 config[k] = v
165
167
166 def invalidate_cache(name, *args):
168 def invalidate_cache(name, *args):
167 """Invalidates given name cache"""
169 """Invalidates given name cache"""
168
170
169 from beaker.cache import region_invalidate
171 from beaker.cache import region_invalidate
170 log.info('INVALIDATING CACHE FOR %s', name)
172 log.info('INVALIDATING CACHE FOR %s', name)
171
173
172 """propagate our arguments to make sure invalidation works. First
174 """propagate our arguments to make sure invalidation works. First
173 argument has to be the name of cached func name give to cache decorator
175 argument has to be the name of cached func name give to cache decorator
174 without that the invalidation would not work"""
176 without that the invalidation would not work"""
175 tmp = [name]
177 tmp = [name]
176 tmp.extend(args)
178 tmp.extend(args)
177 args = tuple(tmp)
179 args = tuple(tmp)
178
180
179 if name == 'cached_repo_list':
181 if name == 'cached_repo_list':
180 from pylons_app.model.hg_model import _get_repos_cached
182 from pylons_app.model.hg_model import _get_repos_cached
181 region_invalidate(_get_repos_cached, None, *args)
183 region_invalidate(_get_repos_cached, None, *args)
182
184
183 if name == 'full_changelog':
185 if name == 'full_changelog':
184 from pylons_app.model.hg_model import _full_changelog_cached
186 from pylons_app.model.hg_model import _full_changelog_cached
185 region_invalidate(_full_changelog_cached, None, *args)
187 region_invalidate(_full_changelog_cached, None, *args)
186
188
187 class EmptyChangeset(BaseChangeset):
189 class EmptyChangeset(BaseChangeset):
188
190
189 revision = -1
191 revision = -1
190 message = ''
192 message = ''
191
193
192 @LazyProperty
194 @LazyProperty
193 def raw_id(self):
195 def raw_id(self):
194 """
196 """
195 Returns raw string identifing this changeset, useful for web
197 Returns raw string identifing this changeset, useful for web
196 representation.
198 representation.
197 """
199 """
198 return '0' * 12
200 return '0' * 12
199
201
200
202
201 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
203 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
202 """
204 """
203 maps all found repositories into db
205 maps all found repositories into db
204 """
206 """
205 from pylons_app.model.repo_model import RepoModel
207 from pylons_app.model.repo_model import RepoModel
206
208
207 sa = meta.Session
209 sa = meta.Session
208 user = sa.query(User).filter(User.admin == True).first()
210 user = sa.query(User).filter(User.admin == True).first()
209
211
210 rm = RepoModel()
212 rm = RepoModel()
211
213
212 for name, repo in initial_repo_list.items():
214 for name, repo in initial_repo_list.items():
213 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
215 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
214 log.info('repository %s not found creating default', name)
216 log.info('repository %s not found creating default', name)
215
217
216 form_data = {
218 form_data = {
217 'repo_name':name,
219 'repo_name':name,
218 'description':repo.description if repo.description != 'unknown' else \
220 'description':repo.description if repo.description != 'unknown' else \
219 'auto description for %s' % name,
221 'auto description for %s' % name,
220 'private':False
222 'private':False
221 }
223 }
222 rm.create(form_data, user, just_db=True)
224 rm.create(form_data, user, just_db=True)
223
225
224
226
225 if remove_obsolete:
227 if remove_obsolete:
226 #remove from database those repositories that are not in the filesystem
228 #remove from database those repositories that are not in the filesystem
227 for repo in sa.query(Repository).all():
229 for repo in sa.query(Repository).all():
228 if repo.repo_name not in initial_repo_list.keys():
230 if repo.repo_name not in initial_repo_list.keys():
229 sa.delete(repo)
231 sa.delete(repo)
230 sa.commit()
232 sa.commit()
231
233
232
234
233 meta.Session.remove()
235 meta.Session.remove()
234
236
235 from UserDict import DictMixin
237 from UserDict import DictMixin
236
238
237 class OrderedDict(dict, DictMixin):
239 class OrderedDict(dict, DictMixin):
238
240
239 def __init__(self, *args, **kwds):
241 def __init__(self, *args, **kwds):
240 if len(args) > 1:
242 if len(args) > 1:
241 raise TypeError('expected at most 1 arguments, got %d' % len(args))
243 raise TypeError('expected at most 1 arguments, got %d' % len(args))
242 try:
244 try:
243 self.__end
245 self.__end
244 except AttributeError:
246 except AttributeError:
245 self.clear()
247 self.clear()
246 self.update(*args, **kwds)
248 self.update(*args, **kwds)
247
249
248 def clear(self):
250 def clear(self):
249 self.__end = end = []
251 self.__end = end = []
250 end += [None, end, end] # sentinel node for doubly linked list
252 end += [None, end, end] # sentinel node for doubly linked list
251 self.__map = {} # key --> [key, prev, next]
253 self.__map = {} # key --> [key, prev, next]
252 dict.clear(self)
254 dict.clear(self)
253
255
254 def __setitem__(self, key, value):
256 def __setitem__(self, key, value):
255 if key not in self:
257 if key not in self:
256 end = self.__end
258 end = self.__end
257 curr = end[1]
259 curr = end[1]
258 curr[2] = end[1] = self.__map[key] = [key, curr, end]
260 curr[2] = end[1] = self.__map[key] = [key, curr, end]
259 dict.__setitem__(self, key, value)
261 dict.__setitem__(self, key, value)
260
262
261 def __delitem__(self, key):
263 def __delitem__(self, key):
262 dict.__delitem__(self, key)
264 dict.__delitem__(self, key)
263 key, prev, next = self.__map.pop(key)
265 key, prev, next = self.__map.pop(key)
264 prev[2] = next
266 prev[2] = next
265 next[1] = prev
267 next[1] = prev
266
268
267 def __iter__(self):
269 def __iter__(self):
268 end = self.__end
270 end = self.__end
269 curr = end[2]
271 curr = end[2]
270 while curr is not end:
272 while curr is not end:
271 yield curr[0]
273 yield curr[0]
272 curr = curr[2]
274 curr = curr[2]
273
275
274 def __reversed__(self):
276 def __reversed__(self):
275 end = self.__end
277 end = self.__end
276 curr = end[1]
278 curr = end[1]
277 while curr is not end:
279 while curr is not end:
278 yield curr[0]
280 yield curr[0]
279 curr = curr[1]
281 curr = curr[1]
280
282
281 def popitem(self, last=True):
283 def popitem(self, last=True):
282 if not self:
284 if not self:
283 raise KeyError('dictionary is empty')
285 raise KeyError('dictionary is empty')
284 if last:
286 if last:
285 key = reversed(self).next()
287 key = reversed(self).next()
286 else:
288 else:
287 key = iter(self).next()
289 key = iter(self).next()
288 value = self.pop(key)
290 value = self.pop(key)
289 return key, value
291 return key, value
290
292
291 def __reduce__(self):
293 def __reduce__(self):
292 items = [[k, self[k]] for k in self]
294 items = [[k, self[k]] for k in self]
293 tmp = self.__map, self.__end
295 tmp = self.__map, self.__end
294 del self.__map, self.__end
296 del self.__map, self.__end
295 inst_dict = vars(self).copy()
297 inst_dict = vars(self).copy()
296 self.__map, self.__end = tmp
298 self.__map, self.__end = tmp
297 if inst_dict:
299 if inst_dict:
298 return (self.__class__, (items,), inst_dict)
300 return (self.__class__, (items,), inst_dict)
299 return self.__class__, (items,)
301 return self.__class__, (items,)
300
302
301 def keys(self):
303 def keys(self):
302 return list(self)
304 return list(self)
303
305
304 setdefault = DictMixin.setdefault
306 setdefault = DictMixin.setdefault
305 update = DictMixin.update
307 update = DictMixin.update
306 pop = DictMixin.pop
308 pop = DictMixin.pop
307 values = DictMixin.values
309 values = DictMixin.values
308 items = DictMixin.items
310 items = DictMixin.items
309 iterkeys = DictMixin.iterkeys
311 iterkeys = DictMixin.iterkeys
310 itervalues = DictMixin.itervalues
312 itervalues = DictMixin.itervalues
311 iteritems = DictMixin.iteritems
313 iteritems = DictMixin.iteritems
312
314
313 def __repr__(self):
315 def __repr__(self):
314 if not self:
316 if not self:
315 return '%s()' % (self.__class__.__name__,)
317 return '%s()' % (self.__class__.__name__,)
316 return '%s(%r)' % (self.__class__.__name__, self.items())
318 return '%s(%r)' % (self.__class__.__name__, self.items())
317
319
318 def copy(self):
320 def copy(self):
319 return self.__class__(self)
321 return self.__class__(self)
320
322
321 @classmethod
323 @classmethod
322 def fromkeys(cls, iterable, value=None):
324 def fromkeys(cls, iterable, value=None):
323 d = cls()
325 d = cls()
324 for key in iterable:
326 for key in iterable:
325 d[key] = value
327 d[key] = value
326 return d
328 return d
327
329
328 def __eq__(self, other):
330 def __eq__(self, other):
329 if isinstance(other, OrderedDict):
331 if isinstance(other, OrderedDict):
330 return len(self) == len(other) and self.items() == other.items()
332 return len(self) == len(other) and self.items() == other.items()
331 return dict.__eq__(self, other)
333 return dict.__eq__(self, other)
332
334
333 def __ne__(self, other):
335 def __ne__(self, other):
334 return not self == other
336 return not self == other
General Comments 0
You need to be logged in to leave comments. Login now