##// END OF EJS Templates
merge
marcink -
r2044:fa74e916 merge default
parent child Browse files
Show More
@@ -1,92 +1,92 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.__init__
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 RhodeCode, a web based repository management based on pylons
7 7 versioning implementation: http://semver.org/
8 8
9 9 :created_on: Apr 9, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26 import sys
27 27 import platform
28 28
29 VERSION = (1, 3, 0)
29 VERSION = (1, 3, 1)
30 30 __version__ = '.'.join((str(each) for each in VERSION[:4]))
31 31 __dbversion__ = 5 # defines current db version for migrations
32 32 __platform__ = platform.system()
33 33 __license__ = 'GPLv3'
34 34 __py_version__ = sys.version_info
35 35
36 36 PLATFORM_WIN = ('Windows')
37 37 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
38 38
39 39 requirements = [
40 40 "Pylons==1.0.0",
41 41 "Beaker==1.6.2",
42 42 "WebHelpers>=1.2",
43 43 "formencode==1.2.4",
44 44 "SQLAlchemy==0.7.4",
45 45 "Mako==0.5.0",
46 46 "pygments>=1.4",
47 47 "whoosh>=2.3.0,<2.4",
48 48 "celery>=2.2.5,<2.3",
49 49 "babel",
50 50 "python-dateutil>=1.5.0,<2.0.0",
51 51 "dulwich>=0.8.0,<0.9.0",
52 52 "webob==1.0.8",
53 53 "markdown==2.1.1",
54 54 "docutils==0.8.1",
55 55 ]
56 56
57 57 if __py_version__ < (2, 6):
58 58 requirements.append("simplejson")
59 59 requirements.append("pysqlite")
60 60
61 61 if __platform__ in PLATFORM_WIN:
62 62 requirements.append("mercurial>=2.1,<2.2")
63 63 else:
64 64 requirements.append("py-bcrypt")
65 65 requirements.append("mercurial>=2.1,<2.2")
66 66
67 67
68 68 try:
69 69 from rhodecode.lib import get_current_revision
70 70 _rev = get_current_revision(quiet=True)
71 71 except ImportError:
72 72 # this is needed when doing some setup.py operations
73 73 _rev = False
74 74
75 75 if len(VERSION) > 3 and _rev:
76 76 __version__ += ' [rev:%s]' % _rev[0]
77 77
78 78
79 79 def get_version():
80 80 """Returns shorter version (digit parts only) as string."""
81 81
82 82 return '.'.join((str(each) for each in VERSION[:3]))
83 83
84 84 BACKENDS = {
85 85 'hg': 'Mercurial repository',
86 86 'git': 'Git repository',
87 87 }
88 88
89 89 CELERY_ON = False
90 90
91 91 # link to config for pylons
92 92 CONFIG = {}
@@ -1,464 +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 234
235 # if it's not basestr cast to str
235 236 if not isinstance(unicode_, basestring):
236 237 return str(unicode_)
237 238
238 239 if isinstance(unicode_, str):
239 240 return unicode_
240 241
241 242 if not to_encoding:
242 243 import rhodecode
243 244 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
244 245 to_encoding = DEFAULT_ENCODING
245 246
246 247 try:
247 248 return unicode_.encode(to_encoding)
248 249 except UnicodeEncodeError:
249 250 pass
250 251
251 252 try:
252 253 import chardet
253 254 encoding = chardet.detect(unicode_)['encoding']
254 255 print encoding
255 256 if encoding is None:
256 257 raise UnicodeEncodeError()
257 258
258 259 return unicode_.encode(encoding)
259 260 except (ImportError, UnicodeEncodeError):
260 261 return unicode_.encode(to_encoding, 'replace')
261 262
262 263 return safe_str
263 264
264 265
265 266 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
266 267 """
267 268 Custom engine_from_config functions that makes sure we use NullPool for
268 269 file based sqlite databases. This prevents errors on sqlite. This only
269 270 applies to sqlalchemy versions < 0.7.0
270 271
271 272 """
272 273 import sqlalchemy
273 274 from sqlalchemy import engine_from_config as efc
274 275 import logging
275 276
276 277 if int(sqlalchemy.__version__.split('.')[1]) < 7:
277 278
278 279 # This solution should work for sqlalchemy < 0.7.0, and should use
279 280 # proxy=TimerProxy() for execution time profiling
280 281
281 282 from sqlalchemy.pool import NullPool
282 283 url = configuration[prefix + 'url']
283 284
284 285 if url.startswith('sqlite'):
285 286 kwargs.update({'poolclass': NullPool})
286 287 return efc(configuration, prefix, **kwargs)
287 288 else:
288 289 import time
289 290 from sqlalchemy import event
290 291 from sqlalchemy.engine import Engine
291 292
292 293 log = logging.getLogger('sqlalchemy.engine')
293 294 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
294 295 engine = efc(configuration, prefix, **kwargs)
295 296
296 297 def color_sql(sql):
297 298 COLOR_SEQ = "\033[1;%dm"
298 299 COLOR_SQL = YELLOW
299 300 normal = '\x1b[0m'
300 301 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
301 302
302 303 if configuration['debug']:
303 304 #attach events only for debug configuration
304 305
305 306 def before_cursor_execute(conn, cursor, statement,
306 307 parameters, context, executemany):
307 308 context._query_start_time = time.time()
308 309 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
309 310
310 311
311 312 def after_cursor_execute(conn, cursor, statement,
312 313 parameters, context, executemany):
313 314 total = time.time() - context._query_start_time
314 315 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
315 316
316 317 event.listen(engine, "before_cursor_execute",
317 318 before_cursor_execute)
318 319 event.listen(engine, "after_cursor_execute",
319 320 after_cursor_execute)
320 321
321 322 return engine
322 323
323 324
324 325 def age(curdate):
325 326 """
326 327 turns a datetime into an age string.
327 328
328 329 :param curdate: datetime object
329 330 :rtype: unicode
330 331 :returns: unicode words describing age
331 332 """
332 333
333 334 from datetime import datetime
334 335 from webhelpers.date import time_ago_in_words
335 336
336 337 _ = lambda s: s
337 338
338 339 if not curdate:
339 340 return ''
340 341
341 342 agescales = [(_(u"year"), 3600 * 24 * 365),
342 343 (_(u"month"), 3600 * 24 * 30),
343 344 (_(u"day"), 3600 * 24),
344 345 (_(u"hour"), 3600),
345 346 (_(u"minute"), 60),
346 347 (_(u"second"), 1), ]
347 348
348 349 age = datetime.now() - curdate
349 350 age_seconds = (age.days * agescales[2][1]) + age.seconds
350 351 pos = 1
351 352 for scale in agescales:
352 353 if scale[1] <= age_seconds:
353 354 if pos == 6:
354 355 pos = 5
355 356 return '%s %s' % (time_ago_in_words(curdate,
356 357 agescales[pos][0]), _('ago'))
357 358 pos += 1
358 359
359 360 return _(u'just now')
360 361
361 362
362 363 def uri_filter(uri):
363 364 """
364 365 Removes user:password from given url string
365 366
366 367 :param uri:
367 368 :rtype: unicode
368 369 :returns: filtered list of strings
369 370 """
370 371 if not uri:
371 372 return ''
372 373
373 374 proto = ''
374 375
375 376 for pat in ('https://', 'http://'):
376 377 if uri.startswith(pat):
377 378 uri = uri[len(pat):]
378 379 proto = pat
379 380 break
380 381
381 382 # remove passwords and username
382 383 uri = uri[uri.find('@') + 1:]
383 384
384 385 # get the port
385 386 cred_pos = uri.find(':')
386 387 if cred_pos == -1:
387 388 host, port = uri, None
388 389 else:
389 390 host, port = uri[:cred_pos], uri[cred_pos + 1:]
390 391
391 392 return filter(None, [proto, host, port])
392 393
393 394
394 395 def credentials_filter(uri):
395 396 """
396 397 Returns a url with removed credentials
397 398
398 399 :param uri:
399 400 """
400 401
401 402 uri = uri_filter(uri)
402 403 #check if we have port
403 404 if len(uri) > 2 and uri[2]:
404 405 uri[2] = ':' + uri[2]
405 406
406 407 return ''.join(uri)
407 408
408 409
409 410 def get_changeset_safe(repo, rev):
410 411 """
411 412 Safe version of get_changeset if this changeset doesn't exists for a
412 413 repo it returns a Dummy one instead
413 414
414 415 :param repo:
415 416 :param rev:
416 417 """
417 418 from rhodecode.lib.vcs.backends.base import BaseRepository
418 419 from rhodecode.lib.vcs.exceptions import RepositoryError
419 420 if not isinstance(repo, BaseRepository):
420 421 raise Exception('You must pass an Repository '
421 422 'object as first argument got %s', type(repo))
422 423
423 424 try:
424 425 cs = repo.get_changeset(rev)
425 426 except RepositoryError:
426 427 from rhodecode.lib.utils import EmptyChangeset
427 428 cs = EmptyChangeset(requested_revision=rev)
428 429 return cs
429 430
430 431
431 432 def get_current_revision(quiet=False):
432 433 """
433 434 Returns tuple of (number, id) from repository containing this package
434 435 or None if repository could not be found.
435 436
436 437 :param quiet: prints error for fetching revision if True
437 438 """
438 439
439 440 try:
440 441 from rhodecode.lib.vcs import get_repo
441 442 from rhodecode.lib.vcs.utils.helpers import get_scm
442 443 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
443 444 scm = get_scm(repopath)[0]
444 445 repo = get_repo(path=repopath, alias=scm)
445 446 tip = repo.get_changeset()
446 447 return (tip.revision, tip.short_id)
447 448 except Exception, err:
448 449 if not quiet:
449 450 print ("Cannot retrieve rhodecode's revision. Original error "
450 451 "was: %s" % err)
451 452 return None
452 453
453 454
454 455 def extract_mentioned_users(s):
455 456 """
456 457 Returns unique usernames from given string s that have @mention
457 458
458 459 :param s: string to get mentions
459 460 """
460 461 usrs = {}
461 462 for username in re.findall(r'(?:^@|\s@)(\w+)', s):
462 463 usrs[username] = username
463 464
464 465 return sorted(usrs.keys())
General Comments 0
You need to be logged in to leave comments. Login now