##// END OF EJS Templates
merged beta into stable
marcink -
r2033:abe75448 merge default
parent child Browse files
Show More
@@ -1,467 +1,464 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.__init__
3 rhodecode.lib.__init__
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Some simple helper functions
6 Some simple helper functions
7
7
8 :created_on: Jan 5, 2011
8 :created_on: Jan 5, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 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 re
27 import re
28 from rhodecode.lib.vcs.utils.lazy import LazyProperty
28 from rhodecode.lib.vcs.utils.lazy import LazyProperty
29
29
30
30
31 def __get_lem():
31 def __get_lem():
32 from pygments import lexers
32 from pygments import lexers
33 from string import lower
33 from string import lower
34 from collections import defaultdict
34 from collections import defaultdict
35
35
36 d = defaultdict(lambda: [])
36 d = defaultdict(lambda: [])
37
37
38 def __clean(s):
38 def __clean(s):
39 s = s.lstrip('*')
39 s = s.lstrip('*')
40 s = s.lstrip('.')
40 s = s.lstrip('.')
41
41
42 if s.find('[') != -1:
42 if s.find('[') != -1:
43 exts = []
43 exts = []
44 start, stop = s.find('['), s.find(']')
44 start, stop = s.find('['), s.find(']')
45
45
46 for suffix in s[start + 1:stop]:
46 for suffix in s[start + 1:stop]:
47 exts.append(s[:s.find('[')] + suffix)
47 exts.append(s[:s.find('[')] + suffix)
48 return map(lower, exts)
48 return map(lower, exts)
49 else:
49 else:
50 return map(lower, [s])
50 return map(lower, [s])
51
51
52 for lx, t in sorted(lexers.LEXERS.items()):
52 for lx, t in sorted(lexers.LEXERS.items()):
53 m = map(__clean, t[-2])
53 m = map(__clean, t[-2])
54 if m:
54 if m:
55 m = reduce(lambda x, y: x + y, m)
55 m = reduce(lambda x, y: x + y, m)
56 for ext in m:
56 for ext in m:
57 desc = lx.replace('Lexer', '')
57 desc = lx.replace('Lexer', '')
58 d[ext].append(desc)
58 d[ext].append(desc)
59
59
60 return dict(d)
60 return dict(d)
61
61
62 # language map is also used by whoosh indexer, which for those specified
62 # language map is also used by whoosh indexer, which for those specified
63 # extensions will index it's content
63 # extensions will index it's content
64 LANGUAGES_EXTENSIONS_MAP = __get_lem()
64 LANGUAGES_EXTENSIONS_MAP = __get_lem()
65
65
66 # Additional mappings that are not present in the pygments lexers
66 # Additional mappings that are not present in the pygments lexers
67 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
67 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
68 ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
68 ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
69
69
70 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
70 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
71
71
72 # list of readme files to search in file tree and display in summary
72 # list of readme files to search in file tree and display in summary
73 # attached weights defines the search order lower is first
73 # attached weights defines the search order lower is first
74 ALL_READMES = [
74 ALL_READMES = [
75 ('readme', 0), ('README', 0), ('Readme', 0),
75 ('readme', 0), ('README', 0), ('Readme', 0),
76 ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1),
76 ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1),
77 ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2),
77 ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2),
78 ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2),
78 ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2),
79 ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2),
79 ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2),
80 ]
80 ]
81
81
82 # extension together with weights to search lower is first
82 # extension together with weights to search lower is first
83 RST_EXTS = [
83 RST_EXTS = [
84 ('', 0), ('.rst', 1), ('.rest', 1),
84 ('', 0), ('.rst', 1), ('.rest', 1),
85 ('.RST', 2), ('.REST', 2),
85 ('.RST', 2), ('.REST', 2),
86 ('.txt', 3), ('.TXT', 3)
86 ('.txt', 3), ('.TXT', 3)
87 ]
87 ]
88
88
89 MARKDOWN_EXTS = [
89 MARKDOWN_EXTS = [
90 ('.md', 1), ('.MD', 1),
90 ('.md', 1), ('.MD', 1),
91 ('.mkdn', 2), ('.MKDN', 2),
91 ('.mkdn', 2), ('.MKDN', 2),
92 ('.mdown', 3), ('.MDOWN', 3),
92 ('.mdown', 3), ('.MDOWN', 3),
93 ('.markdown', 4), ('.MARKDOWN', 4)
93 ('.markdown', 4), ('.MARKDOWN', 4)
94 ]
94 ]
95
95
96 PLAIN_EXTS = [('.text', 2), ('.TEXT', 2)]
96 PLAIN_EXTS = [('.text', 2), ('.TEXT', 2)]
97
97
98 ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS
98 ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS
99
99
100
100
101 def str2bool(_str):
101 def str2bool(_str):
102 """
102 """
103 returs True/False value from given string, it tries to translate the
103 returs True/False value from given string, it tries to translate the
104 string into boolean
104 string into boolean
105
105
106 :param _str: string value to translate into boolean
106 :param _str: string value to translate into boolean
107 :rtype: boolean
107 :rtype: boolean
108 :returns: boolean from given string
108 :returns: boolean from given string
109 """
109 """
110 if _str is None:
110 if _str is None:
111 return False
111 return False
112 if _str in (True, False):
112 if _str in (True, False):
113 return _str
113 return _str
114 _str = str(_str).strip().lower()
114 _str = str(_str).strip().lower()
115 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
115 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
116
116
117
117
118 def convert_line_endings(line, mode):
118 def convert_line_endings(line, mode):
119 """
119 """
120 Converts a given line "line end" accordingly to given mode
120 Converts a given line "line end" accordingly to given mode
121
121
122 Available modes are::
122 Available modes are::
123 0 - Unix
123 0 - Unix
124 1 - Mac
124 1 - Mac
125 2 - DOS
125 2 - DOS
126
126
127 :param line: given line to convert
127 :param line: given line to convert
128 :param mode: mode to convert to
128 :param mode: mode to convert to
129 :rtype: str
129 :rtype: str
130 :return: converted line according to mode
130 :return: converted line according to mode
131 """
131 """
132 from string import replace
132 from string import replace
133
133
134 if mode == 0:
134 if mode == 0:
135 line = replace(line, '\r\n', '\n')
135 line = replace(line, '\r\n', '\n')
136 line = replace(line, '\r', '\n')
136 line = replace(line, '\r', '\n')
137 elif mode == 1:
137 elif mode == 1:
138 line = replace(line, '\r\n', '\r')
138 line = replace(line, '\r\n', '\r')
139 line = replace(line, '\n', '\r')
139 line = replace(line, '\n', '\r')
140 elif mode == 2:
140 elif mode == 2:
141 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
141 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
142 return line
142 return line
143
143
144
144
145 def detect_mode(line, default):
145 def detect_mode(line, default):
146 """
146 """
147 Detects line break for given line, if line break couldn't be found
147 Detects line break for given line, if line break couldn't be found
148 given default value is returned
148 given default value is returned
149
149
150 :param line: str line
150 :param line: str line
151 :param default: default
151 :param default: default
152 :rtype: int
152 :rtype: int
153 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
153 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
154 """
154 """
155 if line.endswith('\r\n'):
155 if line.endswith('\r\n'):
156 return 2
156 return 2
157 elif line.endswith('\n'):
157 elif line.endswith('\n'):
158 return 0
158 return 0
159 elif line.endswith('\r'):
159 elif line.endswith('\r'):
160 return 1
160 return 1
161 else:
161 else:
162 return default
162 return default
163
163
164
164
165 def generate_api_key(username, salt=None):
165 def generate_api_key(username, salt=None):
166 """
166 """
167 Generates unique API key for given username, if salt is not given
167 Generates unique API key for given username, if salt is not given
168 it'll be generated from some random string
168 it'll be generated from some random string
169
169
170 :param username: username as string
170 :param username: username as string
171 :param salt: salt to hash generate KEY
171 :param salt: salt to hash generate KEY
172 :rtype: str
172 :rtype: str
173 :returns: sha1 hash from username+salt
173 :returns: sha1 hash from username+salt
174 """
174 """
175 from tempfile import _RandomNameSequence
175 from tempfile import _RandomNameSequence
176 import hashlib
176 import hashlib
177
177
178 if salt is None:
178 if salt is None:
179 salt = _RandomNameSequence().next()
179 salt = _RandomNameSequence().next()
180
180
181 return hashlib.sha1(username + salt).hexdigest()
181 return hashlib.sha1(username + salt).hexdigest()
182
182
183
183
184 def safe_unicode(str_, from_encoding=None):
184 def safe_unicode(str_, from_encoding=None):
185 """
185 """
186 safe unicode function. Does few trick to turn str_ into unicode
186 safe unicode function. Does few trick to turn str_ into unicode
187
187
188 In case of UnicodeDecode error we try to return it with encoding detected
188 In case of UnicodeDecode error we try to return it with encoding detected
189 by chardet library if it fails fallback to unicode with errors replaced
189 by chardet library if it fails fallback to unicode with errors replaced
190
190
191 :param str_: string to decode
191 :param str_: string to decode
192 :rtype: unicode
192 :rtype: unicode
193 :returns: unicode object
193 :returns: unicode object
194 """
194 """
195 if isinstance(str_, unicode):
195 if isinstance(str_, unicode):
196 return str_
196 return str_
197
197
198 if not from_encoding:
198 if not from_encoding:
199 import rhodecode
199 import rhodecode
200 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
200 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
201 from_encoding = DEFAULT_ENCODING
201 from_encoding = DEFAULT_ENCODING
202
202
203 try:
203 try:
204 return unicode(str_)
204 return unicode(str_)
205 except UnicodeDecodeError:
205 except UnicodeDecodeError:
206 pass
206 pass
207
207
208 try:
208 try:
209 return unicode(str_, from_encoding)
209 return unicode(str_, from_encoding)
210 except UnicodeDecodeError:
210 except UnicodeDecodeError:
211 pass
211 pass
212
212
213 try:
213 try:
214 import chardet
214 import chardet
215 encoding = chardet.detect(str_)['encoding']
215 encoding = chardet.detect(str_)['encoding']
216 if encoding is None:
216 if encoding is None:
217 raise Exception()
217 raise Exception()
218 return str_.decode(encoding)
218 return str_.decode(encoding)
219 except (ImportError, UnicodeDecodeError, Exception):
219 except (ImportError, UnicodeDecodeError, Exception):
220 return unicode(str_, from_encoding, 'replace')
220 return unicode(str_, from_encoding, 'replace')
221
221
222
222
223 def safe_str(unicode_, to_encoding=None):
223 def safe_str(unicode_, to_encoding=None):
224 """
224 """
225 safe str function. Does few trick to turn unicode_ into string
225 safe str function. Does few trick to turn unicode_ into string
226
226
227 In case of UnicodeEncodeError we try to return it with encoding detected
227 In case of UnicodeEncodeError we try to return it with encoding detected
228 by chardet library if it fails fallback to string with errors replaced
228 by chardet library if it fails fallback to string with errors replaced
229
229
230 :param unicode_: unicode to encode
230 :param unicode_: unicode to encode
231 :rtype: str
231 :rtype: str
232 :returns: str object
232 :returns: str object
233 """
233 """
234
234
235 if not isinstance(unicode_, basestring):
235 if not isinstance(unicode_, basestring):
236 return str(unicode_)
236 return str(unicode_)
237
237
238 if not isinstance(unicode_, basestring):
239 return str(unicode_)
240
241 if isinstance(unicode_, str):
238 if isinstance(unicode_, str):
242 return unicode_
239 return unicode_
243
240
244 if not to_encoding:
241 if not to_encoding:
245 import rhodecode
242 import rhodecode
246 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
243 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
247 to_encoding = DEFAULT_ENCODING
244 to_encoding = DEFAULT_ENCODING
248
245
249 try:
246 try:
250 return unicode_.encode(to_encoding)
247 return unicode_.encode(to_encoding)
251 except UnicodeEncodeError:
248 except UnicodeEncodeError:
252 pass
249 pass
253
250
254 try:
251 try:
255 import chardet
252 import chardet
256 encoding = chardet.detect(unicode_)['encoding']
253 encoding = chardet.detect(unicode_)['encoding']
257 print encoding
254 print encoding
258 if encoding is None:
255 if encoding is None:
259 raise UnicodeEncodeError()
256 raise UnicodeEncodeError()
260
257
261 return unicode_.encode(encoding)
258 return unicode_.encode(encoding)
262 except (ImportError, UnicodeEncodeError):
259 except (ImportError, UnicodeEncodeError):
263 return unicode_.encode(to_encoding, 'replace')
260 return unicode_.encode(to_encoding, 'replace')
264
261
265 return safe_str
262 return safe_str
266
263
267
264
268 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
265 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
269 """
266 """
270 Custom engine_from_config functions that makes sure we use NullPool for
267 Custom engine_from_config functions that makes sure we use NullPool for
271 file based sqlite databases. This prevents errors on sqlite. This only
268 file based sqlite databases. This prevents errors on sqlite. This only
272 applies to sqlalchemy versions < 0.7.0
269 applies to sqlalchemy versions < 0.7.0
273
270
274 """
271 """
275 import sqlalchemy
272 import sqlalchemy
276 from sqlalchemy import engine_from_config as efc
273 from sqlalchemy import engine_from_config as efc
277 import logging
274 import logging
278
275
279 if int(sqlalchemy.__version__.split('.')[1]) < 7:
276 if int(sqlalchemy.__version__.split('.')[1]) < 7:
280
277
281 # This solution should work for sqlalchemy < 0.7.0, and should use
278 # This solution should work for sqlalchemy < 0.7.0, and should use
282 # proxy=TimerProxy() for execution time profiling
279 # proxy=TimerProxy() for execution time profiling
283
280
284 from sqlalchemy.pool import NullPool
281 from sqlalchemy.pool import NullPool
285 url = configuration[prefix + 'url']
282 url = configuration[prefix + 'url']
286
283
287 if url.startswith('sqlite'):
284 if url.startswith('sqlite'):
288 kwargs.update({'poolclass': NullPool})
285 kwargs.update({'poolclass': NullPool})
289 return efc(configuration, prefix, **kwargs)
286 return efc(configuration, prefix, **kwargs)
290 else:
287 else:
291 import time
288 import time
292 from sqlalchemy import event
289 from sqlalchemy import event
293 from sqlalchemy.engine import Engine
290 from sqlalchemy.engine import Engine
294
291
295 log = logging.getLogger('sqlalchemy.engine')
292 log = logging.getLogger('sqlalchemy.engine')
296 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
293 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
297 engine = efc(configuration, prefix, **kwargs)
294 engine = efc(configuration, prefix, **kwargs)
298
295
299 def color_sql(sql):
296 def color_sql(sql):
300 COLOR_SEQ = "\033[1;%dm"
297 COLOR_SEQ = "\033[1;%dm"
301 COLOR_SQL = YELLOW
298 COLOR_SQL = YELLOW
302 normal = '\x1b[0m'
299 normal = '\x1b[0m'
303 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
300 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
304
301
305 if configuration['debug']:
302 if configuration['debug']:
306 #attach events only for debug configuration
303 #attach events only for debug configuration
307
304
308 def before_cursor_execute(conn, cursor, statement,
305 def before_cursor_execute(conn, cursor, statement,
309 parameters, context, executemany):
306 parameters, context, executemany):
310 context._query_start_time = time.time()
307 context._query_start_time = time.time()
311 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
308 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
312
309
313
310
314 def after_cursor_execute(conn, cursor, statement,
311 def after_cursor_execute(conn, cursor, statement,
315 parameters, context, executemany):
312 parameters, context, executemany):
316 total = time.time() - context._query_start_time
313 total = time.time() - context._query_start_time
317 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
314 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
318
315
319 event.listen(engine, "before_cursor_execute",
316 event.listen(engine, "before_cursor_execute",
320 before_cursor_execute)
317 before_cursor_execute)
321 event.listen(engine, "after_cursor_execute",
318 event.listen(engine, "after_cursor_execute",
322 after_cursor_execute)
319 after_cursor_execute)
323
320
324 return engine
321 return engine
325
322
326
323
327 def age(curdate):
324 def age(curdate):
328 """
325 """
329 turns a datetime into an age string.
326 turns a datetime into an age string.
330
327
331 :param curdate: datetime object
328 :param curdate: datetime object
332 :rtype: unicode
329 :rtype: unicode
333 :returns: unicode words describing age
330 :returns: unicode words describing age
334 """
331 """
335
332
336 from datetime import datetime
333 from datetime import datetime
337 from webhelpers.date import time_ago_in_words
334 from webhelpers.date import time_ago_in_words
338
335
339 _ = lambda s: s
336 _ = lambda s: s
340
337
341 if not curdate:
338 if not curdate:
342 return ''
339 return ''
343
340
344 agescales = [(_(u"year"), 3600 * 24 * 365),
341 agescales = [(_(u"year"), 3600 * 24 * 365),
345 (_(u"month"), 3600 * 24 * 30),
342 (_(u"month"), 3600 * 24 * 30),
346 (_(u"day"), 3600 * 24),
343 (_(u"day"), 3600 * 24),
347 (_(u"hour"), 3600),
344 (_(u"hour"), 3600),
348 (_(u"minute"), 60),
345 (_(u"minute"), 60),
349 (_(u"second"), 1), ]
346 (_(u"second"), 1), ]
350
347
351 age = datetime.now() - curdate
348 age = datetime.now() - curdate
352 age_seconds = (age.days * agescales[2][1]) + age.seconds
349 age_seconds = (age.days * agescales[2][1]) + age.seconds
353 pos = 1
350 pos = 1
354 for scale in agescales:
351 for scale in agescales:
355 if scale[1] <= age_seconds:
352 if scale[1] <= age_seconds:
356 if pos == 6:
353 if pos == 6:
357 pos = 5
354 pos = 5
358 return '%s %s' % (time_ago_in_words(curdate,
355 return '%s %s' % (time_ago_in_words(curdate,
359 agescales[pos][0]), _('ago'))
356 agescales[pos][0]), _('ago'))
360 pos += 1
357 pos += 1
361
358
362 return _(u'just now')
359 return _(u'just now')
363
360
364
361
365 def uri_filter(uri):
362 def uri_filter(uri):
366 """
363 """
367 Removes user:password from given url string
364 Removes user:password from given url string
368
365
369 :param uri:
366 :param uri:
370 :rtype: unicode
367 :rtype: unicode
371 :returns: filtered list of strings
368 :returns: filtered list of strings
372 """
369 """
373 if not uri:
370 if not uri:
374 return ''
371 return ''
375
372
376 proto = ''
373 proto = ''
377
374
378 for pat in ('https://', 'http://'):
375 for pat in ('https://', 'http://'):
379 if uri.startswith(pat):
376 if uri.startswith(pat):
380 uri = uri[len(pat):]
377 uri = uri[len(pat):]
381 proto = pat
378 proto = pat
382 break
379 break
383
380
384 # remove passwords and username
381 # remove passwords and username
385 uri = uri[uri.find('@') + 1:]
382 uri = uri[uri.find('@') + 1:]
386
383
387 # get the port
384 # get the port
388 cred_pos = uri.find(':')
385 cred_pos = uri.find(':')
389 if cred_pos == -1:
386 if cred_pos == -1:
390 host, port = uri, None
387 host, port = uri, None
391 else:
388 else:
392 host, port = uri[:cred_pos], uri[cred_pos + 1:]
389 host, port = uri[:cred_pos], uri[cred_pos + 1:]
393
390
394 return filter(None, [proto, host, port])
391 return filter(None, [proto, host, port])
395
392
396
393
397 def credentials_filter(uri):
394 def credentials_filter(uri):
398 """
395 """
399 Returns a url with removed credentials
396 Returns a url with removed credentials
400
397
401 :param uri:
398 :param uri:
402 """
399 """
403
400
404 uri = uri_filter(uri)
401 uri = uri_filter(uri)
405 #check if we have port
402 #check if we have port
406 if len(uri) > 2 and uri[2]:
403 if len(uri) > 2 and uri[2]:
407 uri[2] = ':' + uri[2]
404 uri[2] = ':' + uri[2]
408
405
409 return ''.join(uri)
406 return ''.join(uri)
410
407
411
408
412 def get_changeset_safe(repo, rev):
409 def get_changeset_safe(repo, rev):
413 """
410 """
414 Safe version of get_changeset if this changeset doesn't exists for a
411 Safe version of get_changeset if this changeset doesn't exists for a
415 repo it returns a Dummy one instead
412 repo it returns a Dummy one instead
416
413
417 :param repo:
414 :param repo:
418 :param rev:
415 :param rev:
419 """
416 """
420 from rhodecode.lib.vcs.backends.base import BaseRepository
417 from rhodecode.lib.vcs.backends.base import BaseRepository
421 from rhodecode.lib.vcs.exceptions import RepositoryError
418 from rhodecode.lib.vcs.exceptions import RepositoryError
422 if not isinstance(repo, BaseRepository):
419 if not isinstance(repo, BaseRepository):
423 raise Exception('You must pass an Repository '
420 raise Exception('You must pass an Repository '
424 'object as first argument got %s', type(repo))
421 'object as first argument got %s', type(repo))
425
422
426 try:
423 try:
427 cs = repo.get_changeset(rev)
424 cs = repo.get_changeset(rev)
428 except RepositoryError:
425 except RepositoryError:
429 from rhodecode.lib.utils import EmptyChangeset
426 from rhodecode.lib.utils import EmptyChangeset
430 cs = EmptyChangeset(requested_revision=rev)
427 cs = EmptyChangeset(requested_revision=rev)
431 return cs
428 return cs
432
429
433
430
434 def get_current_revision(quiet=False):
431 def get_current_revision(quiet=False):
435 """
432 """
436 Returns tuple of (number, id) from repository containing this package
433 Returns tuple of (number, id) from repository containing this package
437 or None if repository could not be found.
434 or None if repository could not be found.
438
435
439 :param quiet: prints error for fetching revision if True
436 :param quiet: prints error for fetching revision if True
440 """
437 """
441
438
442 try:
439 try:
443 from rhodecode.lib.vcs import get_repo
440 from rhodecode.lib.vcs import get_repo
444 from rhodecode.lib.vcs.utils.helpers import get_scm
441 from rhodecode.lib.vcs.utils.helpers import get_scm
445 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
442 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
446 scm = get_scm(repopath)[0]
443 scm = get_scm(repopath)[0]
447 repo = get_repo(path=repopath, alias=scm)
444 repo = get_repo(path=repopath, alias=scm)
448 tip = repo.get_changeset()
445 tip = repo.get_changeset()
449 return (tip.revision, tip.short_id)
446 return (tip.revision, tip.short_id)
450 except Exception, err:
447 except Exception, err:
451 if not quiet:
448 if not quiet:
452 print ("Cannot retrieve rhodecode's revision. Original error "
449 print ("Cannot retrieve rhodecode's revision. Original error "
453 "was: %s" % err)
450 "was: %s" % err)
454 return None
451 return None
455
452
456
453
457 def extract_mentioned_users(s):
454 def extract_mentioned_users(s):
458 """
455 """
459 Returns unique usernames from given string s that have @mention
456 Returns unique usernames from given string s that have @mention
460
457
461 :param s: string to get mentions
458 :param s: string to get mentions
462 """
459 """
463 usrs = {}
460 usrs = {}
464 for username in re.findall(r'(?:^@|\s@)(\w+)', s):
461 for username in re.findall(r'(?:^@|\s@)(\w+)', s):
465 usrs[username] = username
462 usrs[username] = username
466
463
467 return sorted(usrs.keys())
464 return sorted(usrs.keys())
General Comments 0
You need to be logged in to leave comments. Login now