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