##// END OF EJS Templates
debug: skip model.settings query logging
dan -
r249:e180c09c default
parent child Browse files
Show More
@@ -1,853 +1,854 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2016 RhodeCode GmbH
3 # Copyright (C) 2011-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 Some simple helper functions
23 Some simple helper functions
24 """
24 """
25
25
26
26
27 import collections
27 import collections
28 import datetime
28 import datetime
29 import dateutil.relativedelta
29 import dateutil.relativedelta
30 import hashlib
30 import hashlib
31 import logging
31 import logging
32 import re
32 import re
33 import sys
33 import sys
34 import time
34 import time
35 import threading
35 import threading
36 import urllib
36 import urllib
37 import urlobject
37 import urlobject
38 import uuid
38 import uuid
39
39
40 import pygments.lexers
40 import pygments.lexers
41 import sqlalchemy
41 import sqlalchemy
42 import sqlalchemy.engine.url
42 import sqlalchemy.engine.url
43 import webob
43 import webob
44
44
45 import rhodecode
45 import rhodecode
46
46
47
47
48 def md5(s):
48 def md5(s):
49 return hashlib.md5(s).hexdigest()
49 return hashlib.md5(s).hexdigest()
50
50
51
51
52 def md5_safe(s):
52 def md5_safe(s):
53 return md5(safe_str(s))
53 return md5(safe_str(s))
54
54
55
55
56 def __get_lem():
56 def __get_lem():
57 """
57 """
58 Get language extension map based on what's inside pygments lexers
58 Get language extension map based on what's inside pygments lexers
59 """
59 """
60 d = collections.defaultdict(lambda: [])
60 d = collections.defaultdict(lambda: [])
61
61
62 def __clean(s):
62 def __clean(s):
63 s = s.lstrip('*')
63 s = s.lstrip('*')
64 s = s.lstrip('.')
64 s = s.lstrip('.')
65
65
66 if s.find('[') != -1:
66 if s.find('[') != -1:
67 exts = []
67 exts = []
68 start, stop = s.find('['), s.find(']')
68 start, stop = s.find('['), s.find(']')
69
69
70 for suffix in s[start + 1:stop]:
70 for suffix in s[start + 1:stop]:
71 exts.append(s[:s.find('[')] + suffix)
71 exts.append(s[:s.find('[')] + suffix)
72 return [e.lower() for e in exts]
72 return [e.lower() for e in exts]
73 else:
73 else:
74 return [s.lower()]
74 return [s.lower()]
75
75
76 for lx, t in sorted(pygments.lexers.LEXERS.items()):
76 for lx, t in sorted(pygments.lexers.LEXERS.items()):
77 m = map(__clean, t[-2])
77 m = map(__clean, t[-2])
78 if m:
78 if m:
79 m = reduce(lambda x, y: x + y, m)
79 m = reduce(lambda x, y: x + y, m)
80 for ext in m:
80 for ext in m:
81 desc = lx.replace('Lexer', '')
81 desc = lx.replace('Lexer', '')
82 d[ext].append(desc)
82 d[ext].append(desc)
83
83
84 return dict(d)
84 return dict(d)
85
85
86
86
87 def str2bool(_str):
87 def str2bool(_str):
88 """
88 """
89 returs True/False value from given string, it tries to translate the
89 returs True/False value from given string, it tries to translate the
90 string into boolean
90 string into boolean
91
91
92 :param _str: string value to translate into boolean
92 :param _str: string value to translate into boolean
93 :rtype: boolean
93 :rtype: boolean
94 :returns: boolean from given string
94 :returns: boolean from given string
95 """
95 """
96 if _str is None:
96 if _str is None:
97 return False
97 return False
98 if _str in (True, False):
98 if _str in (True, False):
99 return _str
99 return _str
100 _str = str(_str).strip().lower()
100 _str = str(_str).strip().lower()
101 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
101 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
102
102
103
103
104 def aslist(obj, sep=None, strip=True):
104 def aslist(obj, sep=None, strip=True):
105 """
105 """
106 Returns given string separated by sep as list
106 Returns given string separated by sep as list
107
107
108 :param obj:
108 :param obj:
109 :param sep:
109 :param sep:
110 :param strip:
110 :param strip:
111 """
111 """
112 if isinstance(obj, (basestring)):
112 if isinstance(obj, (basestring)):
113 lst = obj.split(sep)
113 lst = obj.split(sep)
114 if strip:
114 if strip:
115 lst = [v.strip() for v in lst]
115 lst = [v.strip() for v in lst]
116 return lst
116 return lst
117 elif isinstance(obj, (list, tuple)):
117 elif isinstance(obj, (list, tuple)):
118 return obj
118 return obj
119 elif obj is None:
119 elif obj is None:
120 return []
120 return []
121 else:
121 else:
122 return [obj]
122 return [obj]
123
123
124
124
125 def convert_line_endings(line, mode):
125 def convert_line_endings(line, mode):
126 """
126 """
127 Converts a given line "line end" accordingly to given mode
127 Converts a given line "line end" accordingly to given mode
128
128
129 Available modes are::
129 Available modes are::
130 0 - Unix
130 0 - Unix
131 1 - Mac
131 1 - Mac
132 2 - DOS
132 2 - DOS
133
133
134 :param line: given line to convert
134 :param line: given line to convert
135 :param mode: mode to convert to
135 :param mode: mode to convert to
136 :rtype: str
136 :rtype: str
137 :return: converted line according to mode
137 :return: converted line according to mode
138 """
138 """
139 if mode == 0:
139 if mode == 0:
140 line = line.replace('\r\n', '\n')
140 line = line.replace('\r\n', '\n')
141 line = line.replace('\r', '\n')
141 line = line.replace('\r', '\n')
142 elif mode == 1:
142 elif mode == 1:
143 line = line.replace('\r\n', '\r')
143 line = line.replace('\r\n', '\r')
144 line = line.replace('\n', '\r')
144 line = line.replace('\n', '\r')
145 elif mode == 2:
145 elif mode == 2:
146 line = re.sub('\r(?!\n)|(?<!\r)\n', '\r\n', line)
146 line = re.sub('\r(?!\n)|(?<!\r)\n', '\r\n', line)
147 return line
147 return line
148
148
149
149
150 def detect_mode(line, default):
150 def detect_mode(line, default):
151 """
151 """
152 Detects line break for given line, if line break couldn't be found
152 Detects line break for given line, if line break couldn't be found
153 given default value is returned
153 given default value is returned
154
154
155 :param line: str line
155 :param line: str line
156 :param default: default
156 :param default: default
157 :rtype: int
157 :rtype: int
158 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
158 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
159 """
159 """
160 if line.endswith('\r\n'):
160 if line.endswith('\r\n'):
161 return 2
161 return 2
162 elif line.endswith('\n'):
162 elif line.endswith('\n'):
163 return 0
163 return 0
164 elif line.endswith('\r'):
164 elif line.endswith('\r'):
165 return 1
165 return 1
166 else:
166 else:
167 return default
167 return default
168
168
169
169
170 def safe_int(val, default=None):
170 def safe_int(val, default=None):
171 """
171 """
172 Returns int() of val if val is not convertable to int use default
172 Returns int() of val if val is not convertable to int use default
173 instead
173 instead
174
174
175 :param val:
175 :param val:
176 :param default:
176 :param default:
177 """
177 """
178
178
179 try:
179 try:
180 val = int(val)
180 val = int(val)
181 except (ValueError, TypeError):
181 except (ValueError, TypeError):
182 val = default
182 val = default
183
183
184 return val
184 return val
185
185
186
186
187 def safe_unicode(str_, from_encoding=None):
187 def safe_unicode(str_, from_encoding=None):
188 """
188 """
189 safe unicode function. Does few trick to turn str_ into unicode
189 safe unicode function. Does few trick to turn str_ into unicode
190
190
191 In case of UnicodeDecode error, we try to return it with encoding detected
191 In case of UnicodeDecode error, we try to return it with encoding detected
192 by chardet library if it fails fallback to unicode with errors replaced
192 by chardet library if it fails fallback to unicode with errors replaced
193
193
194 :param str_: string to decode
194 :param str_: string to decode
195 :rtype: unicode
195 :rtype: unicode
196 :returns: unicode object
196 :returns: unicode object
197 """
197 """
198 if isinstance(str_, unicode):
198 if isinstance(str_, unicode):
199 return str_
199 return str_
200
200
201 if not from_encoding:
201 if not from_encoding:
202 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
202 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
203 'utf8'), sep=',')
203 'utf8'), sep=',')
204 from_encoding = DEFAULT_ENCODINGS
204 from_encoding = DEFAULT_ENCODINGS
205
205
206 if not isinstance(from_encoding, (list, tuple)):
206 if not isinstance(from_encoding, (list, tuple)):
207 from_encoding = [from_encoding]
207 from_encoding = [from_encoding]
208
208
209 try:
209 try:
210 return unicode(str_)
210 return unicode(str_)
211 except UnicodeDecodeError:
211 except UnicodeDecodeError:
212 pass
212 pass
213
213
214 for enc in from_encoding:
214 for enc in from_encoding:
215 try:
215 try:
216 return unicode(str_, enc)
216 return unicode(str_, enc)
217 except UnicodeDecodeError:
217 except UnicodeDecodeError:
218 pass
218 pass
219
219
220 try:
220 try:
221 import chardet
221 import chardet
222 encoding = chardet.detect(str_)['encoding']
222 encoding = chardet.detect(str_)['encoding']
223 if encoding is None:
223 if encoding is None:
224 raise Exception()
224 raise Exception()
225 return str_.decode(encoding)
225 return str_.decode(encoding)
226 except (ImportError, UnicodeDecodeError, Exception):
226 except (ImportError, UnicodeDecodeError, Exception):
227 return unicode(str_, from_encoding[0], 'replace')
227 return unicode(str_, from_encoding[0], 'replace')
228
228
229
229
230 def safe_str(unicode_, to_encoding=None):
230 def safe_str(unicode_, to_encoding=None):
231 """
231 """
232 safe str function. Does few trick to turn unicode_ into string
232 safe str function. Does few trick to turn unicode_ into string
233
233
234 In case of UnicodeEncodeError, we try to return it with encoding detected
234 In case of UnicodeEncodeError, we try to return it with encoding detected
235 by chardet library if it fails fallback to string with errors replaced
235 by chardet library if it fails fallback to string with errors replaced
236
236
237 :param unicode_: unicode to encode
237 :param unicode_: unicode to encode
238 :rtype: str
238 :rtype: str
239 :returns: str object
239 :returns: str object
240 """
240 """
241
241
242 # if it's not basestr cast to str
242 # if it's not basestr cast to str
243 if not isinstance(unicode_, basestring):
243 if not isinstance(unicode_, basestring):
244 return str(unicode_)
244 return str(unicode_)
245
245
246 if isinstance(unicode_, str):
246 if isinstance(unicode_, str):
247 return unicode_
247 return unicode_
248
248
249 if not to_encoding:
249 if not to_encoding:
250 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
250 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
251 'utf8'), sep=',')
251 'utf8'), sep=',')
252 to_encoding = DEFAULT_ENCODINGS
252 to_encoding = DEFAULT_ENCODINGS
253
253
254 if not isinstance(to_encoding, (list, tuple)):
254 if not isinstance(to_encoding, (list, tuple)):
255 to_encoding = [to_encoding]
255 to_encoding = [to_encoding]
256
256
257 for enc in to_encoding:
257 for enc in to_encoding:
258 try:
258 try:
259 return unicode_.encode(enc)
259 return unicode_.encode(enc)
260 except UnicodeEncodeError:
260 except UnicodeEncodeError:
261 pass
261 pass
262
262
263 try:
263 try:
264 import chardet
264 import chardet
265 encoding = chardet.detect(unicode_)['encoding']
265 encoding = chardet.detect(unicode_)['encoding']
266 if encoding is None:
266 if encoding is None:
267 raise UnicodeEncodeError()
267 raise UnicodeEncodeError()
268
268
269 return unicode_.encode(encoding)
269 return unicode_.encode(encoding)
270 except (ImportError, UnicodeEncodeError):
270 except (ImportError, UnicodeEncodeError):
271 return unicode_.encode(to_encoding[0], 'replace')
271 return unicode_.encode(to_encoding[0], 'replace')
272
272
273
273
274 def remove_suffix(s, suffix):
274 def remove_suffix(s, suffix):
275 if s.endswith(suffix):
275 if s.endswith(suffix):
276 s = s[:-1 * len(suffix)]
276 s = s[:-1 * len(suffix)]
277 return s
277 return s
278
278
279
279
280 def remove_prefix(s, prefix):
280 def remove_prefix(s, prefix):
281 if s.startswith(prefix):
281 if s.startswith(prefix):
282 s = s[len(prefix):]
282 s = s[len(prefix):]
283 return s
283 return s
284
284
285
285
286 def find_calling_context(ignore_modules=None):
286 def find_calling_context(ignore_modules=None):
287 """
287 """
288 Look through the calling stack and return the frame which called
288 Look through the calling stack and return the frame which called
289 this function and is part of core module ( ie. rhodecode.* )
289 this function and is part of core module ( ie. rhodecode.* )
290
290
291 :param ignore_modules: list of modules to ignore eg. ['rhodecode.lib']
291 :param ignore_modules: list of modules to ignore eg. ['rhodecode.lib']
292 """
292 """
293
293
294 ignore_modules = ignore_modules or []
294 ignore_modules = ignore_modules or []
295
295
296 f = sys._getframe(2)
296 f = sys._getframe(2)
297 while f.f_back is not None:
297 while f.f_back is not None:
298 name = f.f_globals.get('__name__')
298 name = f.f_globals.get('__name__')
299 if name and name.startswith(__name__.split('.')[0]):
299 if name and name.startswith(__name__.split('.')[0]):
300 if name not in ignore_modules:
300 if name not in ignore_modules:
301 return f
301 return f
302 f = f.f_back
302 f = f.f_back
303 return None
303 return None
304
304
305
305
306 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
306 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
307 """Custom engine_from_config functions."""
307 """Custom engine_from_config functions."""
308 log = logging.getLogger('sqlalchemy.engine')
308 log = logging.getLogger('sqlalchemy.engine')
309 engine = sqlalchemy.engine_from_config(configuration, prefix, **kwargs)
309 engine = sqlalchemy.engine_from_config(configuration, prefix, **kwargs)
310
310
311 def color_sql(sql):
311 def color_sql(sql):
312 color_seq = '\033[1;33m' # This is yellow: code 33
312 color_seq = '\033[1;33m' # This is yellow: code 33
313 normal = '\x1b[0m'
313 normal = '\x1b[0m'
314 return ''.join([color_seq, sql, normal])
314 return ''.join([color_seq, sql, normal])
315
315
316 if configuration['debug']:
316 if configuration['debug']:
317 # attach events only for debug configuration
317 # attach events only for debug configuration
318
318
319 def before_cursor_execute(conn, cursor, statement,
319 def before_cursor_execute(conn, cursor, statement,
320 parameters, context, executemany):
320 parameters, context, executemany):
321 setattr(conn, 'query_start_time', time.time())
321 setattr(conn, 'query_start_time', time.time())
322 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
322 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
323 calling_context = find_calling_context(ignore_modules=[
323 calling_context = find_calling_context(ignore_modules=[
324 'rhodecode.lib.caching_query'
324 'rhodecode.lib.caching_query',
325 'rhodecode.model.settings',
325 ])
326 ])
326 if calling_context:
327 if calling_context:
327 log.info(color_sql('call context %s:%s' % (
328 log.info(color_sql('call context %s:%s' % (
328 calling_context.f_code.co_filename,
329 calling_context.f_code.co_filename,
329 calling_context.f_lineno,
330 calling_context.f_lineno,
330 )))
331 )))
331
332
332 def after_cursor_execute(conn, cursor, statement,
333 def after_cursor_execute(conn, cursor, statement,
333 parameters, context, executemany):
334 parameters, context, executemany):
334 delattr(conn, 'query_start_time')
335 delattr(conn, 'query_start_time')
335
336
336 sqlalchemy.event.listen(engine, "before_cursor_execute",
337 sqlalchemy.event.listen(engine, "before_cursor_execute",
337 before_cursor_execute)
338 before_cursor_execute)
338 sqlalchemy.event.listen(engine, "after_cursor_execute",
339 sqlalchemy.event.listen(engine, "after_cursor_execute",
339 after_cursor_execute)
340 after_cursor_execute)
340
341
341 return engine
342 return engine
342
343
343
344
344 def age(prevdate, now=None, show_short_version=False, show_suffix=True,
345 def age(prevdate, now=None, show_short_version=False, show_suffix=True,
345 short_format=False):
346 short_format=False):
346 """
347 """
347 Turns a datetime into an age string.
348 Turns a datetime into an age string.
348 If show_short_version is True, this generates a shorter string with
349 If show_short_version is True, this generates a shorter string with
349 an approximate age; ex. '1 day ago', rather than '1 day and 23 hours ago'.
350 an approximate age; ex. '1 day ago', rather than '1 day and 23 hours ago'.
350
351
351 * IMPORTANT*
352 * IMPORTANT*
352 Code of this function is written in special way so it's easier to
353 Code of this function is written in special way so it's easier to
353 backport it to javascript. If you mean to update it, please also update
354 backport it to javascript. If you mean to update it, please also update
354 `jquery.timeago-extension.js` file
355 `jquery.timeago-extension.js` file
355
356
356 :param prevdate: datetime object
357 :param prevdate: datetime object
357 :param now: get current time, if not define we use
358 :param now: get current time, if not define we use
358 `datetime.datetime.now()`
359 `datetime.datetime.now()`
359 :param show_short_version: if it should approximate the date and
360 :param show_short_version: if it should approximate the date and
360 return a shorter string
361 return a shorter string
361 :param show_suffix:
362 :param show_suffix:
362 :param short_format: show short format, eg 2D instead of 2 days
363 :param short_format: show short format, eg 2D instead of 2 days
363 :rtype: unicode
364 :rtype: unicode
364 :returns: unicode words describing age
365 :returns: unicode words describing age
365 """
366 """
366 from pylons.i18n.translation import _, ungettext
367 from pylons.i18n.translation import _, ungettext
367
368
368 def _get_relative_delta(now, prevdate):
369 def _get_relative_delta(now, prevdate):
369 base = dateutil.relativedelta.relativedelta(now, prevdate)
370 base = dateutil.relativedelta.relativedelta(now, prevdate)
370 return {
371 return {
371 'year': base.years,
372 'year': base.years,
372 'month': base.months,
373 'month': base.months,
373 'day': base.days,
374 'day': base.days,
374 'hour': base.hours,
375 'hour': base.hours,
375 'minute': base.minutes,
376 'minute': base.minutes,
376 'second': base.seconds,
377 'second': base.seconds,
377 }
378 }
378
379
379 def _is_leap_year(year):
380 def _is_leap_year(year):
380 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
381 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
381
382
382 def get_month(prevdate):
383 def get_month(prevdate):
383 return prevdate.month
384 return prevdate.month
384
385
385 def get_year(prevdate):
386 def get_year(prevdate):
386 return prevdate.year
387 return prevdate.year
387
388
388 now = now or datetime.datetime.now()
389 now = now or datetime.datetime.now()
389 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
390 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
390 deltas = {}
391 deltas = {}
391 future = False
392 future = False
392
393
393 if prevdate > now:
394 if prevdate > now:
394 now_old = now
395 now_old = now
395 now = prevdate
396 now = prevdate
396 prevdate = now_old
397 prevdate = now_old
397 future = True
398 future = True
398 if future:
399 if future:
399 prevdate = prevdate.replace(microsecond=0)
400 prevdate = prevdate.replace(microsecond=0)
400 # Get date parts deltas
401 # Get date parts deltas
401 for part in order:
402 for part in order:
402 rel_delta = _get_relative_delta(now, prevdate)
403 rel_delta = _get_relative_delta(now, prevdate)
403 deltas[part] = rel_delta[part]
404 deltas[part] = rel_delta[part]
404
405
405 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
406 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
406 # not 1 hour, -59 minutes and -59 seconds)
407 # not 1 hour, -59 minutes and -59 seconds)
407 offsets = [[5, 60], [4, 60], [3, 24]]
408 offsets = [[5, 60], [4, 60], [3, 24]]
408 for element in offsets: # seconds, minutes, hours
409 for element in offsets: # seconds, minutes, hours
409 num = element[0]
410 num = element[0]
410 length = element[1]
411 length = element[1]
411
412
412 part = order[num]
413 part = order[num]
413 carry_part = order[num - 1]
414 carry_part = order[num - 1]
414
415
415 if deltas[part] < 0:
416 if deltas[part] < 0:
416 deltas[part] += length
417 deltas[part] += length
417 deltas[carry_part] -= 1
418 deltas[carry_part] -= 1
418
419
419 # Same thing for days except that the increment depends on the (variable)
420 # Same thing for days except that the increment depends on the (variable)
420 # number of days in the month
421 # number of days in the month
421 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
422 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
422 if deltas['day'] < 0:
423 if deltas['day'] < 0:
423 if get_month(prevdate) == 2 and _is_leap_year(get_year(prevdate)):
424 if get_month(prevdate) == 2 and _is_leap_year(get_year(prevdate)):
424 deltas['day'] += 29
425 deltas['day'] += 29
425 else:
426 else:
426 deltas['day'] += month_lengths[get_month(prevdate) - 1]
427 deltas['day'] += month_lengths[get_month(prevdate) - 1]
427
428
428 deltas['month'] -= 1
429 deltas['month'] -= 1
429
430
430 if deltas['month'] < 0:
431 if deltas['month'] < 0:
431 deltas['month'] += 12
432 deltas['month'] += 12
432 deltas['year'] -= 1
433 deltas['year'] -= 1
433
434
434 # Format the result
435 # Format the result
435 if short_format:
436 if short_format:
436 fmt_funcs = {
437 fmt_funcs = {
437 'year': lambda d: u'%dy' % d,
438 'year': lambda d: u'%dy' % d,
438 'month': lambda d: u'%dm' % d,
439 'month': lambda d: u'%dm' % d,
439 'day': lambda d: u'%dd' % d,
440 'day': lambda d: u'%dd' % d,
440 'hour': lambda d: u'%dh' % d,
441 'hour': lambda d: u'%dh' % d,
441 'minute': lambda d: u'%dmin' % d,
442 'minute': lambda d: u'%dmin' % d,
442 'second': lambda d: u'%dsec' % d,
443 'second': lambda d: u'%dsec' % d,
443 }
444 }
444 else:
445 else:
445 fmt_funcs = {
446 fmt_funcs = {
446 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
447 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
447 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
448 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
448 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
449 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
449 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
450 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
450 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
451 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
451 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
452 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
452 }
453 }
453
454
454 i = 0
455 i = 0
455 for part in order:
456 for part in order:
456 value = deltas[part]
457 value = deltas[part]
457 if value != 0:
458 if value != 0:
458
459
459 if i < 5:
460 if i < 5:
460 sub_part = order[i + 1]
461 sub_part = order[i + 1]
461 sub_value = deltas[sub_part]
462 sub_value = deltas[sub_part]
462 else:
463 else:
463 sub_value = 0
464 sub_value = 0
464
465
465 if sub_value == 0 or show_short_version:
466 if sub_value == 0 or show_short_version:
466 _val = fmt_funcs[part](value)
467 _val = fmt_funcs[part](value)
467 if future:
468 if future:
468 if show_suffix:
469 if show_suffix:
469 return _(u'in %s') % _val
470 return _(u'in %s') % _val
470 else:
471 else:
471 return _val
472 return _val
472
473
473 else:
474 else:
474 if show_suffix:
475 if show_suffix:
475 return _(u'%s ago') % _val
476 return _(u'%s ago') % _val
476 else:
477 else:
477 return _val
478 return _val
478
479
479 val = fmt_funcs[part](value)
480 val = fmt_funcs[part](value)
480 val_detail = fmt_funcs[sub_part](sub_value)
481 val_detail = fmt_funcs[sub_part](sub_value)
481
482
482 if short_format:
483 if short_format:
483 datetime_tmpl = u'%s, %s'
484 datetime_tmpl = u'%s, %s'
484 if show_suffix:
485 if show_suffix:
485 datetime_tmpl = _(u'%s, %s ago')
486 datetime_tmpl = _(u'%s, %s ago')
486 if future:
487 if future:
487 datetime_tmpl = _(u'in %s, %s')
488 datetime_tmpl = _(u'in %s, %s')
488 else:
489 else:
489 datetime_tmpl = _(u'%s and %s')
490 datetime_tmpl = _(u'%s and %s')
490 if show_suffix:
491 if show_suffix:
491 datetime_tmpl = _(u'%s and %s ago')
492 datetime_tmpl = _(u'%s and %s ago')
492 if future:
493 if future:
493 datetime_tmpl = _(u'in %s and %s')
494 datetime_tmpl = _(u'in %s and %s')
494
495
495 return datetime_tmpl % (val, val_detail)
496 return datetime_tmpl % (val, val_detail)
496 i += 1
497 i += 1
497 return _(u'just now')
498 return _(u'just now')
498
499
499
500
500 def uri_filter(uri):
501 def uri_filter(uri):
501 """
502 """
502 Removes user:password from given url string
503 Removes user:password from given url string
503
504
504 :param uri:
505 :param uri:
505 :rtype: unicode
506 :rtype: unicode
506 :returns: filtered list of strings
507 :returns: filtered list of strings
507 """
508 """
508 if not uri:
509 if not uri:
509 return ''
510 return ''
510
511
511 proto = ''
512 proto = ''
512
513
513 for pat in ('https://', 'http://'):
514 for pat in ('https://', 'http://'):
514 if uri.startswith(pat):
515 if uri.startswith(pat):
515 uri = uri[len(pat):]
516 uri = uri[len(pat):]
516 proto = pat
517 proto = pat
517 break
518 break
518
519
519 # remove passwords and username
520 # remove passwords and username
520 uri = uri[uri.find('@') + 1:]
521 uri = uri[uri.find('@') + 1:]
521
522
522 # get the port
523 # get the port
523 cred_pos = uri.find(':')
524 cred_pos = uri.find(':')
524 if cred_pos == -1:
525 if cred_pos == -1:
525 host, port = uri, None
526 host, port = uri, None
526 else:
527 else:
527 host, port = uri[:cred_pos], uri[cred_pos + 1:]
528 host, port = uri[:cred_pos], uri[cred_pos + 1:]
528
529
529 return filter(None, [proto, host, port])
530 return filter(None, [proto, host, port])
530
531
531
532
532 def credentials_filter(uri):
533 def credentials_filter(uri):
533 """
534 """
534 Returns a url with removed credentials
535 Returns a url with removed credentials
535
536
536 :param uri:
537 :param uri:
537 """
538 """
538
539
539 uri = uri_filter(uri)
540 uri = uri_filter(uri)
540 # check if we have port
541 # check if we have port
541 if len(uri) > 2 and uri[2]:
542 if len(uri) > 2 and uri[2]:
542 uri[2] = ':' + uri[2]
543 uri[2] = ':' + uri[2]
543
544
544 return ''.join(uri)
545 return ''.join(uri)
545
546
546
547
547 def get_clone_url(uri_tmpl, qualifed_home_url, repo_name, repo_id, **override):
548 def get_clone_url(uri_tmpl, qualifed_home_url, repo_name, repo_id, **override):
548 parsed_url = urlobject.URLObject(qualifed_home_url)
549 parsed_url = urlobject.URLObject(qualifed_home_url)
549 decoded_path = safe_unicode(urllib.unquote(parsed_url.path.rstrip('/')))
550 decoded_path = safe_unicode(urllib.unquote(parsed_url.path.rstrip('/')))
550 args = {
551 args = {
551 'scheme': parsed_url.scheme,
552 'scheme': parsed_url.scheme,
552 'user': '',
553 'user': '',
553 # path if we use proxy-prefix
554 # path if we use proxy-prefix
554 'netloc': parsed_url.netloc+decoded_path,
555 'netloc': parsed_url.netloc+decoded_path,
555 'prefix': decoded_path,
556 'prefix': decoded_path,
556 'repo': repo_name,
557 'repo': repo_name,
557 'repoid': str(repo_id)
558 'repoid': str(repo_id)
558 }
559 }
559 args.update(override)
560 args.update(override)
560 args['user'] = urllib.quote(safe_str(args['user']))
561 args['user'] = urllib.quote(safe_str(args['user']))
561
562
562 for k, v in args.items():
563 for k, v in args.items():
563 uri_tmpl = uri_tmpl.replace('{%s}' % k, v)
564 uri_tmpl = uri_tmpl.replace('{%s}' % k, v)
564
565
565 # remove leading @ sign if it's present. Case of empty user
566 # remove leading @ sign if it's present. Case of empty user
566 url_obj = urlobject.URLObject(uri_tmpl)
567 url_obj = urlobject.URLObject(uri_tmpl)
567 url = url_obj.with_netloc(url_obj.netloc.lstrip('@'))
568 url = url_obj.with_netloc(url_obj.netloc.lstrip('@'))
568
569
569 return safe_unicode(url)
570 return safe_unicode(url)
570
571
571
572
572 def get_commit_safe(repo, commit_id=None, commit_idx=None, pre_load=None):
573 def get_commit_safe(repo, commit_id=None, commit_idx=None, pre_load=None):
573 """
574 """
574 Safe version of get_commit if this commit doesn't exists for a
575 Safe version of get_commit if this commit doesn't exists for a
575 repository it returns a Dummy one instead
576 repository it returns a Dummy one instead
576
577
577 :param repo: repository instance
578 :param repo: repository instance
578 :param commit_id: commit id as str
579 :param commit_id: commit id as str
579 :param pre_load: optional list of commit attributes to load
580 :param pre_load: optional list of commit attributes to load
580 """
581 """
581 # TODO(skreft): remove these circular imports
582 # TODO(skreft): remove these circular imports
582 from rhodecode.lib.vcs.backends.base import BaseRepository, EmptyCommit
583 from rhodecode.lib.vcs.backends.base import BaseRepository, EmptyCommit
583 from rhodecode.lib.vcs.exceptions import RepositoryError
584 from rhodecode.lib.vcs.exceptions import RepositoryError
584 if not isinstance(repo, BaseRepository):
585 if not isinstance(repo, BaseRepository):
585 raise Exception('You must pass an Repository '
586 raise Exception('You must pass an Repository '
586 'object as first argument got %s', type(repo))
587 'object as first argument got %s', type(repo))
587
588
588 try:
589 try:
589 commit = repo.get_commit(
590 commit = repo.get_commit(
590 commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load)
591 commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load)
591 except (RepositoryError, LookupError):
592 except (RepositoryError, LookupError):
592 commit = EmptyCommit()
593 commit = EmptyCommit()
593 return commit
594 return commit
594
595
595
596
596 def datetime_to_time(dt):
597 def datetime_to_time(dt):
597 if dt:
598 if dt:
598 return time.mktime(dt.timetuple())
599 return time.mktime(dt.timetuple())
599
600
600
601
601 def time_to_datetime(tm):
602 def time_to_datetime(tm):
602 if tm:
603 if tm:
603 if isinstance(tm, basestring):
604 if isinstance(tm, basestring):
604 try:
605 try:
605 tm = float(tm)
606 tm = float(tm)
606 except ValueError:
607 except ValueError:
607 return
608 return
608 return datetime.datetime.fromtimestamp(tm)
609 return datetime.datetime.fromtimestamp(tm)
609
610
610
611
611 def time_to_utcdatetime(tm):
612 def time_to_utcdatetime(tm):
612 if tm:
613 if tm:
613 if isinstance(tm, basestring):
614 if isinstance(tm, basestring):
614 try:
615 try:
615 tm = float(tm)
616 tm = float(tm)
616 except ValueError:
617 except ValueError:
617 return
618 return
618 return datetime.datetime.utcfromtimestamp(tm)
619 return datetime.datetime.utcfromtimestamp(tm)
619
620
620
621
621 MENTIONS_REGEX = re.compile(
622 MENTIONS_REGEX = re.compile(
622 # ^@ or @ without any special chars in front
623 # ^@ or @ without any special chars in front
623 r'(?:^@|[^a-zA-Z0-9\-\_\.]@)'
624 r'(?:^@|[^a-zA-Z0-9\-\_\.]@)'
624 # main body starts with letter, then can be . - _
625 # main body starts with letter, then can be . - _
625 r'([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)',
626 r'([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)',
626 re.VERBOSE | re.MULTILINE)
627 re.VERBOSE | re.MULTILINE)
627
628
628
629
629 def extract_mentioned_users(s):
630 def extract_mentioned_users(s):
630 """
631 """
631 Returns unique usernames from given string s that have @mention
632 Returns unique usernames from given string s that have @mention
632
633
633 :param s: string to get mentions
634 :param s: string to get mentions
634 """
635 """
635 usrs = set()
636 usrs = set()
636 for username in MENTIONS_REGEX.findall(s):
637 for username in MENTIONS_REGEX.findall(s):
637 usrs.add(username)
638 usrs.add(username)
638
639
639 return sorted(list(usrs), key=lambda k: k.lower())
640 return sorted(list(usrs), key=lambda k: k.lower())
640
641
641
642
642 class AttributeDict(dict):
643 class AttributeDict(dict):
643 def __getattr__(self, attr):
644 def __getattr__(self, attr):
644 return self.get(attr, None)
645 return self.get(attr, None)
645 __setattr__ = dict.__setitem__
646 __setattr__ = dict.__setitem__
646 __delattr__ = dict.__delitem__
647 __delattr__ = dict.__delitem__
647
648
648
649
649 def fix_PATH(os_=None):
650 def fix_PATH(os_=None):
650 """
651 """
651 Get current active python path, and append it to PATH variable to fix
652 Get current active python path, and append it to PATH variable to fix
652 issues of subprocess calls and different python versions
653 issues of subprocess calls and different python versions
653 """
654 """
654 if os_ is None:
655 if os_ is None:
655 import os
656 import os
656 else:
657 else:
657 os = os_
658 os = os_
658
659
659 cur_path = os.path.split(sys.executable)[0]
660 cur_path = os.path.split(sys.executable)[0]
660 if not os.environ['PATH'].startswith(cur_path):
661 if not os.environ['PATH'].startswith(cur_path):
661 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
662 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
662
663
663
664
664 def obfuscate_url_pw(engine):
665 def obfuscate_url_pw(engine):
665 _url = engine or ''
666 _url = engine or ''
666 try:
667 try:
667 _url = sqlalchemy.engine.url.make_url(engine)
668 _url = sqlalchemy.engine.url.make_url(engine)
668 if _url.password:
669 if _url.password:
669 _url.password = 'XXXXX'
670 _url.password = 'XXXXX'
670 except Exception:
671 except Exception:
671 pass
672 pass
672 return unicode(_url)
673 return unicode(_url)
673
674
674
675
675 def get_server_url(environ):
676 def get_server_url(environ):
676 req = webob.Request(environ)
677 req = webob.Request(environ)
677 return req.host_url + req.script_name
678 return req.host_url + req.script_name
678
679
679
680
680 def unique_id(hexlen=32):
681 def unique_id(hexlen=32):
681 alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz"
682 alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz"
682 return suuid(truncate_to=hexlen, alphabet=alphabet)
683 return suuid(truncate_to=hexlen, alphabet=alphabet)
683
684
684
685
685 def suuid(url=None, truncate_to=22, alphabet=None):
686 def suuid(url=None, truncate_to=22, alphabet=None):
686 """
687 """
687 Generate and return a short URL safe UUID.
688 Generate and return a short URL safe UUID.
688
689
689 If the url parameter is provided, set the namespace to the provided
690 If the url parameter is provided, set the namespace to the provided
690 URL and generate a UUID.
691 URL and generate a UUID.
691
692
692 :param url to get the uuid for
693 :param url to get the uuid for
693 :truncate_to: truncate the basic 22 UUID to shorter version
694 :truncate_to: truncate the basic 22 UUID to shorter version
694
695
695 The IDs won't be universally unique any longer, but the probability of
696 The IDs won't be universally unique any longer, but the probability of
696 a collision will still be very low.
697 a collision will still be very low.
697 """
698 """
698 # Define our alphabet.
699 # Define our alphabet.
699 _ALPHABET = alphabet or "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
700 _ALPHABET = alphabet or "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
700
701
701 # If no URL is given, generate a random UUID.
702 # If no URL is given, generate a random UUID.
702 if url is None:
703 if url is None:
703 unique_id = uuid.uuid4().int
704 unique_id = uuid.uuid4().int
704 else:
705 else:
705 unique_id = uuid.uuid3(uuid.NAMESPACE_URL, url).int
706 unique_id = uuid.uuid3(uuid.NAMESPACE_URL, url).int
706
707
707 alphabet_length = len(_ALPHABET)
708 alphabet_length = len(_ALPHABET)
708 output = []
709 output = []
709 while unique_id > 0:
710 while unique_id > 0:
710 digit = unique_id % alphabet_length
711 digit = unique_id % alphabet_length
711 output.append(_ALPHABET[digit])
712 output.append(_ALPHABET[digit])
712 unique_id = int(unique_id / alphabet_length)
713 unique_id = int(unique_id / alphabet_length)
713 return "".join(output)[:truncate_to]
714 return "".join(output)[:truncate_to]
714
715
715
716
716 def get_current_rhodecode_user():
717 def get_current_rhodecode_user():
717 """
718 """
718 Gets rhodecode user from threadlocal tmpl_context variable if it's
719 Gets rhodecode user from threadlocal tmpl_context variable if it's
719 defined, else returns None.
720 defined, else returns None.
720 """
721 """
721 from pylons import tmpl_context as c
722 from pylons import tmpl_context as c
722 if hasattr(c, 'rhodecode_user'):
723 if hasattr(c, 'rhodecode_user'):
723 return c.rhodecode_user
724 return c.rhodecode_user
724
725
725 return None
726 return None
726
727
727
728
728 def action_logger_generic(action, namespace=''):
729 def action_logger_generic(action, namespace=''):
729 """
730 """
730 A generic logger for actions useful to the system overview, tries to find
731 A generic logger for actions useful to the system overview, tries to find
731 an acting user for the context of the call otherwise reports unknown user
732 an acting user for the context of the call otherwise reports unknown user
732
733
733 :param action: logging message eg 'comment 5 deleted'
734 :param action: logging message eg 'comment 5 deleted'
734 :param type: string
735 :param type: string
735
736
736 :param namespace: namespace of the logging message eg. 'repo.comments'
737 :param namespace: namespace of the logging message eg. 'repo.comments'
737 :param type: string
738 :param type: string
738
739
739 """
740 """
740
741
741 logger_name = 'rhodecode.actions'
742 logger_name = 'rhodecode.actions'
742
743
743 if namespace:
744 if namespace:
744 logger_name += '.' + namespace
745 logger_name += '.' + namespace
745
746
746 log = logging.getLogger(logger_name)
747 log = logging.getLogger(logger_name)
747
748
748 # get a user if we can
749 # get a user if we can
749 user = get_current_rhodecode_user()
750 user = get_current_rhodecode_user()
750
751
751 logfunc = log.info
752 logfunc = log.info
752
753
753 if not user:
754 if not user:
754 user = '<unknown user>'
755 user = '<unknown user>'
755 logfunc = log.warning
756 logfunc = log.warning
756
757
757 logfunc('Logging action by {}: {}'.format(user, action))
758 logfunc('Logging action by {}: {}'.format(user, action))
758
759
759
760
760 def escape_split(text, sep=',', maxsplit=-1):
761 def escape_split(text, sep=',', maxsplit=-1):
761 r"""
762 r"""
762 Allows for escaping of the separator: e.g. arg='foo\, bar'
763 Allows for escaping of the separator: e.g. arg='foo\, bar'
763
764
764 It should be noted that the way bash et. al. do command line parsing, those
765 It should be noted that the way bash et. al. do command line parsing, those
765 single quotes are required.
766 single quotes are required.
766 """
767 """
767 escaped_sep = r'\%s' % sep
768 escaped_sep = r'\%s' % sep
768
769
769 if escaped_sep not in text:
770 if escaped_sep not in text:
770 return text.split(sep, maxsplit)
771 return text.split(sep, maxsplit)
771
772
772 before, _mid, after = text.partition(escaped_sep)
773 before, _mid, after = text.partition(escaped_sep)
773 startlist = before.split(sep, maxsplit) # a regular split is fine here
774 startlist = before.split(sep, maxsplit) # a regular split is fine here
774 unfinished = startlist[-1]
775 unfinished = startlist[-1]
775 startlist = startlist[:-1]
776 startlist = startlist[:-1]
776
777
777 # recurse because there may be more escaped separators
778 # recurse because there may be more escaped separators
778 endlist = escape_split(after, sep, maxsplit)
779 endlist = escape_split(after, sep, maxsplit)
779
780
780 # finish building the escaped value. we use endlist[0] becaue the first
781 # finish building the escaped value. we use endlist[0] becaue the first
781 # part of the string sent in recursion is the rest of the escaped value.
782 # part of the string sent in recursion is the rest of the escaped value.
782 unfinished += sep + endlist[0]
783 unfinished += sep + endlist[0]
783
784
784 return startlist + [unfinished] + endlist[1:] # put together all the parts
785 return startlist + [unfinished] + endlist[1:] # put together all the parts
785
786
786
787
787 class OptionalAttr(object):
788 class OptionalAttr(object):
788 """
789 """
789 Special Optional Option that defines other attribute. Example::
790 Special Optional Option that defines other attribute. Example::
790
791
791 def test(apiuser, userid=Optional(OAttr('apiuser')):
792 def test(apiuser, userid=Optional(OAttr('apiuser')):
792 user = Optional.extract(userid)
793 user = Optional.extract(userid)
793 # calls
794 # calls
794
795
795 """
796 """
796
797
797 def __init__(self, attr_name):
798 def __init__(self, attr_name):
798 self.attr_name = attr_name
799 self.attr_name = attr_name
799
800
800 def __repr__(self):
801 def __repr__(self):
801 return '<OptionalAttr:%s>' % self.attr_name
802 return '<OptionalAttr:%s>' % self.attr_name
802
803
803 def __call__(self):
804 def __call__(self):
804 return self
805 return self
805
806
806
807
807 # alias
808 # alias
808 OAttr = OptionalAttr
809 OAttr = OptionalAttr
809
810
810
811
811 class Optional(object):
812 class Optional(object):
812 """
813 """
813 Defines an optional parameter::
814 Defines an optional parameter::
814
815
815 param = param.getval() if isinstance(param, Optional) else param
816 param = param.getval() if isinstance(param, Optional) else param
816 param = param() if isinstance(param, Optional) else param
817 param = param() if isinstance(param, Optional) else param
817
818
818 is equivalent of::
819 is equivalent of::
819
820
820 param = Optional.extract(param)
821 param = Optional.extract(param)
821
822
822 """
823 """
823
824
824 def __init__(self, type_):
825 def __init__(self, type_):
825 self.type_ = type_
826 self.type_ = type_
826
827
827 def __repr__(self):
828 def __repr__(self):
828 return '<Optional:%s>' % self.type_.__repr__()
829 return '<Optional:%s>' % self.type_.__repr__()
829
830
830 def __call__(self):
831 def __call__(self):
831 return self.getval()
832 return self.getval()
832
833
833 def getval(self):
834 def getval(self):
834 """
835 """
835 returns value from this Optional instance
836 returns value from this Optional instance
836 """
837 """
837 if isinstance(self.type_, OAttr):
838 if isinstance(self.type_, OAttr):
838 # use params name
839 # use params name
839 return self.type_.attr_name
840 return self.type_.attr_name
840 return self.type_
841 return self.type_
841
842
842 @classmethod
843 @classmethod
843 def extract(cls, val):
844 def extract(cls, val):
844 """
845 """
845 Extracts value from Optional() instance
846 Extracts value from Optional() instance
846
847
847 :param val:
848 :param val:
848 :return: original value if it's not Optional instance else
849 :return: original value if it's not Optional instance else
849 value of instance
850 value of instance
850 """
851 """
851 if isinstance(val, cls):
852 if isinstance(val, cls):
852 return val.getval()
853 return val.getval()
853 return val
854 return val
General Comments 0
You need to be logged in to leave comments. Login now