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