##// END OF EJS Templates
fixed typo
marcink -
r4014:7c0eff86 default
parent child Browse files
Show More
@@ -1,655 +1,655 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
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 import sys
28 import sys
29 import time
29 import time
30 import uuid
30 import uuid
31 import datetime
31 import datetime
32 import traceback
32 import traceback
33 import webob
33 import webob
34
34
35 from pylons.i18n.translation import _, ungettext
35 from pylons.i18n.translation import _, ungettext
36 from rhodecode.lib.vcs.utils.lazy import LazyProperty
36 from rhodecode.lib.vcs.utils.lazy import LazyProperty
37 from rhodecode.lib.compat import json
37 from rhodecode.lib.compat import json
38
38
39
39
40 def __get_lem():
40 def __get_lem():
41 """
41 """
42 Get language extension map based on what's inside pygments lexers
42 Get language extension map based on what's inside pygments lexers
43 """
43 """
44 from pygments import lexers
44 from pygments import lexers
45 from string import lower
45 from string import lower
46 from collections import defaultdict
46 from collections import defaultdict
47
47
48 d = defaultdict(lambda: [])
48 d = defaultdict(lambda: [])
49
49
50 def __clean(s):
50 def __clean(s):
51 s = s.lstrip('*')
51 s = s.lstrip('*')
52 s = s.lstrip('.')
52 s = s.lstrip('.')
53
53
54 if s.find('[') != -1:
54 if s.find('[') != -1:
55 exts = []
55 exts = []
56 start, stop = s.find('['), s.find(']')
56 start, stop = s.find('['), s.find(']')
57
57
58 for suffix in s[start + 1:stop]:
58 for suffix in s[start + 1:stop]:
59 exts.append(s[:s.find('[')] + suffix)
59 exts.append(s[:s.find('[')] + suffix)
60 return map(lower, exts)
60 return map(lower, exts)
61 else:
61 else:
62 return map(lower, [s])
62 return map(lower, [s])
63
63
64 for lx, t in sorted(lexers.LEXERS.items()):
64 for lx, t in sorted(lexers.LEXERS.items()):
65 m = map(__clean, t[-2])
65 m = map(__clean, t[-2])
66 if m:
66 if m:
67 m = reduce(lambda x, y: x + y, m)
67 m = reduce(lambda x, y: x + y, m)
68 for ext in m:
68 for ext in m:
69 desc = lx.replace('Lexer', '')
69 desc = lx.replace('Lexer', '')
70 d[ext].append(desc)
70 d[ext].append(desc)
71
71
72 return dict(d)
72 return dict(d)
73
73
74
74
75 def str2bool(_str):
75 def str2bool(_str):
76 """
76 """
77 returs True/False value from given string, it tries to translate the
77 returs True/False value from given string, it tries to translate the
78 string into boolean
78 string into boolean
79
79
80 :param _str: string value to translate into boolean
80 :param _str: string value to translate into boolean
81 :rtype: boolean
81 :rtype: boolean
82 :returns: boolean from given string
82 :returns: boolean from given string
83 """
83 """
84 if _str is None:
84 if _str is None:
85 return False
85 return False
86 if _str in (True, False):
86 if _str in (True, False):
87 return _str
87 return _str
88 _str = str(_str).strip().lower()
88 _str = str(_str).strip().lower()
89 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
89 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
90
90
91
91
92 def aslist(obj, sep=None, strip=True):
92 def aslist(obj, sep=None, strip=True):
93 """
93 """
94 Returns given string separated by sep as list
94 Returns given string separated by sep as list
95
95
96 :param obj:
96 :param obj:
97 :param sep:
97 :param sep:
98 :param strip:
98 :param strip:
99 """
99 """
100 if isinstance(obj, (basestring)):
100 if isinstance(obj, (basestring)):
101 lst = obj.split(sep)
101 lst = obj.split(sep)
102 if strip:
102 if strip:
103 lst = [v.strip() for v in lst]
103 lst = [v.strip() for v in lst]
104 return lst
104 return lst
105 elif isinstance(obj, (list, tuple)):
105 elif isinstance(obj, (list, tuple)):
106 return obj
106 return obj
107 elif obj is None:
107 elif obj is None:
108 return []
108 return []
109 else:
109 else:
110 return [obj]
110 return [obj]
111
111
112
112
113 def convert_line_endings(line, mode):
113 def convert_line_endings(line, mode):
114 """
114 """
115 Converts a given line "line end" accordingly to given mode
115 Converts a given line "line end" accordingly to given mode
116
116
117 Available modes are::
117 Available modes are::
118 0 - Unix
118 0 - Unix
119 1 - Mac
119 1 - Mac
120 2 - DOS
120 2 - DOS
121
121
122 :param line: given line to convert
122 :param line: given line to convert
123 :param mode: mode to convert to
123 :param mode: mode to convert to
124 :rtype: str
124 :rtype: str
125 :return: converted line according to mode
125 :return: converted line according to mode
126 """
126 """
127 from string import replace
127 from string import replace
128
128
129 if mode == 0:
129 if mode == 0:
130 line = replace(line, '\r\n', '\n')
130 line = replace(line, '\r\n', '\n')
131 line = replace(line, '\r', '\n')
131 line = replace(line, '\r', '\n')
132 elif mode == 1:
132 elif mode == 1:
133 line = replace(line, '\r\n', '\r')
133 line = replace(line, '\r\n', '\r')
134 line = replace(line, '\n', '\r')
134 line = replace(line, '\n', '\r')
135 elif mode == 2:
135 elif mode == 2:
136 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
136 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
137 return line
137 return line
138
138
139
139
140 def detect_mode(line, default):
140 def detect_mode(line, default):
141 """
141 """
142 Detects line break for given line, if line break couldn't be found
142 Detects line break for given line, if line break couldn't be found
143 given default value is returned
143 given default value is returned
144
144
145 :param line: str line
145 :param line: str line
146 :param default: default
146 :param default: default
147 :rtype: int
147 :rtype: int
148 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
148 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
149 """
149 """
150 if line.endswith('\r\n'):
150 if line.endswith('\r\n'):
151 return 2
151 return 2
152 elif line.endswith('\n'):
152 elif line.endswith('\n'):
153 return 0
153 return 0
154 elif line.endswith('\r'):
154 elif line.endswith('\r'):
155 return 1
155 return 1
156 else:
156 else:
157 return default
157 return default
158
158
159
159
160 def generate_api_key(username, salt=None):
160 def generate_api_key(username, salt=None):
161 """
161 """
162 Generates unique API key for given username, if salt is not given
162 Generates unique API key for given username, if salt is not given
163 it'll be generated from some random string
163 it'll be generated from some random string
164
164
165 :param username: username as string
165 :param username: username as string
166 :param salt: salt to hash generate KEY
166 :param salt: salt to hash generate KEY
167 :rtype: str
167 :rtype: str
168 :returns: sha1 hash from username+salt
168 :returns: sha1 hash from username+salt
169 """
169 """
170 from tempfile import _RandomNameSequence
170 from tempfile import _RandomNameSequence
171 import hashlib
171 import hashlib
172
172
173 if salt is None:
173 if salt is None:
174 salt = _RandomNameSequence().next()
174 salt = _RandomNameSequence().next()
175
175
176 return hashlib.sha1(username + salt).hexdigest()
176 return hashlib.sha1(username + salt).hexdigest()
177
177
178
178
179 def safe_int(val, default=None):
179 def safe_int(val, default=None):
180 """
180 """
181 Returns int() of val if val is not convertable to int use default
181 Returns int() of val if val is not convertable to int use default
182 instead
182 instead
183
183
184 :param val:
184 :param val:
185 :param default:
185 :param default:
186 """
186 """
187
187
188 try:
188 try:
189 val = int(val)
189 val = int(val)
190 except (ValueError, TypeError):
190 except (ValueError, TypeError):
191 val = default
191 val = default
192
192
193 return val
193 return val
194
194
195
195
196 def safe_unicode(str_, from_encoding=None):
196 def safe_unicode(str_, from_encoding=None):
197 """
197 """
198 safe unicode function. Does few trick to turn str_ into unicode
198 safe unicode function. Does few trick to turn str_ into unicode
199
199
200 In case of UnicodeDecode error we try to return it with encoding detected
200 In case of UnicodeDecode error we try to return it with encoding detected
201 by chardet library if it fails fallback to unicode with errors replaced
201 by chardet library if it fails fallback to unicode with errors replaced
202
202
203 :param str_: string to decode
203 :param str_: string to decode
204 :rtype: unicode
204 :rtype: unicode
205 :returns: unicode object
205 :returns: unicode object
206 """
206 """
207 if isinstance(str_, unicode):
207 if isinstance(str_, unicode):
208 return str_
208 return str_
209
209
210 if not from_encoding:
210 if not from_encoding:
211 import rhodecode
211 import rhodecode
212 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
212 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
213 'utf8'), sep=',')
213 'utf8'), sep=',')
214 from_encoding = DEFAULT_ENCODINGS
214 from_encoding = DEFAULT_ENCODINGS
215
215
216 if not isinstance(from_encoding, (list, tuple)):
216 if not isinstance(from_encoding, (list, tuple)):
217 from_encoding = [from_encoding]
217 from_encoding = [from_encoding]
218
218
219 try:
219 try:
220 return unicode(str_)
220 return unicode(str_)
221 except UnicodeDecodeError:
221 except UnicodeDecodeError:
222 pass
222 pass
223
223
224 for enc in from_encoding:
224 for enc in from_encoding:
225 try:
225 try:
226 return unicode(str_, enc)
226 return unicode(str_, enc)
227 except UnicodeDecodeError:
227 except UnicodeDecodeError:
228 pass
228 pass
229
229
230 try:
230 try:
231 import chardet
231 import chardet
232 encoding = chardet.detect(str_)['encoding']
232 encoding = chardet.detect(str_)['encoding']
233 if encoding is None:
233 if encoding is None:
234 raise Exception()
234 raise Exception()
235 return str_.decode(encoding)
235 return str_.decode(encoding)
236 except (ImportError, UnicodeDecodeError, Exception):
236 except (ImportError, UnicodeDecodeError, Exception):
237 return unicode(str_, from_encoding[0], 'replace')
237 return unicode(str_, from_encoding[0], 'replace')
238
238
239
239
240 def safe_str(unicode_, to_encoding=None):
240 def safe_str(unicode_, to_encoding=None):
241 """
241 """
242 safe str function. Does few trick to turn unicode_ into string
242 safe str function. Does few trick to turn unicode_ into string
243
243
244 In case of UnicodeEncodeError we try to return it with encoding detected
244 In case of UnicodeEncodeError we try to return it with encoding detected
245 by chardet library if it fails fallback to string with errors replaced
245 by chardet library if it fails fallback to string with errors replaced
246
246
247 :param unicode_: unicode to encode
247 :param unicode_: unicode to encode
248 :rtype: str
248 :rtype: str
249 :returns: str object
249 :returns: str object
250 """
250 """
251
251
252 # if it's not basestr cast to str
252 # if it's not basestr cast to str
253 if not isinstance(unicode_, basestring):
253 if not isinstance(unicode_, basestring):
254 return str(unicode_)
254 return str(unicode_)
255
255
256 if isinstance(unicode_, str):
256 if isinstance(unicode_, str):
257 return unicode_
257 return unicode_
258
258
259 if not to_encoding:
259 if not to_encoding:
260 import rhodecode
260 import rhodecode
261 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
261 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
262 'utf8'), sep=',')
262 'utf8'), sep=',')
263 to_encoding = DEFAULT_ENCODINGS
263 to_encoding = DEFAULT_ENCODINGS
264
264
265 if not isinstance(to_encoding, (list, tuple)):
265 if not isinstance(to_encoding, (list, tuple)):
266 to_encoding = [to_encoding]
266 to_encoding = [to_encoding]
267
267
268 for enc in to_encoding:
268 for enc in to_encoding:
269 try:
269 try:
270 return unicode_.encode(enc)
270 return unicode_.encode(enc)
271 except UnicodeEncodeError:
271 except UnicodeEncodeError:
272 pass
272 pass
273
273
274 try:
274 try:
275 import chardet
275 import chardet
276 encoding = chardet.detect(unicode_)['encoding']
276 encoding = chardet.detect(unicode_)['encoding']
277 if encoding is None:
277 if encoding is None:
278 raise UnicodeEncodeError()
278 raise UnicodeEncodeError()
279
279
280 return unicode_.encode(encoding)
280 return unicode_.encode(encoding)
281 except (ImportError, UnicodeEncodeError):
281 except (ImportError, UnicodeEncodeError):
282 return unicode_.encode(to_encoding[0], 'replace')
282 return unicode_.encode(to_encoding[0], 'replace')
283
283
284
284
285 def remove_suffix(s, suffix):
285 def remove_suffix(s, suffix):
286 if s.endswith(suffix):
286 if s.endswith(suffix):
287 s = s[:-1 * len(suffix)]
287 s = s[:-1 * len(suffix)]
288 return s
288 return s
289
289
290
290
291 def remove_prefix(s, prefix):
291 def remove_prefix(s, prefix):
292 if s.startswith(prefix):
292 if s.startswith(prefix):
293 s = s[len(prefix):]
293 s = s[len(prefix):]
294 return s
294 return s
295
295
296
296
297 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
297 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
298 """
298 """
299 Custom engine_from_config functions that makes sure we use NullPool for
299 Custom engine_from_config functions that makes sure we use NullPool for
300 file based sqlite databases. This prevents errors on sqlite. This only
300 file based sqlite databases. This prevents errors on sqlite. This only
301 applies to sqlalchemy versions < 0.7.0
301 applies to sqlalchemy versions < 0.7.0
302
302
303 """
303 """
304 import sqlalchemy
304 import sqlalchemy
305 from sqlalchemy import engine_from_config as efc
305 from sqlalchemy import engine_from_config as efc
306 import logging
306 import logging
307
307
308 if int(sqlalchemy.__version__.split('.')[1]) < 7:
308 if int(sqlalchemy.__version__.split('.')[1]) < 7:
309
309
310 # This solution should work for sqlalchemy < 0.7.0, and should use
310 # This solution should work for sqlalchemy < 0.7.0, and should use
311 # proxy=TimerProxy() for execution time profiling
311 # proxy=TimerProxy() for execution time profiling
312
312
313 from sqlalchemy.pool import NullPool
313 from sqlalchemy.pool import NullPool
314 url = configuration[prefix + 'url']
314 url = configuration[prefix + 'url']
315
315
316 if url.startswith('sqlite'):
316 if url.startswith('sqlite'):
317 kwargs.update({'poolclass': NullPool})
317 kwargs.update({'poolclass': NullPool})
318 return efc(configuration, prefix, **kwargs)
318 return efc(configuration, prefix, **kwargs)
319 else:
319 else:
320 import time
320 import time
321 from sqlalchemy import event
321 from sqlalchemy import event
322 from sqlalchemy.engine import Engine
322 from sqlalchemy.engine import Engine
323
323
324 log = logging.getLogger('sqlalchemy.engine')
324 log = logging.getLogger('sqlalchemy.engine')
325 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
325 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
326 engine = efc(configuration, prefix, **kwargs)
326 engine = efc(configuration, prefix, **kwargs)
327
327
328 def color_sql(sql):
328 def color_sql(sql):
329 COLOR_SEQ = "\033[1;%dm"
329 COLOR_SEQ = "\033[1;%dm"
330 COLOR_SQL = YELLOW
330 COLOR_SQL = YELLOW
331 normal = '\x1b[0m'
331 normal = '\x1b[0m'
332 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
332 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
333
333
334 if configuration['debug']:
334 if configuration['debug']:
335 #attach events only for debug configuration
335 #attach events only for debug configuration
336
336
337 def before_cursor_execute(conn, cursor, statement,
337 def before_cursor_execute(conn, cursor, statement,
338 parameters, context, executemany):
338 parameters, context, executemany):
339 context._query_start_time = time.time()
339 context._query_start_time = time.time()
340 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
340 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
341
341
342 def after_cursor_execute(conn, cursor, statement,
342 def after_cursor_execute(conn, cursor, statement,
343 parameters, context, executemany):
343 parameters, context, executemany):
344 total = time.time() - context._query_start_time
344 total = time.time() - context._query_start_time
345 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
345 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
346
346
347 event.listen(engine, "before_cursor_execute",
347 event.listen(engine, "before_cursor_execute",
348 before_cursor_execute)
348 before_cursor_execute)
349 event.listen(engine, "after_cursor_execute",
349 event.listen(engine, "after_cursor_execute",
350 after_cursor_execute)
350 after_cursor_execute)
351
351
352 return engine
352 return engine
353
353
354
354
355 def age(prevdate, show_short_version=False, now=None):
355 def age(prevdate, show_short_version=False, now=None):
356 """
356 """
357 turns a datetime into an age string.
357 turns a datetime into an age string.
358 If show_short_version is True, then it will generate a not so accurate but shorter string,
358 If show_short_version is True, then it will generate a not so accurate but shorter string,
359 example: 2days ago, instead of 2 days and 23 hours ago.
359 example: 2days ago, instead of 2 days and 23 hours ago.
360
360
361 :param prevdate: datetime object
361 :param prevdate: datetime object
362 :param show_short_version: if it should aproximate the date and return a shorter string
362 :param show_short_version: if it should aproximate the date and return a shorter string
363 :rtype: unicode
363 :rtype: unicode
364 :returns: unicode words describing age
364 :returns: unicode words describing age
365 """
365 """
366 now = now or datetime.datetime.now()
366 now = now or datetime.datetime.now()
367 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
367 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
368 deltas = {}
368 deltas = {}
369 future = False
369 future = False
370
370
371 if prevdate > now:
371 if prevdate > now:
372 now, prevdate = prevdate, now
372 now, prevdate = prevdate, now
373 future = True
373 future = True
374 if future:
374 if future:
375 prevdate = prevdate.replace(microsecond=0)
375 prevdate = prevdate.replace(microsecond=0)
376 # Get date parts deltas
376 # Get date parts deltas
377 from dateutil import relativedelta
377 from dateutil import relativedelta
378 for part in order:
378 for part in order:
379 d = relativedelta.relativedelta(now, prevdate)
379 d = relativedelta.relativedelta(now, prevdate)
380 deltas[part] = getattr(d, part + 's')
380 deltas[part] = getattr(d, part + 's')
381
381
382 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
382 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
383 # not 1 hour, -59 minutes and -59 seconds)
383 # not 1 hour, -59 minutes and -59 seconds)
384 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
384 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
385 part = order[num]
385 part = order[num]
386 carry_part = order[num - 1]
386 carry_part = order[num - 1]
387
387
388 if deltas[part] < 0:
388 if deltas[part] < 0:
389 deltas[part] += length
389 deltas[part] += length
390 deltas[carry_part] -= 1
390 deltas[carry_part] -= 1
391
391
392 # Same thing for days except that the increment depends on the (variable)
392 # Same thing for days except that the increment depends on the (variable)
393 # number of days in the month
393 # number of days in the month
394 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
394 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
395 if deltas['day'] < 0:
395 if deltas['day'] < 0:
396 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
396 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
397 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
397 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
398 deltas['day'] += 29
398 deltas['day'] += 29
399 else:
399 else:
400 deltas['day'] += month_lengths[prevdate.month - 1]
400 deltas['day'] += month_lengths[prevdate.month - 1]
401
401
402 deltas['month'] -= 1
402 deltas['month'] -= 1
403
403
404 if deltas['month'] < 0:
404 if deltas['month'] < 0:
405 deltas['month'] += 12
405 deltas['month'] += 12
406 deltas['year'] -= 1
406 deltas['year'] -= 1
407
407
408 # Format the result
408 # Format the result
409 fmt_funcs = {
409 fmt_funcs = {
410 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
410 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
411 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
411 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
412 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
412 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
413 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
413 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
414 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
414 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
415 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
415 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
416 }
416 }
417
417
418 for i, part in enumerate(order):
418 for i, part in enumerate(order):
419 value = deltas[part]
419 value = deltas[part]
420 if value == 0:
420 if value == 0:
421 continue
421 continue
422
422
423 if i < 5:
423 if i < 5:
424 sub_part = order[i + 1]
424 sub_part = order[i + 1]
425 sub_value = deltas[sub_part]
425 sub_value = deltas[sub_part]
426 else:
426 else:
427 sub_value = 0
427 sub_value = 0
428
428
429 if sub_value == 0 or show_short_version:
429 if sub_value == 0 or show_short_version:
430 if future:
430 if future:
431 return _(u'in %s') % fmt_funcs[part](value)
431 return _(u'in %s') % fmt_funcs[part](value)
432 else:
432 else:
433 return _(u'%s ago') % fmt_funcs[part](value)
433 return _(u'%s ago') % fmt_funcs[part](value)
434 if future:
434 if future:
435 return _(u'in %s and %s') % (fmt_funcs[part](value),
435 return _(u'in %s and %s') % (fmt_funcs[part](value),
436 fmt_funcs[sub_part](sub_value))
436 fmt_funcs[sub_part](sub_value))
437 else:
437 else:
438 return _(u'%s and %s ago') % (fmt_funcs[part](value),
438 return _(u'%s and %s ago') % (fmt_funcs[part](value),
439 fmt_funcs[sub_part](sub_value))
439 fmt_funcs[sub_part](sub_value))
440
440
441 return _(u'just now')
441 return _(u'just now')
442
442
443
443
444 def uri_filter(uri):
444 def uri_filter(uri):
445 """
445 """
446 Removes user:password from given url string
446 Removes user:password from given url string
447
447
448 :param uri:
448 :param uri:
449 :rtype: unicode
449 :rtype: unicode
450 :returns: filtered list of strings
450 :returns: filtered list of strings
451 """
451 """
452 if not uri:
452 if not uri:
453 return ''
453 return ''
454
454
455 proto = ''
455 proto = ''
456
456
457 for pat in ('https://', 'http://'):
457 for pat in ('https://', 'http://'):
458 if uri.startswith(pat):
458 if uri.startswith(pat):
459 uri = uri[len(pat):]
459 uri = uri[len(pat):]
460 proto = pat
460 proto = pat
461 break
461 break
462
462
463 # remove passwords and username
463 # remove passwords and username
464 uri = uri[uri.find('@') + 1:]
464 uri = uri[uri.find('@') + 1:]
465
465
466 # get the port
466 # get the port
467 cred_pos = uri.find(':')
467 cred_pos = uri.find(':')
468 if cred_pos == -1:
468 if cred_pos == -1:
469 host, port = uri, None
469 host, port = uri, None
470 else:
470 else:
471 host, port = uri[:cred_pos], uri[cred_pos + 1:]
471 host, port = uri[:cred_pos], uri[cred_pos + 1:]
472
472
473 return filter(None, [proto, host, port])
473 return filter(None, [proto, host, port])
474
474
475
475
476 def credentials_filter(uri):
476 def credentials_filter(uri):
477 """
477 """
478 Returns a url with removed credentials
478 Returns a url with removed credentials
479
479
480 :param uri:
480 :param uri:
481 """
481 """
482
482
483 uri = uri_filter(uri)
483 uri = uri_filter(uri)
484 #check if we have port
484 #check if we have port
485 if len(uri) > 2 and uri[2]:
485 if len(uri) > 2 and uri[2]:
486 uri[2] = ':' + uri[2]
486 uri[2] = ':' + uri[2]
487
487
488 return ''.join(uri)
488 return ''.join(uri)
489
489
490
490
491 def get_changeset_safe(repo, rev):
491 def get_changeset_safe(repo, rev):
492 """
492 """
493 Safe version of get_changeset if this changeset doesn't exists for a
493 Safe version of get_changeset if this changeset doesn't exists for a
494 repo it returns a Dummy one instead
494 repo it returns a Dummy one instead
495
495
496 :param repo:
496 :param repo:
497 :param rev:
497 :param rev:
498 """
498 """
499 from rhodecode.lib.vcs.backends.base import BaseRepository
499 from rhodecode.lib.vcs.backends.base import BaseRepository
500 from rhodecode.lib.vcs.exceptions import RepositoryError
500 from rhodecode.lib.vcs.exceptions import RepositoryError
501 from rhodecode.lib.vcs.backends.base import EmptyChangeset
501 from rhodecode.lib.vcs.backends.base import EmptyChangeset
502 if not isinstance(repo, BaseRepository):
502 if not isinstance(repo, BaseRepository):
503 raise Exception('You must pass an Repository '
503 raise Exception('You must pass an Repository '
504 'object as first argument got %s', type(repo))
504 'object as first argument got %s', type(repo))
505
505
506 try:
506 try:
507 cs = repo.get_changeset(rev)
507 cs = repo.get_changeset(rev)
508 except RepositoryError:
508 except RepositoryError:
509 cs = EmptyChangeset(requested_revision=rev)
509 cs = EmptyChangeset(requested_revision=rev)
510 return cs
510 return cs
511
511
512
512
513 def datetime_to_time(dt):
513 def datetime_to_time(dt):
514 if dt:
514 if dt:
515 return time.mktime(dt.timetuple())
515 return time.mktime(dt.timetuple())
516
516
517
517
518 def time_to_datetime(tm):
518 def time_to_datetime(tm):
519 if tm:
519 if tm:
520 if isinstance(tm, basestring):
520 if isinstance(tm, basestring):
521 try:
521 try:
522 tm = float(tm)
522 tm = float(tm)
523 except ValueError:
523 except ValueError:
524 return
524 return
525 return datetime.datetime.fromtimestamp(tm)
525 return datetime.datetime.fromtimestamp(tm)
526
526
527 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
527 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
528
528
529
529
530 def extract_mentioned_users(s):
530 def extract_mentioned_users(s):
531 """
531 """
532 Returns unique usernames from given string s that have @mention
532 Returns unique usernames from given string s that have @mention
533
533
534 :param s: string to get mentions
534 :param s: string to get mentions
535 """
535 """
536 usrs = set()
536 usrs = set()
537 for username in re.findall(MENTIONS_REGEX, s):
537 for username in re.findall(MENTIONS_REGEX, s):
538 usrs.add(username)
538 usrs.add(username)
539
539
540 return sorted(list(usrs), key=lambda k: k.lower())
540 return sorted(list(usrs), key=lambda k: k.lower())
541
541
542
542
543 class AttributeDict(dict):
543 class AttributeDict(dict):
544 def __getattr__(self, attr):
544 def __getattr__(self, attr):
545 return self.get(attr, None)
545 return self.get(attr, None)
546 __setattr__ = dict.__setitem__
546 __setattr__ = dict.__setitem__
547 __delattr__ = dict.__delitem__
547 __delattr__ = dict.__delitem__
548
548
549
549
550 def fix_PATH(os_=None):
550 def fix_PATH(os_=None):
551 """
551 """
552 Get current active python path, and append it to PATH variable to fix issues
552 Get current active python path, and append it to PATH variable to fix issues
553 of subprocess calls and different python versions
553 of subprocess calls and different python versions
554 """
554 """
555 if os_ is None:
555 if os_ is None:
556 import os
556 import os
557 else:
557 else:
558 os = os_
558 os = os_
559
559
560 cur_path = os.path.split(sys.executable)[0]
560 cur_path = os.path.split(sys.executable)[0]
561 if not os.environ['PATH'].startswith(cur_path):
561 if not os.environ['PATH'].startswith(cur_path):
562 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
562 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
563
563
564
564
565 def obfuscate_url_pw(engine):
565 def obfuscate_url_pw(engine):
566 _url = engine or ''
566 _url = engine or ''
567 from sqlalchemy.engine import url as sa_url
567 from sqlalchemy.engine import url as sa_url
568 try:
568 try:
569 _url = sa_url.make_url(engine)
569 _url = sa_url.make_url(engine)
570 if _url.password:
570 if _url.password:
571 _url.password = 'XXXXX'
571 _url.password = 'XXXXX'
572 except Exception:
572 except Exception:
573 pass
573 pass
574 return str(_url)
574 return str(_url)
575
575
576
576
577 def get_server_url(environ):
577 def get_server_url(environ):
578 req = webob.Request(environ)
578 req = webob.Request(environ)
579 return req.host_url + req.script_name
579 return req.host_url + req.script_name
580
580
581
581
582 def _extract_extras(env=None):
582 def _extract_extras(env=None):
583 """
583 """
584 Extracts the rc extras data from os.environ, and wraps it into named
584 Extracts the rc extras data from os.environ, and wraps it into named
585 AttributeDict object
585 AttributeDict object
586 """
586 """
587 if not env:
587 if not env:
588 env = os.environ
588 env = os.environ
589
589
590 try:
590 try:
591 rc_extras = json.loads(env['RC_SCM_DATA'])
591 rc_extras = json.loads(env['RC_SCM_DATA'])
592 except Exception:
592 except Exception:
593 print os.environ
593 print os.environ
594 print >> sys.stderr, traceback.format_exc()
594 print >> sys.stderr, traceback.format_exc()
595 rc_extras = {}
595 rc_extras = {}
596
596
597 try:
597 try:
598 for k in ['username', 'repository', 'locked_by', 'scm', 'make_lock',
598 for k in ['username', 'repository', 'locked_by', 'scm', 'make_lock',
599 'action', 'ip']:
599 'action', 'ip']:
600 rc_extras[k]
600 rc_extras[k]
601 except KeyError, e:
601 except KeyError, e:
602 raise Exception('Missing key %s in os.environ %s' % (e, rc_extras))
602 raise Exception('Missing key %s in os.environ %s' % (e, rc_extras))
603
603
604 return AttributeDict(rc_extras)
604 return AttributeDict(rc_extras)
605
605
606
606
607 def _set_extras(extras):
607 def _set_extras(extras):
608 os.environ['RC_SCM_DATA'] = json.dumps(extras)
608 os.environ['RC_SCM_DATA'] = json.dumps(extras)
609
609
610
610
611 def unique_id(hexlen=32):
611 def unique_id(hexlen=32):
612 alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz"
612 alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz"
613 return suuid(truncate_to=hexlen, alphabet=alphabet)
613 return suuid(truncate_to=hexlen, alphabet=alphabet)
614
614
615
615
616 def suuid(url=None, truncate_to=22, alphabet=None):
616 def suuid(url=None, truncate_to=22, alphabet=None):
617 """
617 """
618 Generate and return a short URL safe UUID.
618 Generate and return a short URL safe UUID.
619
619
620 If the url parameter is provided, set the namespace to the provided
620 If the url parameter is provided, set the namespace to the provided
621 URL and generate a UUID.
621 URL and generate a UUID.
622
622
623 :param url to get the uuid for
623 :param url to get the uuid for
624 :truncate_to: truncate the basic 22 UUID to shorter version
624 :truncate_to: truncate the basic 22 UUID to shorter version
625
625
626 The IDs won't be universally unique any longer, but the probability of
626 The IDs won't be universally unique any longer, but the probability of
627 a collision will still be very low.
627 a collision will still be very low.
628 """
628 """
629 # Define our alphabet.
629 # Define our alphabet.
630 _ALPHABET = alphabet or "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
630 _ALPHABET = alphabet or "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
631
631
632 # If no URL is given, generate a random UUID.
632 # If no URL is given, generate a random UUID.
633 if url is None:
633 if url is None:
634 unique_id = uuid.uuid4().int
634 unique_id = uuid.uuid4().int
635 else:
635 else:
636 unique_id = uuid.uuid3(uuid.NAMESPACE_URL, url).int
636 unique_id = uuid.uuid3(uuid.NAMESPACE_URL, url).int
637
637
638 alphabet_length = len(_ALPHABET)
638 alphabet_length = len(_ALPHABET)
639 output = []
639 output = []
640 while unique_id > 0:
640 while unique_id > 0:
641 digit = unique_id % alphabet_length
641 digit = unique_id % alphabet_length
642 output.append(_ALPHABET[digit])
642 output.append(_ALPHABET[digit])
643 unique_id = int(unique_id / alphabet_length)
643 unique_id = int(unique_id / alphabet_length)
644 return "".join(output)[:truncate_to]
644 return "".join(output)[:truncate_to]
645
645
646 def get_current_rhodecode_user():
646 def get_current_rhodecode_user():
647 """
647 """
648 Get's rhodecode user from threadlocal tmpl_context variable if it's
648 Gets rhodecode user from threadlocal tmpl_context variable if it's
649 defined, else returns None.
649 defined, else returns None.
650 """
650 """
651 from pylons import tmpl_context
651 from pylons import tmpl_context
652 if hasattr(tmpl_context, 'rhodecode_user'):
652 if hasattr(tmpl_context, 'rhodecode_user'):
653 return tmpl_context.rhodecode_user
653 return tmpl_context.rhodecode_user
654
654
655 return None
655 return None
General Comments 0
You need to be logged in to leave comments. Login now