##// END OF EJS Templates
fixed AGE funtion for future dates, testa are now cleaner with dateutil usage
marcink -
r3261:54a439ed beta
parent child Browse files
Show More
@@ -1,569 +1,573 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 re
26 import re
27 import time
27 import time
28 import datetime
28 import datetime
29 import webob
29 import webob
30
30
31 from pylons.i18n.translation import _, ungettext
31 from pylons.i18n.translation import _, ungettext
32 from rhodecode.lib.vcs.utils.lazy import LazyProperty
32 from rhodecode.lib.vcs.utils.lazy import LazyProperty
33
33
34
34
35 def __get_lem():
35 def __get_lem():
36 """
36 """
37 Get language extension map based on what's inside pygments lexers
37 Get language extension map based on what's inside pygments lexers
38 """
38 """
39 from pygments import lexers
39 from pygments import lexers
40 from string import lower
40 from string import lower
41 from collections import defaultdict
41 from collections import defaultdict
42
42
43 d = defaultdict(lambda: [])
43 d = defaultdict(lambda: [])
44
44
45 def __clean(s):
45 def __clean(s):
46 s = s.lstrip('*')
46 s = s.lstrip('*')
47 s = s.lstrip('.')
47 s = s.lstrip('.')
48
48
49 if s.find('[') != -1:
49 if s.find('[') != -1:
50 exts = []
50 exts = []
51 start, stop = s.find('['), s.find(']')
51 start, stop = s.find('['), s.find(']')
52
52
53 for suffix in s[start + 1:stop]:
53 for suffix in s[start + 1:stop]:
54 exts.append(s[:s.find('[')] + suffix)
54 exts.append(s[:s.find('[')] + suffix)
55 return map(lower, exts)
55 return map(lower, exts)
56 else:
56 else:
57 return map(lower, [s])
57 return map(lower, [s])
58
58
59 for lx, t in sorted(lexers.LEXERS.items()):
59 for lx, t in sorted(lexers.LEXERS.items()):
60 m = map(__clean, t[-2])
60 m = map(__clean, t[-2])
61 if m:
61 if m:
62 m = reduce(lambda x, y: x + y, m)
62 m = reduce(lambda x, y: x + y, m)
63 for ext in m:
63 for ext in m:
64 desc = lx.replace('Lexer', '')
64 desc = lx.replace('Lexer', '')
65 d[ext].append(desc)
65 d[ext].append(desc)
66
66
67 return dict(d)
67 return dict(d)
68
68
69
69
70 def str2bool(_str):
70 def str2bool(_str):
71 """
71 """
72 returs True/False value from given string, it tries to translate the
72 returs True/False value from given string, it tries to translate the
73 string into boolean
73 string into boolean
74
74
75 :param _str: string value to translate into boolean
75 :param _str: string value to translate into boolean
76 :rtype: boolean
76 :rtype: boolean
77 :returns: boolean from given string
77 :returns: boolean from given string
78 """
78 """
79 if _str is None:
79 if _str is None:
80 return False
80 return False
81 if _str in (True, False):
81 if _str in (True, False):
82 return _str
82 return _str
83 _str = str(_str).strip().lower()
83 _str = str(_str).strip().lower()
84 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
84 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
85
85
86
86
87 def aslist(obj, sep=None, strip=True):
87 def aslist(obj, sep=None, strip=True):
88 """
88 """
89 Returns given string separated by sep as list
89 Returns given string separated by sep as list
90
90
91 :param obj:
91 :param obj:
92 :param sep:
92 :param sep:
93 :param strip:
93 :param strip:
94 """
94 """
95 if isinstance(obj, (basestring)):
95 if isinstance(obj, (basestring)):
96 lst = obj.split(sep)
96 lst = obj.split(sep)
97 if strip:
97 if strip:
98 lst = [v.strip() for v in lst]
98 lst = [v.strip() for v in lst]
99 return lst
99 return lst
100 elif isinstance(obj, (list, tuple)):
100 elif isinstance(obj, (list, tuple)):
101 return obj
101 return obj
102 elif obj is None:
102 elif obj is None:
103 return []
103 return []
104 else:
104 else:
105 return [obj]
105 return [obj]
106
106
107
107
108 def convert_line_endings(line, mode):
108 def convert_line_endings(line, mode):
109 """
109 """
110 Converts a given line "line end" accordingly to given mode
110 Converts a given line "line end" accordingly to given mode
111
111
112 Available modes are::
112 Available modes are::
113 0 - Unix
113 0 - Unix
114 1 - Mac
114 1 - Mac
115 2 - DOS
115 2 - DOS
116
116
117 :param line: given line to convert
117 :param line: given line to convert
118 :param mode: mode to convert to
118 :param mode: mode to convert to
119 :rtype: str
119 :rtype: str
120 :return: converted line according to mode
120 :return: converted line according to mode
121 """
121 """
122 from string import replace
122 from string import replace
123
123
124 if mode == 0:
124 if mode == 0:
125 line = replace(line, '\r\n', '\n')
125 line = replace(line, '\r\n', '\n')
126 line = replace(line, '\r', '\n')
126 line = replace(line, '\r', '\n')
127 elif mode == 1:
127 elif mode == 1:
128 line = replace(line, '\r\n', '\r')
128 line = replace(line, '\r\n', '\r')
129 line = replace(line, '\n', '\r')
129 line = replace(line, '\n', '\r')
130 elif mode == 2:
130 elif mode == 2:
131 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
131 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
132 return line
132 return line
133
133
134
134
135 def detect_mode(line, default):
135 def detect_mode(line, default):
136 """
136 """
137 Detects line break for given line, if line break couldn't be found
137 Detects line break for given line, if line break couldn't be found
138 given default value is returned
138 given default value is returned
139
139
140 :param line: str line
140 :param line: str line
141 :param default: default
141 :param default: default
142 :rtype: int
142 :rtype: int
143 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
143 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
144 """
144 """
145 if line.endswith('\r\n'):
145 if line.endswith('\r\n'):
146 return 2
146 return 2
147 elif line.endswith('\n'):
147 elif line.endswith('\n'):
148 return 0
148 return 0
149 elif line.endswith('\r'):
149 elif line.endswith('\r'):
150 return 1
150 return 1
151 else:
151 else:
152 return default
152 return default
153
153
154
154
155 def generate_api_key(username, salt=None):
155 def generate_api_key(username, salt=None):
156 """
156 """
157 Generates unique API key for given username, if salt is not given
157 Generates unique API key for given username, if salt is not given
158 it'll be generated from some random string
158 it'll be generated from some random string
159
159
160 :param username: username as string
160 :param username: username as string
161 :param salt: salt to hash generate KEY
161 :param salt: salt to hash generate KEY
162 :rtype: str
162 :rtype: str
163 :returns: sha1 hash from username+salt
163 :returns: sha1 hash from username+salt
164 """
164 """
165 from tempfile import _RandomNameSequence
165 from tempfile import _RandomNameSequence
166 import hashlib
166 import hashlib
167
167
168 if salt is None:
168 if salt is None:
169 salt = _RandomNameSequence().next()
169 salt = _RandomNameSequence().next()
170
170
171 return hashlib.sha1(username + salt).hexdigest()
171 return hashlib.sha1(username + salt).hexdigest()
172
172
173
173
174 def safe_int(val, default=None):
174 def safe_int(val, default=None):
175 """
175 """
176 Returns int() of val if val is not convertable to int use default
176 Returns int() of val if val is not convertable to int use default
177 instead
177 instead
178
178
179 :param val:
179 :param val:
180 :param default:
180 :param default:
181 """
181 """
182
182
183 try:
183 try:
184 val = int(val)
184 val = int(val)
185 except (ValueError, TypeError):
185 except (ValueError, TypeError):
186 val = default
186 val = default
187
187
188 return val
188 return val
189
189
190
190
191 def safe_unicode(str_, from_encoding=None):
191 def safe_unicode(str_, from_encoding=None):
192 """
192 """
193 safe unicode function. Does few trick to turn str_ into unicode
193 safe unicode function. Does few trick to turn str_ into unicode
194
194
195 In case of UnicodeDecode error we try to return it with encoding detected
195 In case of UnicodeDecode error we try to return it with encoding detected
196 by chardet library if it fails fallback to unicode with errors replaced
196 by chardet library if it fails fallback to unicode with errors replaced
197
197
198 :param str_: string to decode
198 :param str_: string to decode
199 :rtype: unicode
199 :rtype: unicode
200 :returns: unicode object
200 :returns: unicode object
201 """
201 """
202 if isinstance(str_, unicode):
202 if isinstance(str_, unicode):
203 return str_
203 return str_
204
204
205 if not from_encoding:
205 if not from_encoding:
206 import rhodecode
206 import rhodecode
207 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
207 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
208 'utf8'), sep=',')
208 'utf8'), sep=',')
209 from_encoding = DEFAULT_ENCODINGS
209 from_encoding = DEFAULT_ENCODINGS
210
210
211 if not isinstance(from_encoding, (list, tuple)):
211 if not isinstance(from_encoding, (list, tuple)):
212 from_encoding = [from_encoding]
212 from_encoding = [from_encoding]
213
213
214 try:
214 try:
215 return unicode(str_)
215 return unicode(str_)
216 except UnicodeDecodeError:
216 except UnicodeDecodeError:
217 pass
217 pass
218
218
219 for enc in from_encoding:
219 for enc in from_encoding:
220 try:
220 try:
221 return unicode(str_, enc)
221 return unicode(str_, enc)
222 except UnicodeDecodeError:
222 except UnicodeDecodeError:
223 pass
223 pass
224
224
225 try:
225 try:
226 import chardet
226 import chardet
227 encoding = chardet.detect(str_)['encoding']
227 encoding = chardet.detect(str_)['encoding']
228 if encoding is None:
228 if encoding is None:
229 raise Exception()
229 raise Exception()
230 return str_.decode(encoding)
230 return str_.decode(encoding)
231 except (ImportError, UnicodeDecodeError, Exception):
231 except (ImportError, UnicodeDecodeError, Exception):
232 return unicode(str_, from_encoding[0], 'replace')
232 return unicode(str_, from_encoding[0], 'replace')
233
233
234
234
235 def safe_str(unicode_, to_encoding=None):
235 def safe_str(unicode_, to_encoding=None):
236 """
236 """
237 safe str function. Does few trick to turn unicode_ into string
237 safe str function. Does few trick to turn unicode_ into string
238
238
239 In case of UnicodeEncodeError we try to return it with encoding detected
239 In case of UnicodeEncodeError we try to return it with encoding detected
240 by chardet library if it fails fallback to string with errors replaced
240 by chardet library if it fails fallback to string with errors replaced
241
241
242 :param unicode_: unicode to encode
242 :param unicode_: unicode to encode
243 :rtype: str
243 :rtype: str
244 :returns: str object
244 :returns: str object
245 """
245 """
246
246
247 # if it's not basestr cast to str
247 # if it's not basestr cast to str
248 if not isinstance(unicode_, basestring):
248 if not isinstance(unicode_, basestring):
249 return str(unicode_)
249 return str(unicode_)
250
250
251 if isinstance(unicode_, str):
251 if isinstance(unicode_, str):
252 return unicode_
252 return unicode_
253
253
254 if not to_encoding:
254 if not to_encoding:
255 import rhodecode
255 import rhodecode
256 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
256 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
257 'utf8'), sep=',')
257 'utf8'), sep=',')
258 to_encoding = DEFAULT_ENCODINGS
258 to_encoding = DEFAULT_ENCODINGS
259
259
260 if not isinstance(to_encoding, (list, tuple)):
260 if not isinstance(to_encoding, (list, tuple)):
261 to_encoding = [to_encoding]
261 to_encoding = [to_encoding]
262
262
263 for enc in to_encoding:
263 for enc in to_encoding:
264 try:
264 try:
265 return unicode_.encode(enc)
265 return unicode_.encode(enc)
266 except UnicodeEncodeError:
266 except UnicodeEncodeError:
267 pass
267 pass
268
268
269 try:
269 try:
270 import chardet
270 import chardet
271 encoding = chardet.detect(unicode_)['encoding']
271 encoding = chardet.detect(unicode_)['encoding']
272 if encoding is None:
272 if encoding is None:
273 raise UnicodeEncodeError()
273 raise UnicodeEncodeError()
274
274
275 return unicode_.encode(encoding)
275 return unicode_.encode(encoding)
276 except (ImportError, UnicodeEncodeError):
276 except (ImportError, UnicodeEncodeError):
277 return unicode_.encode(to_encoding[0], 'replace')
277 return unicode_.encode(to_encoding[0], 'replace')
278
278
279 return safe_str
279 return safe_str
280
280
281
281
282 def remove_suffix(s, suffix):
282 def remove_suffix(s, suffix):
283 if s.endswith(suffix):
283 if s.endswith(suffix):
284 s = s[:-1 * len(suffix)]
284 s = s[:-1 * len(suffix)]
285 return s
285 return s
286
286
287
287
288 def remove_prefix(s, prefix):
288 def remove_prefix(s, prefix):
289 if s.startswith(prefix):
289 if s.startswith(prefix):
290 s = s[len(prefix):]
290 s = s[len(prefix):]
291 return s
291 return s
292
292
293
293
294 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
294 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
295 """
295 """
296 Custom engine_from_config functions that makes sure we use NullPool for
296 Custom engine_from_config functions that makes sure we use NullPool for
297 file based sqlite databases. This prevents errors on sqlite. This only
297 file based sqlite databases. This prevents errors on sqlite. This only
298 applies to sqlalchemy versions < 0.7.0
298 applies to sqlalchemy versions < 0.7.0
299
299
300 """
300 """
301 import sqlalchemy
301 import sqlalchemy
302 from sqlalchemy import engine_from_config as efc
302 from sqlalchemy import engine_from_config as efc
303 import logging
303 import logging
304
304
305 if int(sqlalchemy.__version__.split('.')[1]) < 7:
305 if int(sqlalchemy.__version__.split('.')[1]) < 7:
306
306
307 # This solution should work for sqlalchemy < 0.7.0, and should use
307 # This solution should work for sqlalchemy < 0.7.0, and should use
308 # proxy=TimerProxy() for execution time profiling
308 # proxy=TimerProxy() for execution time profiling
309
309
310 from sqlalchemy.pool import NullPool
310 from sqlalchemy.pool import NullPool
311 url = configuration[prefix + 'url']
311 url = configuration[prefix + 'url']
312
312
313 if url.startswith('sqlite'):
313 if url.startswith('sqlite'):
314 kwargs.update({'poolclass': NullPool})
314 kwargs.update({'poolclass': NullPool})
315 return efc(configuration, prefix, **kwargs)
315 return efc(configuration, prefix, **kwargs)
316 else:
316 else:
317 import time
317 import time
318 from sqlalchemy import event
318 from sqlalchemy import event
319 from sqlalchemy.engine import Engine
319 from sqlalchemy.engine import Engine
320
320
321 log = logging.getLogger('sqlalchemy.engine')
321 log = logging.getLogger('sqlalchemy.engine')
322 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
322 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
323 engine = efc(configuration, prefix, **kwargs)
323 engine = efc(configuration, prefix, **kwargs)
324
324
325 def color_sql(sql):
325 def color_sql(sql):
326 COLOR_SEQ = "\033[1;%dm"
326 COLOR_SEQ = "\033[1;%dm"
327 COLOR_SQL = YELLOW
327 COLOR_SQL = YELLOW
328 normal = '\x1b[0m'
328 normal = '\x1b[0m'
329 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
329 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
330
330
331 if configuration['debug']:
331 if configuration['debug']:
332 #attach events only for debug configuration
332 #attach events only for debug configuration
333
333
334 def before_cursor_execute(conn, cursor, statement,
334 def before_cursor_execute(conn, cursor, statement,
335 parameters, context, executemany):
335 parameters, context, executemany):
336 context._query_start_time = time.time()
336 context._query_start_time = time.time()
337 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
337 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
338
338
339 def after_cursor_execute(conn, cursor, statement,
339 def after_cursor_execute(conn, cursor, statement,
340 parameters, context, executemany):
340 parameters, context, executemany):
341 total = time.time() - context._query_start_time
341 total = time.time() - context._query_start_time
342 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
342 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
343
343
344 event.listen(engine, "before_cursor_execute",
344 event.listen(engine, "before_cursor_execute",
345 before_cursor_execute)
345 before_cursor_execute)
346 event.listen(engine, "after_cursor_execute",
346 event.listen(engine, "after_cursor_execute",
347 after_cursor_execute)
347 after_cursor_execute)
348
348
349 return engine
349 return engine
350
350
351
351
352 def age(prevdate):
352 def age(prevdate):
353 """
353 """
354 turns a datetime into an age string.
354 turns a datetime into an age string.
355
355
356 :param prevdate: datetime object
356 :param prevdate: datetime object
357 :rtype: unicode
357 :rtype: unicode
358 :returns: unicode words describing age
358 :returns: unicode words describing age
359 """
359 """
360
360 now = datetime.datetime.now()
361 now = now.replace(microsecond=0)
361 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
362 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
362 deltas = {}
363 deltas = {}
363 future = False
364 future = False
364
365
365 # Get date parts deltas
366 now = datetime.datetime.now()
367 if prevdate > now:
366 if prevdate > now:
368 now, prevdate = prevdate, now
367 now, prevdate = prevdate, now
369 future = True
368 future = True
370
369
370 # Get date parts deltas
371 for part in order:
371 for part in order:
372 deltas[part] = getattr(now, part) - getattr(prevdate, part)
372 if future:
373 import dateutil
374 d = dateutil.relativedelta.relativedelta(now, prevdate)
375 deltas[part] = getattr(d, part + 's')
376 else:
377 deltas[part] = getattr(now, part) - getattr(prevdate, part)
373
378
374 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
379 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
375 # not 1 hour, -59 minutes and -59 seconds)
380 # not 1 hour, -59 minutes and -59 seconds)
376
377 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
381 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
378 part = order[num]
382 part = order[num]
379 carry_part = order[num - 1]
383 carry_part = order[num - 1]
380
384
381 if deltas[part] < 0:
385 if deltas[part] < 0:
382 deltas[part] += length
386 deltas[part] += length
383 deltas[carry_part] -= 1
387 deltas[carry_part] -= 1
384
388
385 # Same thing for days except that the increment depends on the (variable)
389 # Same thing for days except that the increment depends on the (variable)
386 # number of days in the month
390 # number of days in the month
387 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
391 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
388 if deltas['day'] < 0:
392 if deltas['day'] < 0:
389 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
393 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
390 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
394 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
391 deltas['day'] += 29
395 deltas['day'] += 29
392 else:
396 else:
393 deltas['day'] += month_lengths[prevdate.month - 1]
397 deltas['day'] += month_lengths[prevdate.month - 1]
394
398
395 deltas['month'] -= 1
399 deltas['month'] -= 1
396
400
397 if deltas['month'] < 0:
401 if deltas['month'] < 0:
398 deltas['month'] += 12
402 deltas['month'] += 12
399 deltas['year'] -= 1
403 deltas['year'] -= 1
400
404
401 # Format the result
405 # Format the result
402 fmt_funcs = {
406 fmt_funcs = {
403 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
407 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
404 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
408 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
405 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
409 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
406 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
410 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
407 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
411 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
408 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
412 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
409 }
413 }
410
414
411 for i, part in enumerate(order):
415 for i, part in enumerate(order):
412 value = deltas[part]
416 value = deltas[part]
413 if value == 0:
417 if value == 0:
414 continue
418 continue
415
419
416 if i < 5:
420 if i < 5:
417 sub_part = order[i + 1]
421 sub_part = order[i + 1]
418 sub_value = deltas[sub_part]
422 sub_value = deltas[sub_part]
419 else:
423 else:
420 sub_value = 0
424 sub_value = 0
421
425
422 if sub_value == 0:
426 if sub_value == 0:
423 if future:
427 if future:
424 return _(u'in %s') % fmt_funcs[part](value)
428 return _(u'in %s') % fmt_funcs[part](value)
425 else:
429 else:
426 return _(u'%s ago') % fmt_funcs[part](value)
430 return _(u'%s ago') % fmt_funcs[part](value)
427 if future:
431 if future:
428 return _(u'in %s and %s') % (fmt_funcs[part](value),
432 return _(u'in %s and %s') % (fmt_funcs[part](value),
429 fmt_funcs[sub_part](sub_value))
433 fmt_funcs[sub_part](sub_value))
430 else:
434 else:
431 return _(u'%s and %s ago') % (fmt_funcs[part](value),
435 return _(u'%s and %s ago') % (fmt_funcs[part](value),
432 fmt_funcs[sub_part](sub_value))
436 fmt_funcs[sub_part](sub_value))
433
437
434 return _(u'just now')
438 return _(u'just now')
435
439
436
440
437 def uri_filter(uri):
441 def uri_filter(uri):
438 """
442 """
439 Removes user:password from given url string
443 Removes user:password from given url string
440
444
441 :param uri:
445 :param uri:
442 :rtype: unicode
446 :rtype: unicode
443 :returns: filtered list of strings
447 :returns: filtered list of strings
444 """
448 """
445 if not uri:
449 if not uri:
446 return ''
450 return ''
447
451
448 proto = ''
452 proto = ''
449
453
450 for pat in ('https://', 'http://'):
454 for pat in ('https://', 'http://'):
451 if uri.startswith(pat):
455 if uri.startswith(pat):
452 uri = uri[len(pat):]
456 uri = uri[len(pat):]
453 proto = pat
457 proto = pat
454 break
458 break
455
459
456 # remove passwords and username
460 # remove passwords and username
457 uri = uri[uri.find('@') + 1:]
461 uri = uri[uri.find('@') + 1:]
458
462
459 # get the port
463 # get the port
460 cred_pos = uri.find(':')
464 cred_pos = uri.find(':')
461 if cred_pos == -1:
465 if cred_pos == -1:
462 host, port = uri, None
466 host, port = uri, None
463 else:
467 else:
464 host, port = uri[:cred_pos], uri[cred_pos + 1:]
468 host, port = uri[:cred_pos], uri[cred_pos + 1:]
465
469
466 return filter(None, [proto, host, port])
470 return filter(None, [proto, host, port])
467
471
468
472
469 def credentials_filter(uri):
473 def credentials_filter(uri):
470 """
474 """
471 Returns a url with removed credentials
475 Returns a url with removed credentials
472
476
473 :param uri:
477 :param uri:
474 """
478 """
475
479
476 uri = uri_filter(uri)
480 uri = uri_filter(uri)
477 #check if we have port
481 #check if we have port
478 if len(uri) > 2 and uri[2]:
482 if len(uri) > 2 and uri[2]:
479 uri[2] = ':' + uri[2]
483 uri[2] = ':' + uri[2]
480
484
481 return ''.join(uri)
485 return ''.join(uri)
482
486
483
487
484 def get_changeset_safe(repo, rev):
488 def get_changeset_safe(repo, rev):
485 """
489 """
486 Safe version of get_changeset if this changeset doesn't exists for a
490 Safe version of get_changeset if this changeset doesn't exists for a
487 repo it returns a Dummy one instead
491 repo it returns a Dummy one instead
488
492
489 :param repo:
493 :param repo:
490 :param rev:
494 :param rev:
491 """
495 """
492 from rhodecode.lib.vcs.backends.base import BaseRepository
496 from rhodecode.lib.vcs.backends.base import BaseRepository
493 from rhodecode.lib.vcs.exceptions import RepositoryError
497 from rhodecode.lib.vcs.exceptions import RepositoryError
494 from rhodecode.lib.vcs.backends.base import EmptyChangeset
498 from rhodecode.lib.vcs.backends.base import EmptyChangeset
495 if not isinstance(repo, BaseRepository):
499 if not isinstance(repo, BaseRepository):
496 raise Exception('You must pass an Repository '
500 raise Exception('You must pass an Repository '
497 'object as first argument got %s', type(repo))
501 'object as first argument got %s', type(repo))
498
502
499 try:
503 try:
500 cs = repo.get_changeset(rev)
504 cs = repo.get_changeset(rev)
501 except RepositoryError:
505 except RepositoryError:
502 cs = EmptyChangeset(requested_revision=rev)
506 cs = EmptyChangeset(requested_revision=rev)
503 return cs
507 return cs
504
508
505
509
506 def datetime_to_time(dt):
510 def datetime_to_time(dt):
507 if dt:
511 if dt:
508 return time.mktime(dt.timetuple())
512 return time.mktime(dt.timetuple())
509
513
510
514
511 def time_to_datetime(tm):
515 def time_to_datetime(tm):
512 if tm:
516 if tm:
513 if isinstance(tm, basestring):
517 if isinstance(tm, basestring):
514 try:
518 try:
515 tm = float(tm)
519 tm = float(tm)
516 except ValueError:
520 except ValueError:
517 return
521 return
518 return datetime.datetime.fromtimestamp(tm)
522 return datetime.datetime.fromtimestamp(tm)
519
523
520 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
524 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
521
525
522
526
523 def extract_mentioned_users(s):
527 def extract_mentioned_users(s):
524 """
528 """
525 Returns unique usernames from given string s that have @mention
529 Returns unique usernames from given string s that have @mention
526
530
527 :param s: string to get mentions
531 :param s: string to get mentions
528 """
532 """
529 usrs = set()
533 usrs = set()
530 for username in re.findall(MENTIONS_REGEX, s):
534 for username in re.findall(MENTIONS_REGEX, s):
531 usrs.add(username)
535 usrs.add(username)
532
536
533 return sorted(list(usrs), key=lambda k: k.lower())
537 return sorted(list(usrs), key=lambda k: k.lower())
534
538
535
539
536 class AttributeDict(dict):
540 class AttributeDict(dict):
537 def __getattr__(self, attr):
541 def __getattr__(self, attr):
538 return self.get(attr, None)
542 return self.get(attr, None)
539 __setattr__ = dict.__setitem__
543 __setattr__ = dict.__setitem__
540 __delattr__ = dict.__delitem__
544 __delattr__ = dict.__delitem__
541
545
542
546
543 def fix_PATH(os_=None):
547 def fix_PATH(os_=None):
544 """
548 """
545 Get current active python path, and append it to PATH variable to fix issues
549 Get current active python path, and append it to PATH variable to fix issues
546 of subprocess calls and different python versions
550 of subprocess calls and different python versions
547 """
551 """
548 import sys
552 import sys
549 if os_ is None:
553 if os_ is None:
550 import os
554 import os
551 else:
555 else:
552 os = os_
556 os = os_
553
557
554 cur_path = os.path.split(sys.executable)[0]
558 cur_path = os.path.split(sys.executable)[0]
555 if not os.environ['PATH'].startswith(cur_path):
559 if not os.environ['PATH'].startswith(cur_path):
556 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
560 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
557
561
558
562
559 def obfuscate_url_pw(engine):
563 def obfuscate_url_pw(engine):
560 from sqlalchemy.engine import url
564 from sqlalchemy.engine import url
561 url = url.make_url(engine)
565 url = url.make_url(engine)
562 if url.password:
566 if url.password:
563 url.password = 'XXXXX'
567 url.password = 'XXXXX'
564 return str(url)
568 return str(url)
565
569
566
570
567 def get_server_url(environ):
571 def get_server_url(environ):
568 req = webob.Request(environ)
572 req = webob.Request(environ)
569 return req.host_url + req.script_name
573 return req.host_url + req.script_name
@@ -1,217 +1,214 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.tests.test_libs
3 rhodecode.tests.test_libs
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6
6
7 Package for testing various lib/helper functions in rhodecode
7 Package for testing various lib/helper functions in rhodecode
8
8
9 :created_on: Jun 9, 2011
9 :created_on: Jun 9, 2011
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 from __future__ import with_statement
25 from __future__ import with_statement
26 import unittest
26 import unittest
27 import datetime
27 import datetime
28 import hashlib
28 import hashlib
29 import mock
29 import mock
30 from rhodecode.tests import *
30 from rhodecode.tests import *
31
31
32 proto = 'http'
32 proto = 'http'
33 TEST_URLS = [
33 TEST_URLS = [
34 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
34 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
35 '%s://127.0.0.1' % proto),
35 '%s://127.0.0.1' % proto),
36 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
36 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
37 '%s://127.0.0.1' % proto),
37 '%s://127.0.0.1' % proto),
38 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
38 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
39 '%s://127.0.0.1' % proto),
39 '%s://127.0.0.1' % proto),
40 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
40 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
41 '%s://127.0.0.1:8080' % proto),
41 '%s://127.0.0.1:8080' % proto),
42 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
42 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
43 '%s://domain.org' % proto),
43 '%s://domain.org' % proto),
44 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
44 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
45 '8080'],
45 '8080'],
46 '%s://domain.org:8080' % proto),
46 '%s://domain.org:8080' % proto),
47 ]
47 ]
48
48
49 proto = 'https'
49 proto = 'https'
50 TEST_URLS += [
50 TEST_URLS += [
51 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
51 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
52 '%s://127.0.0.1' % proto),
52 '%s://127.0.0.1' % proto),
53 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
53 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
54 '%s://127.0.0.1' % proto),
54 '%s://127.0.0.1' % proto),
55 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
55 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
56 '%s://127.0.0.1' % proto),
56 '%s://127.0.0.1' % proto),
57 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
57 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
58 '%s://127.0.0.1:8080' % proto),
58 '%s://127.0.0.1:8080' % proto),
59 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
59 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
60 '%s://domain.org' % proto),
60 '%s://domain.org' % proto),
61 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
61 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
62 '8080'],
62 '8080'],
63 '%s://domain.org:8080' % proto),
63 '%s://domain.org:8080' % proto),
64 ]
64 ]
65
65
66
66
67 class TestLibs(unittest.TestCase):
67 class TestLibs(unittest.TestCase):
68
68
69 def test_uri_filter(self):
69 def test_uri_filter(self):
70 from rhodecode.lib.utils2 import uri_filter
70 from rhodecode.lib.utils2 import uri_filter
71
71
72 for url in TEST_URLS:
72 for url in TEST_URLS:
73 self.assertEqual(uri_filter(url[0]), url[1])
73 self.assertEqual(uri_filter(url[0]), url[1])
74
74
75 def test_credentials_filter(self):
75 def test_credentials_filter(self):
76 from rhodecode.lib.utils2 import credentials_filter
76 from rhodecode.lib.utils2 import credentials_filter
77
77
78 for url in TEST_URLS:
78 for url in TEST_URLS:
79 self.assertEqual(credentials_filter(url[0]), url[2])
79 self.assertEqual(credentials_filter(url[0]), url[2])
80
80
81 def test_str2bool(self):
81 def test_str2bool(self):
82 from rhodecode.lib.utils2 import str2bool
82 from rhodecode.lib.utils2 import str2bool
83 test_cases = [
83 test_cases = [
84 ('t', True),
84 ('t', True),
85 ('true', True),
85 ('true', True),
86 ('y', True),
86 ('y', True),
87 ('yes', True),
87 ('yes', True),
88 ('on', True),
88 ('on', True),
89 ('1', True),
89 ('1', True),
90 ('Y', True),
90 ('Y', True),
91 ('yeS', True),
91 ('yeS', True),
92 ('Y', True),
92 ('Y', True),
93 ('TRUE', True),
93 ('TRUE', True),
94 ('T', True),
94 ('T', True),
95 ('False', False),
95 ('False', False),
96 ('F', False),
96 ('F', False),
97 ('FALSE', False),
97 ('FALSE', False),
98 ('0', False),
98 ('0', False),
99 ('-1', False),
99 ('-1', False),
100 ('', False), ]
100 ('', False), ]
101
101
102 for case in test_cases:
102 for case in test_cases:
103 self.assertEqual(str2bool(case[0]), case[1])
103 self.assertEqual(str2bool(case[0]), case[1])
104
104
105 def test_mention_extractor(self):
105 def test_mention_extractor(self):
106 from rhodecode.lib.utils2 import extract_mentioned_users
106 from rhodecode.lib.utils2 import extract_mentioned_users
107 sample = (
107 sample = (
108 "@first hi there @marcink here's my email marcin@email.com "
108 "@first hi there @marcink here's my email marcin@email.com "
109 "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three "
109 "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three "
110 "@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl "
110 "@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl "
111 "@marian.user just do it @marco-polo and next extract @marco_polo "
111 "@marian.user just do it @marco-polo and next extract @marco_polo "
112 "user.dot hej ! not-needed maril@domain.org"
112 "user.dot hej ! not-needed maril@domain.org"
113 )
113 )
114
114
115 s = sorted([
115 s = sorted([
116 'first', 'marcink', 'lukaszb', 'one_more22', 'MARCIN', 'maRCiN', 'john',
116 'first', 'marcink', 'lukaszb', 'one_more22', 'MARCIN', 'maRCiN', 'john',
117 'marian.user', 'marco-polo', 'marco_polo'
117 'marian.user', 'marco-polo', 'marco_polo'
118 ], key=lambda k: k.lower())
118 ], key=lambda k: k.lower())
119 self.assertEqual(s, extract_mentioned_users(sample))
119 self.assertEqual(s, extract_mentioned_users(sample))
120
120
121 def test_age(self):
121 def test_age(self):
122 import calendar
123 from rhodecode.lib.utils2 import age
122 from rhodecode.lib.utils2 import age
123 from dateutil import relativedelta
124 n = datetime.datetime.now()
124 n = datetime.datetime.now()
125 delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
125 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
126 prev_month = n.month - 1 if n.month != 1 else n.month - 2
126
127 self.assertEqual(age(n), u'just now')
127 self.assertEqual(age(n), u'just now')
128 self.assertEqual(age(n - delt(seconds=1)), u'1 second ago')
128 self.assertEqual(age(n + delt(seconds=-1)), u'1 second ago')
129 self.assertEqual(age(n - delt(seconds=60 * 2)), u'2 minutes ago')
129 self.assertEqual(age(n + delt(seconds=-60 * 2)), u'2 minutes ago')
130 self.assertEqual(age(n - delt(hours=1)), u'1 hour ago')
130 self.assertEqual(age(n + delt(hours=-1)), u'1 hour ago')
131 self.assertEqual(age(n - delt(hours=24)), u'1 day ago')
131 self.assertEqual(age(n + delt(hours=-24)), u'1 day ago')
132 self.assertEqual(age(n - delt(hours=24 * 5)), u'5 days ago')
132 self.assertEqual(age(n + delt(hours=-24 * 5)), u'5 days ago')
133 self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[prev_month]))),
133 self.assertEqual(age(n + delt(months=-1)), u'1 month ago')
134 u'1 month ago')
134 self.assertEqual(age(n + delt(months=-1, days=-2)), u'1 month and 2 days ago')
135 self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[prev_month] + 2))),
135 self.assertEqual(age(n + delt(years=-1, months=-1)), u'1 year and 1 month ago')
136 u'1 month and 2 days ago')
137 self.assertEqual(age(n - delt(hours=24 * 400)), u'1 year and 1 month ago')
138
136
139 def test_age_in_future(self):
137 def test_age_in_future(self):
140 import calendar
141 from rhodecode.lib.utils2 import age
138 from rhodecode.lib.utils2 import age
139 from dateutil import relativedelta
142 n = datetime.datetime.now()
140 n = datetime.datetime.now()
143 delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
141 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
142
144 self.assertEqual(age(n), u'just now')
143 self.assertEqual(age(n), u'just now')
145 self.assertEqual(age(n + delt(seconds=1)), u'in 1 second')
144 self.assertEqual(age(n + delt(seconds=1)), u'in 1 second')
146 self.assertEqual(age(n + delt(seconds=60 * 2)), u'in 2 minutes')
145 self.assertEqual(age(n + delt(seconds=60 * 2)), u'in 2 minutes')
147 self.assertEqual(age(n + delt(hours=1)), u'in 1 hour')
146 self.assertEqual(age(n + delt(hours=1)), u'in 1 hour')
148 self.assertEqual(age(n + delt(hours=24)), u'in 1 day')
147 self.assertEqual(age(n + delt(hours=24)), u'in 1 day')
149 self.assertEqual(age(n + delt(hours=24 * 5)), u'in 5 days')
148 self.assertEqual(age(n + delt(hours=24 * 5)), u'in 5 days')
150 self.assertEqual(age(n + delt(hours=24 * (calendar.mdays[n.month]))),
149 self.assertEqual(age(n + delt(months=1)), u'in 1 month')
151 u'in 1 month')
150 self.assertEqual(age(n + delt(months=1, days=1)), u'in 1 month and 1 day')
152 self.assertEqual(age(n + delt(hours=24 * (calendar.mdays[n.month] + 1))),
151 self.assertEqual(age(n + delt(years=1, months=1)), u'in 1 year and 1 month')
153 u'in 1 month and 1 day')
154 self.assertEqual(age(n + delt(hours=24 * 400)), u'in 1 year and 1 month')
155
152
156 def test_tag_exctrator(self):
153 def test_tag_exctrator(self):
157 sample = (
154 sample = (
158 "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]"
155 "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]"
159 "[requires] [stale] [see<>=>] [see => http://url.com]"
156 "[requires] [stale] [see<>=>] [see => http://url.com]"
160 "[requires => url] [lang => python] [just a tag]"
157 "[requires => url] [lang => python] [just a tag]"
161 "[,d] [ => ULR ] [obsolete] [desc]]"
158 "[,d] [ => ULR ] [obsolete] [desc]]"
162 )
159 )
163 from rhodecode.lib.helpers import desc_stylize
160 from rhodecode.lib.helpers import desc_stylize
164 res = desc_stylize(sample)
161 res = desc_stylize(sample)
165 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
162 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
166 self.assertTrue('<div class="metatag" tag="obsolete">obsolete</div>' in res)
163 self.assertTrue('<div class="metatag" tag="obsolete">obsolete</div>' in res)
167 self.assertTrue('<div class="metatag" tag="stale">stale</div>' in res)
164 self.assertTrue('<div class="metatag" tag="stale">stale</div>' in res)
168 self.assertTrue('<div class="metatag" tag="lang">python</div>' in res)
165 self.assertTrue('<div class="metatag" tag="lang">python</div>' in res)
169 self.assertTrue('<div class="metatag" tag="requires">requires =&gt; <a href="/url">url</a></div>' in res)
166 self.assertTrue('<div class="metatag" tag="requires">requires =&gt; <a href="/url">url</a></div>' in res)
170 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
167 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
171
168
172 def test_alternative_gravatar(self):
169 def test_alternative_gravatar(self):
173 from rhodecode.lib.helpers import gravatar_url
170 from rhodecode.lib.helpers import gravatar_url
174 _md5 = lambda s: hashlib.md5(s).hexdigest()
171 _md5 = lambda s: hashlib.md5(s).hexdigest()
175
172
176 def fake_conf(**kwargs):
173 def fake_conf(**kwargs):
177 from pylons import config
174 from pylons import config
178 config['app_conf'] = {}
175 config['app_conf'] = {}
179 config['app_conf']['use_gravatar'] = True
176 config['app_conf']['use_gravatar'] = True
180 config['app_conf'].update(kwargs)
177 config['app_conf'].update(kwargs)
181 return config
178 return config
182
179
183 class fake_url():
180 class fake_url():
184 @classmethod
181 @classmethod
185 def current(cls, *args, **kwargs):
182 def current(cls, *args, **kwargs):
186 return 'https://server.com'
183 return 'https://server.com'
187
184
188 with mock.patch('pylons.url', fake_url):
185 with mock.patch('pylons.url', fake_url):
189 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
186 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
190 with mock.patch('pylons.config', fake):
187 with mock.patch('pylons.config', fake):
191 from pylons import url
188 from pylons import url
192 assert url.current() == 'https://server.com'
189 assert url.current() == 'https://server.com'
193 grav = gravatar_url(email_address='test@foo.com', size=24)
190 grav = gravatar_url(email_address='test@foo.com', size=24)
194 assert grav == 'http://test.com/test@foo.com'
191 assert grav == 'http://test.com/test@foo.com'
195
192
196 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
193 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
197 with mock.patch('pylons.config', fake):
194 with mock.patch('pylons.config', fake):
198 grav = gravatar_url(email_address='test@foo.com', size=24)
195 grav = gravatar_url(email_address='test@foo.com', size=24)
199 assert grav == 'http://test.com/test@foo.com'
196 assert grav == 'http://test.com/test@foo.com'
200
197
201 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}')
198 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}')
202 with mock.patch('pylons.config', fake):
199 with mock.patch('pylons.config', fake):
203 em = 'test@foo.com'
200 em = 'test@foo.com'
204 grav = gravatar_url(email_address=em, size=24)
201 grav = gravatar_url(email_address=em, size=24)
205 assert grav == 'http://test.com/%s' % (_md5(em))
202 assert grav == 'http://test.com/%s' % (_md5(em))
206
203
207 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}/{size}')
204 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}/{size}')
208 with mock.patch('pylons.config', fake):
205 with mock.patch('pylons.config', fake):
209 em = 'test@foo.com'
206 em = 'test@foo.com'
210 grav = gravatar_url(email_address=em, size=24)
207 grav = gravatar_url(email_address=em, size=24)
211 assert grav == 'http://test.com/%s/%s' % (_md5(em), 24)
208 assert grav == 'http://test.com/%s/%s' % (_md5(em), 24)
212
209
213 fake = fake_conf(alternative_gravatar_url='{scheme}://{netloc}/{md5email}/{size}')
210 fake = fake_conf(alternative_gravatar_url='{scheme}://{netloc}/{md5email}/{size}')
214 with mock.patch('pylons.config', fake):
211 with mock.patch('pylons.config', fake):
215 em = 'test@foo.com'
212 em = 'test@foo.com'
216 grav = gravatar_url(email_address=em, size=24)
213 grav = gravatar_url(email_address=em, size=24)
217 assert grav == 'https://server.com/%s/%s' % (_md5(em), 24)
214 assert grav == 'https://server.com/%s/%s' % (_md5(em), 24)
General Comments 0
You need to be logged in to leave comments. Login now