##// END OF EJS Templates
Added implementation of Ordered Dict.
marcink -
r358:23e720be default
parent child Browse files
Show More
@@ -1,228 +1,329 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).scalar()
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 return ret
110 110
111 111 def make_ui(read_from='file', path=None, checkpaths=True):
112 112 """
113 113 A function that will read python rc files or database
114 114 and make an mercurial ui object from read options
115 115
116 116 @param path: path to mercurial config file
117 117 @param checkpaths: check the path
118 118 @param read_from: read from 'file' or 'db'
119 119 """
120 120 #propagated from mercurial documentation
121 121 sections = ['alias', 'auth',
122 122 'decode/encode', 'defaults',
123 123 'diff', 'email',
124 124 'extensions', 'format',
125 125 'merge-patterns', 'merge-tools',
126 126 'hooks', 'http_proxy',
127 127 'smtp', 'patch',
128 128 'paths', 'profiling',
129 129 'server', 'trusted',
130 130 'ui', 'web', ]
131 131 baseui = ui.ui()
132 132
133 133
134 134 if read_from == 'file':
135 135 if not os.path.isfile(path):
136 136 log.warning('Unable to read config file %s' % path)
137 137 return False
138 138
139 139 cfg = config.config()
140 140 cfg.read(path)
141 141 for section in sections:
142 142 for k, v in cfg.items(section):
143 143 baseui.setconfig(section, k, v)
144 144 if checkpaths:check_repo_dir(cfg.items('paths'))
145 145
146 146
147 147 elif read_from == 'db':
148 148 hg_ui = get_hg_ui_cached()
149 149 for ui_ in hg_ui:
150 150 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
151 151
152 152
153 153 return baseui
154 154
155 155
156 156 def set_hg_app_config(config):
157 157 hgsettings = get_hg_settings()
158 158 config['hg_app_auth_realm'] = hgsettings.app_auth_realm
159 159 config['hg_app_name'] = hgsettings.app_title
160 160
161 161 def invalidate_cache(name, *args):
162 162 """Invalidates given name cache"""
163 163
164 164 from beaker.cache import region_invalidate
165 165 log.info('INVALIDATING CACHE FOR %s', name)
166 166
167 167 """propagate our arguments to make sure invalidation works. First
168 168 argument has to be the name of cached func name give to cache decorator
169 169 without that the invalidation would not work"""
170 170 tmp = [name]
171 171 tmp.extend(args)
172 172 args = tuple(tmp)
173 173
174 174 if name == 'cached_repo_list':
175 175 from pylons_app.model.hg_model import _get_repos_cached
176 176 region_invalidate(_get_repos_cached, None, *args)
177 177
178 178 if name == 'full_changelog':
179 179 from pylons_app.model.hg_model import _full_changelog_cached
180 180 region_invalidate(_full_changelog_cached, None, *args)
181 181
182 182 class EmptyChangeset(BaseChangeset):
183 183
184 184 revision = -1
185 185 message = ''
186 186
187 187 @LazyProperty
188 188 def raw_id(self):
189 189 """
190 190 Returns raw string identifing this changeset, useful for web
191 191 representation.
192 192 """
193 193 return '0' * 12
194 194
195 195
196 196 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
197 197 """
198 198 maps all found repositories into db
199 199 """
200 200 from pylons_app.model.repo_model import RepoModel
201 201
202 202 sa = meta.Session
203 203 user = sa.query(User).filter(User.admin == True).first()
204 204
205 205 rm = RepoModel()
206 206
207 207 for name, repo in initial_repo_list.items():
208 208 if not sa.query(Repository).get(name):
209 209 log.info('repository %s not found creating default', name)
210 210
211 211 form_data = {
212 212 'repo_name':name,
213 213 'description':repo.description if repo.description != 'unknown' else \
214 214 'auto description for %s' % name,
215 215 'private':False
216 216 }
217 217 rm.create(form_data, user, just_db=True)
218 218
219 219
220 220 if remove_obsolete:
221 221 #remove from database those repositories that are not in the filesystem
222 222 for repo in sa.query(Repository).all():
223 223 if repo.repo_name not in initial_repo_list.keys():
224 224 sa.delete(repo)
225 225 sa.commit()
226 226
227 227
228 228 meta.Session.remove()
229
230 from UserDict import DictMixin
231
232 class OrderedDict(dict, DictMixin):
233
234 def __init__(self, *args, **kwds):
235 if len(args) > 1:
236 raise TypeError('expected at most 1 arguments, got %d' % len(args))
237 try:
238 self.__end
239 except AttributeError:
240 self.clear()
241 self.update(*args, **kwds)
242
243 def clear(self):
244 self.__end = end = []
245 end += [None, end, end] # sentinel node for doubly linked list
246 self.__map = {} # key --> [key, prev, next]
247 dict.clear(self)
248
249 def __setitem__(self, key, value):
250 if key not in self:
251 end = self.__end
252 curr = end[1]
253 curr[2] = end[1] = self.__map[key] = [key, curr, end]
254 dict.__setitem__(self, key, value)
255
256 def __delitem__(self, key):
257 dict.__delitem__(self, key)
258 key, prev, next = self.__map.pop(key)
259 prev[2] = next
260 next[1] = prev
261
262 def __iter__(self):
263 end = self.__end
264 curr = end[2]
265 while curr is not end:
266 yield curr[0]
267 curr = curr[2]
268
269 def __reversed__(self):
270 end = self.__end
271 curr = end[1]
272 while curr is not end:
273 yield curr[0]
274 curr = curr[1]
275
276 def popitem(self, last=True):
277 if not self:
278 raise KeyError('dictionary is empty')
279 if last:
280 key = reversed(self).next()
281 else:
282 key = iter(self).next()
283 value = self.pop(key)
284 return key, value
285
286 def __reduce__(self):
287 items = [[k, self[k]] for k in self]
288 tmp = self.__map, self.__end
289 del self.__map, self.__end
290 inst_dict = vars(self).copy()
291 self.__map, self.__end = tmp
292 if inst_dict:
293 return (self.__class__, (items,), inst_dict)
294 return self.__class__, (items,)
295
296 def keys(self):
297 return list(self)
298
299 setdefault = DictMixin.setdefault
300 update = DictMixin.update
301 pop = DictMixin.pop
302 values = DictMixin.values
303 items = DictMixin.items
304 iterkeys = DictMixin.iterkeys
305 itervalues = DictMixin.itervalues
306 iteritems = DictMixin.iteritems
307
308 def __repr__(self):
309 if not self:
310 return '%s()' % (self.__class__.__name__,)
311 return '%s(%r)' % (self.__class__.__name__, self.items())
312
313 def copy(self):
314 return self.__class__(self)
315
316 @classmethod
317 def fromkeys(cls, iterable, value=None):
318 d = cls()
319 for key in iterable:
320 d[key] = value
321 return d
322
323 def __eq__(self, other):
324 if isinstance(other, OrderedDict):
325 return len(self) == len(other) and self.items() == other.items()
326 return dict.__eq__(self, other)
327
328 def __ne__(self, other):
329 return not self == other
General Comments 0
You need to be logged in to leave comments. Login now