~loggerhead-team/loggerhead/trunk-rich

« back to all changes in this revision

Viewing changes to loggerhead/util.py

  • Committer: Martin Albisetti
  • Date: 2008-08-20 22:34:59 UTC
  • mto: This revision was merged to the branch mainline in revision 218.
  • Revision ID: argentina@gmail.com-20080820223459-bes0lppswufhtr5g
Added to NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
except ImportError:
25
25
    from elementtree import ElementTree as ET
26
26
 
27
 
from simpletal.simpleTALUtils import HTMLStructureCleaner
28
 
 
29
27
import base64
30
28
import cgi
31
29
import datetime
34
32
import struct
35
33
import threading
36
34
import time
37
 
import sys
38
 
import os
39
 
import subprocess
 
35
import types
40
36
 
41
37
log = logging.getLogger("loggerhead.controllers")
42
38
 
43
 
 
44
39
def fix_year(year):
45
40
    if year < 70:
46
41
        year += 2000
59
54
# displaydate and approximatedate return an elementtree <span> Element
60
55
# with the full date in a tooltip.
61
56
 
62
 
 
63
57
def date_day(value):
64
58
    return value.strftime('%Y-%m-%d')
65
59
 
133
127
    """
134
128
    Convert a dict into an object with attributes.
135
129
    """
136
 
 
137
130
    def __init__(self, _dict=None, **kw):
138
131
        if _dict is not None:
139
132
            for key, value in _dict.iteritems():
144
137
    def __repr__(self):
145
138
        out = '{ '
146
139
        for key, value in self.__dict__.iteritems():
147
 
            if key.startswith('_') or (getattr(self.__dict__[key],
148
 
                                       '__call__', None) is not None):
 
140
            if key.startswith('_') or (getattr(self.__dict__[key], '__call__', None) is not None):
149
141
                continue
150
142
            out += '%r => %r, ' % (key, value)
151
143
        out += '}'
161
153
STANDARD_PATTERN = re.compile(r'^(.*?)\s*<(.*?)>\s*$')
162
154
EMAIL_PATTERN = re.compile(r'[-\w\d\+_!%\.]+@[-\w\d\+_!%\.]+')
163
155
 
164
 
 
165
156
def hide_email(email):
166
157
    """
167
158
    try to obsure any email address in a bazaar committer's name.
186
177
#_BADCHARS_RE = re.compile(ur'[\u007f-\uffff]')
187
178
 
188
179
# FIXME: get rid of this method; use fixed_width() and avoid XML().
189
 
 
190
 
 
191
180
def html_clean(s):
192
181
    """
193
182
    clean up a string for html display.  expand any tabs, encode any html
198
187
    s = s.replace(' ', '&nbsp;')
199
188
    return s
200
189
 
201
 
 
202
190
NONBREAKING_SPACE = u'\N{NO-BREAK SPACE}'
203
191
 
204
 
 
205
192
def fill_div(s):
206
193
    """
207
194
    CSS is stupid. In some cases we need to replace an empty value with
209
196
 
210
197
    return: the same value recieved if not empty, and a '&nbsp;' if it is.
211
198
    """
212
 
 
 
199
    
213
200
 
214
201
    if s is None:
215
202
        return '&nbsp;'
224
211
            s = s.decode('iso-8859-15')
225
212
        return s
226
213
 
227
 
HSC = HTMLStructureCleaner()
228
214
 
229
215
def fixed_width(s):
230
216
    """
241
227
            s = s.decode('utf-8')
242
228
        except UnicodeDecodeError:
243
229
            s = s.decode('iso-8859-15')
244
 
 
245
 
    s = s.expandtabs().replace(' ', NONBREAKING_SPACE)
246
 
 
247
 
    return HSC.clean(s).replace('\n', '<br/>')
 
230
    return s.expandtabs().replace(' ', NONBREAKING_SPACE)
248
231
 
249
232
 
250
233
def fake_permissions(kind, executable):
283
266
P95_MEG = int(0.9 * MEG)
284
267
P95_GIG = int(0.9 * GIG)
285
268
 
286
 
 
287
269
def human_size(size, min_divisor=0):
288
270
    size = int(size)
289
271
    if (size == 0) and (min_divisor == 0):
308
290
 
309
291
    out = str(base)
310
292
    if (base < 100) and (dot != 0):
311
 
        out += '.%d' % (dot)
 
293
        out += '.%d' % (dot,)
312
294
    if divisor == KILO:
313
295
        out += 'K'
314
296
    elif divisor == MEG:
329
311
        navigation.position = 0
330
312
    navigation.count = len(navigation.revid_list)
331
313
    navigation.page_position = navigation.position // navigation.pagesize + 1
332
 
    navigation.page_count = (len(navigation.revid_list) + (navigation.pagesize\
333
 
 - 1)) // navigation.pagesize
 
314
    navigation.page_count = (len(navigation.revid_list) + (navigation.pagesize - 1)) // navigation.pagesize
334
315
 
335
316
    def get_offset(offset):
336
 
        if (navigation.position + offset < 0) or (
337
 
           navigation.position + offset > navigation.count - 1):
 
317
        if (navigation.position + offset < 0) or (navigation.position + offset > navigation.count - 1):
338
318
            return None
339
319
        return navigation.revid_list[navigation.position + offset]
340
320
 
347
327
            navigation.next_page_revid)
348
328
    start_revno = navigation.history.get_revno(navigation.start_revid)
349
329
 
350
 
    params = {'filter_file_id': navigation.filter_file_id}
 
330
    params = { 'filter_file_id': navigation.filter_file_id }
351
331
    if getattr(navigation, 'query', None) is not None:
352
332
        params['q'] = navigation.query
353
333
 
362
342
            [navigation.scan_url, next_page_revno], **params)
363
343
 
364
344
 
365
 
def directory_breadcrumbs(path, is_root, view):
366
 
    """
367
 
    Generate breadcrumb information from the directory path given
368
 
 
369
 
    The path given should be a path up to any branch that is currently being
370
 
    served
371
 
 
372
 
    Arguments:
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)
376
 
    """
377
 
    # Is our root directory itself a branch?
378
 
    if is_root:
379
 
        if view == 'directory':
380
 
            directory = 'files'
381
 
        breadcrumbs = [{
382
 
            'dir_name': path,
383
 
            'path': '',
384
 
            'suffix': view,
385
 
        }]
386
 
    else:
387
 
        # Create breadcrumb trail for the path leading up to the branch
388
 
        breadcrumbs = [{
389
 
            'dir_name': "(root)",
390
 
            'path': '',
391
 
            'suffix': '',
392
 
        }]
393
 
        if path != '/':
394
 
            dir_parts = path.strip('/').split('/')
395
 
            for index, dir_name in enumerate(dir_parts):
396
 
                breadcrumbs.append({
397
 
                    'dir_name': dir_name,
398
 
                    'path': '/'.join(dir_parts[:index + 1]),
399
 
                    'suffix': '',
400
 
                })
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
405
 
    return breadcrumbs
406
 
 
407
 
 
408
 
def branch_breadcrumbs(path, inv, view):
409
 
    """
410
 
    Generate breadcrumb information from the branch path given
411
 
 
412
 
    The path given should be a path that exists within a branch
413
 
 
414
 
    Arguments:
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)
418
 
    """
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,
426
 
        })
427
 
    return inner_breadcrumbs
428
 
 
429
 
 
430
345
def decorator(unbound):
431
 
 
432
346
    def new_decorator(f):
433
347
        g = unbound(f)
434
348
        g.__name__ = f.__name__
442
356
 
443
357
 
444
358
# common threading-lock decorator
445
 
 
446
 
 
447
359
def with_lock(lockname, debug_name=None):
448
360
    if debug_name is None:
449
361
        debug_name = lockname
450
 
 
451
362
    @decorator
452
363
    def _decorator(unbound):
453
 
 
454
364
        def locked(self, *args, **kw):
455
365
            getattr(self, lockname).acquire()
456
366
            try:
463
373
 
464
374
@decorator
465
375
def lsprof(f):
466
 
 
467
376
    def _f(*a, **kw):
468
377
        from loggerhead.lsprof import profile
469
378
        import cPickle
470
379
        z = time.time()
471
380
        ret, stats = profile(f, *a, **kw)
472
 
        log.debug('Finished profiled %s in %d msec.' % (f.__name__,
473
 
            int((time.time() - z) * 1000)))
 
381
        log.debug('Finished profiled %s in %d msec.' % (f.__name__, int((time.time() - z) * 1000)))
474
382
        stats.sort()
475
383
        stats.freeze()
476
384
        now = time.time()
477
385
        msec = int(now * 1000) % 1000
478
 
        timestr = time.strftime('%Y%m%d%H%M%S',
479
 
                                time.localtime(now)) + ('%03d' % msec)
 
386
        timestr = time.strftime('%Y%m%d%H%M%S', time.localtime(now)) + ('%03d' % msec)
480
387
        filename = f.__name__ + '-' + timestr + '.lsprof'
481
388
        cPickle.dump(stats, open(filename, 'w'), 2)
482
389
        return ret
538
445
    overrides = dict((k, v) for (k, v) in overrides.iteritems() if k in _valid)
539
446
    map.update(overrides)
540
447
    return map
541
 
 
542
 
 
543
 
class Reloader(object):
544
 
    """
545
 
    This class wraps all paste.reloader logic. All methods are @classmethod.
546
 
    """
547
 
 
548
 
    _reloader_environ_key = 'PYTHON_RELOADER_SHOULD_RUN'
549
 
 
550
 
    @classmethod
551
 
    def _turn_sigterm_into_systemexit(self):
552
 
        """
553
 
        Attempts to turn a SIGTERM exception into a SystemExit exception.
554
 
        """
555
 
        try:
556
 
            import signal
557
 
        except ImportError:
558
 
            return
559
 
 
560
 
        def handle_term(signo, frame):
561
 
            raise SystemExit
562
 
        signal.signal(signal.SIGTERM, handle_term)
563
 
 
564
 
    @classmethod
565
 
    def is_installed(self):
566
 
        return os.environ.get(self._reloader_environ_key)
567
 
 
568
 
    @classmethod
569
 
    def install(self):
570
 
        from paste import reloader
571
 
        reloader.install(int(1))
572
 
 
573
 
    @classmethod
574
 
    def restart_with_reloader(self):
575
 
        """Based on restart_with_monitor from paste.script.serve."""
576
 
        print 'Starting subprocess with file monitor'
577
 
        while 1:
578
 
            args = [sys.executable] + sys.argv
579
 
            new_environ = os.environ.copy()
580
 
            new_environ[self._reloader_environ_key] = 'true'
581
 
            proc = None
582
 
            try:
583
 
                try:
584
 
                    self._turn_sigterm_into_systemexit()
585
 
                    proc = subprocess.Popen(args, env=new_environ)
586
 
                    exit_code = proc.wait()
587
 
                    proc = None
588
 
                except KeyboardInterrupt:
589
 
                    print '^C caught in monitor process'
590
 
                    return 1
591
 
            finally:
592
 
                if (proc is not None
593
 
                    and hasattr(os, 'kill')):
594
 
                    import signal
595
 
                    try:
596
 
                        os.kill(proc.pid, signal.SIGTERM)
597
 
                    except (OSError, IOError):
598
 
                        pass
599
 
 
600
 
            # Reloader always exits with code 3; but if we are
601
 
            # a monitor, any exit code will restart
602
 
            if exit_code != 3:
603
 
                return exit_code
604
 
            print '-'*20, 'Restarting', '-'*20