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