59
55
# displaydate and approximatedate return an elementtree <span> Element
60
56
# with the full date in a tooltip.
63
58
def date_day(value):
64
59
return value.strftime('%Y-%m-%d')
67
62
def date_time(value):
69
return value.strftime('%Y-%m-%d %T')
63
return value.strftime('%Y-%m-%d %T')
74
66
def _displaydate(date):
134
126
Convert a dict into an object with attributes.
137
128
def __init__(self, _dict=None, **kw):
138
129
if _dict is not None:
139
130
for key, value in _dict.iteritems():
144
135
def __repr__(self):
146
137
for key, value in self.__dict__.iteritems():
147
if key.startswith('_') or (getattr(self.__dict__[key],
148
'__call__', None) is not None):
138
if key.startswith('_') or (getattr(self.__dict__[key], '__call__', None) is not None):
150
140
out += '%r => %r, ' % (key, value)
145
def clean_revid(revid):
146
if revid == 'missing':
148
return sha.new(revid).hexdigest()
152
return ''.join([ '&#%d;' % ord(c) for c in text ])
155
155
def trunc(text, limit=10):
156
156
if len(text) <= limit:
158
158
return text[:limit] + '...'
162
if isinstance(s, unicode):
163
return s.encode('utf-8')
161
167
STANDARD_PATTERN = re.compile(r'^(.*?)\s*<(.*?)>\s*$')
162
168
EMAIL_PATTERN = re.compile(r'[-\w\d\+_!%\.]+@[-\w\d\+_!%\.]+')
165
170
def hide_email(email):
167
172
try to obsure any email address in a bazaar committer's name.
182
187
return '%s at %s' % (username, domains[0])
190
def triple_factors(min_value=1):
196
yield n * factors[index]
198
if index >= len(factors):
203
def scan_range(pos, max, pagesize=1):
205
given a position in a maximum range, return a list of negative and positive
206
jump factors for an hgweb-style triple-factor geometric scan.
208
for example, with pos=20 and max=500, the range would be:
209
[ -10, -3, -1, 1, 3, 10, 30, 100, 300 ]
211
i admit this is a very strange way of jumping through revisions. i didn't
215
for n in triple_factors(pagesize + 1):
185
224
# only do this if unicode turns out to be a problem
186
225
#_BADCHARS_RE = re.compile(ur'[\u007f-\uffff]')
188
227
# FIXME: get rid of this method; use fixed_width() and avoid XML().
191
228
def html_clean(s):
193
230
clean up a string for html display. expand any tabs, encode any html
198
235
s = s.replace(' ', ' ')
202
238
NONBREAKING_SPACE = u'\N{NO-BREAK SPACE}'
207
242
CSS is stupid. In some cases we need to replace an empty value with
208
243
a non breaking space ( ). There has to be a better way of doing this.
210
return: the same value recieved if not empty, and a ' ' if it is.
245
return: the same value recieved if not empty, and a NONBREAKING_SPACE
216
elif isinstance(s, int):
248
if type(s) is int and s is None:
249
return NONBREAKING_SPACE
250
elif type(s) is int and s is not None:
252
elif type(s) is types.NoneType:
253
return NONBREAKING_SPACE
255
return NONBREAKING_SPACE
222
s = s.decode('utf-8')
223
except UnicodeDecodeError:
224
s = s.decode('iso-8859-15')
227
HSC = HTMLStructureCleaner()
229
261
def fixed_width(s):
241
273
s = s.decode('utf-8')
242
274
except UnicodeDecodeError:
243
275
s = s.decode('iso-8859-15')
245
s = s.expandtabs().replace(' ', NONBREAKING_SPACE)
247
return HSC.clean(s).replace('\n', '<br/>')
276
return s.expandtabs().replace(' ', NONBREAKING_SPACE)
250
279
def fake_permissions(kind, executable):
256
285
return '-rw-r--r--'
288
def if_present(format, value):
290
format a value using a format string, if the value exists and is not None.
294
return format % value
260
298
s = base64.encodestring(s).replace('\n', '')
261
299
while (len(s) > 0) and (s[-1] == '='):
329
366
navigation.position = 0
330
367
navigation.count = len(navigation.revid_list)
331
368
navigation.page_position = navigation.position // navigation.pagesize + 1
332
navigation.page_count = (len(navigation.revid_list) + (navigation.pagesize\
333
- 1)) // navigation.pagesize
369
navigation.page_count = (len(navigation.revid_list) + (navigation.pagesize - 1)) // navigation.pagesize
335
371
def get_offset(offset):
336
if (navigation.position + offset < 0) or (
337
navigation.position + offset > navigation.count - 1):
372
if (navigation.position + offset < 0) or (navigation.position + offset > navigation.count - 1):
339
374
return navigation.revid_list[navigation.position + offset]
341
376
navigation.last_in_page_revid = get_offset(navigation.pagesize - 1)
342
377
navigation.prev_page_revid = get_offset(-1 * navigation.pagesize)
343
378
navigation.next_page_revid = get_offset(1 * navigation.pagesize)
344
prev_page_revno = navigation.history.get_revno(
345
navigation.prev_page_revid)
346
next_page_revno = navigation.history.get_revno(
347
navigation.next_page_revid)
348
start_revno = navigation.history.get_revno(navigation.start_revid)
350
params = {'filter_file_id': navigation.filter_file_id}
379
prev_page_revno = navigation.branch.history.get_revno(
380
navigation.prev_page_revid)
381
next_page_revno = navigation.branch.history.get_revno(
382
navigation.next_page_revid)
383
start_revno = navigation.branch._history.get_revno(navigation.start_revid)
385
prev_page_revno = navigation.branch._history.get_revno(
386
navigation.prev_page_revid)
387
next_page_revno = navigation.branch._history.get_revno(
388
navigation.next_page_revid)
390
params = { 'filter_file_id': navigation.filter_file_id }
351
391
if getattr(navigation, 'query', None) is not None:
352
392
params['q'] = navigation.query
362
402
[navigation.scan_url, next_page_revno], **params)
365
def directory_breadcrumbs(path, is_root, view):
367
Generate breadcrumb information from the directory path given
369
The path given should be a path up to any branch that is currently being
373
path -- The path to convert into breadcrumbs
374
is_root -- Whether or not loggerhead is serving a branch at its root
375
view -- The type of view we are showing (files, changes etc)
377
# Is our root directory itself a branch?
379
if view == 'directory':
387
# Create breadcrumb trail for the path leading up to the branch
389
'dir_name': "(root)",
394
dir_parts = path.strip('/').split('/')
395
for index, dir_name in enumerate(dir_parts):
397
'dir_name': dir_name,
398
'path': '/'.join(dir_parts[:index + 1]),
401
# If we are not in the directory view, the last crumb is a branch,
402
# so we need to specify a view
403
if view != 'directory':
404
breadcrumbs[-1]['suffix'] = '/' + view
408
def branch_breadcrumbs(path, inv, view):
410
Generate breadcrumb information from the branch path given
412
The path given should be a path that exists within a branch
415
path -- The path to convert into breadcrumbs
416
inv -- Inventory to get file information from
417
view -- The type of view we are showing (files, changes etc)
419
dir_parts = path.strip('/').split('/')
420
inner_breadcrumbs = []
421
for index, dir_name in enumerate(dir_parts):
422
inner_breadcrumbs.append({
423
'dir_name': dir_name,
424
'file_id': inv.path2id('/'.join(dir_parts[:index + 1])),
425
'suffix': '/' + view,
427
return inner_breadcrumbs
405
def log_exception(log):
406
for line in ''.join(traceback.format_exception(*sys.exc_info())).split('\n'):
430
410
def decorator(unbound):
432
411
def new_decorator(f):
434
413
g.__name__ = f.__name__
444
423
# common threading-lock decorator
447
424
def with_lock(lockname, debug_name=None):
448
425
if debug_name is None:
449
426
debug_name = lockname
452
428
def _decorator(unbound):
454
429
def locked(self, *args, **kw):
455
430
getattr(self, lockname).acquire()
440
def strip_whitespace(f):
444
out = re.sub(r'\n\s+', '\n', out)
445
out = re.sub(r'[ \t]+', ' ', out)
446
out = re.sub(r'\s+\n', '\n', out)
448
log.debug('Saved %sB (%d%%) by stripping whitespace.',
449
human_size(orig_len - new_len),
450
round(100.0 - float(new_len) * 100.0 / float(orig_len)))
467
457
def _f(*a, **kw):
468
458
from loggerhead.lsprof import profile
471
461
ret, stats = profile(f, *a, **kw)
472
log.debug('Finished profiled %s in %d msec.' % (f.__name__,
473
int((time.time() - z) * 1000)))
462
log.debug('Finished profiled %s in %d msec.' % (f.__name__, int((time.time() - z) * 1000)))
476
465
now = time.time()
477
466
msec = int(now * 1000) % 1000
478
timestr = time.strftime('%Y%m%d%H%M%S',
479
time.localtime(now)) + ('%03d' % msec)
467
timestr = time.strftime('%Y%m%d%H%M%S', time.localtime(now)) + ('%03d' % msec)
480
468
filename = f.__name__ + '-' + timestr + '.lsprof'
481
469
cPickle.dump(stats, open(filename, 'w'), 2)
538
526
overrides = dict((k, v) for (k, v) in overrides.iteritems() if k in _valid)
539
527
map.update(overrides)
543
class Reloader(object):
545
This class wraps all paste.reloader logic. All methods are @classmethod.
548
_reloader_environ_key = 'PYTHON_RELOADER_SHOULD_RUN'
551
def _turn_sigterm_into_systemexit(self):
553
Attempts to turn a SIGTERM exception into a SystemExit exception.
560
def handle_term(signo, frame):
562
signal.signal(signal.SIGTERM, handle_term)
565
def is_installed(self):
566
return os.environ.get(self._reloader_environ_key)
570
from paste import reloader
571
reloader.install(int(1))
574
def restart_with_reloader(self):
575
"""Based on restart_with_monitor from paste.script.serve."""
576
print 'Starting subprocess with file monitor'
578
args = [sys.executable] + sys.argv
579
new_environ = os.environ.copy()
580
new_environ[self._reloader_environ_key] = 'true'
584
self._turn_sigterm_into_systemexit()
585
proc = subprocess.Popen(args, env=new_environ)
586
exit_code = proc.wait()
588
except KeyboardInterrupt:
589
print '^C caught in monitor process'
593
and hasattr(os, 'kill')):
596
os.kill(proc.pid, signal.SIGTERM)
597
except (OSError, IOError):
600
# Reloader always exits with code 3; but if we are
601
# a monitor, any exit code will restart
604
print '-'*20, 'Restarting', '-'*20