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