~loggerhead-team/loggerhead/trunk-rich

« back to all changes in this revision

Viewing changes to loggerhead/util.py

  • Committer: John Arbash Meinel
  • Date: 2010-04-26 19:04:06 UTC
  • mto: This revision was merged to the branch mainline in revision 435.
  • Revision ID: john@arbash-meinel.com-20100426190406-psgsbys8e33cpcbg
Add 'http_log_level' and --log-level.

This allows us to set a default log level that isn't 'WARNING' the logger
default, and isn't DEBUG, the current code default.


Also add a time() check for serving a particular branch page.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
20
#
21
21
 
22
 
try:
23
 
    from xml.etree import ElementTree as ET
24
 
except ImportError:
25
 
    from elementtree import ElementTree as ET
26
 
 
27
22
import base64
28
23
import cgi
29
24
import datetime
36
31
import os
37
32
import subprocess
38
33
 
 
34
try:
 
35
    from xml.etree import ElementTree as ET
 
36
except ImportError:
 
37
    from elementtree import ElementTree as ET
 
38
 
 
39
from bzrlib import urlutils
 
40
 
 
41
from simpletal.simpleTALUtils import HTMLStructureCleaner
 
42
 
39
43
log = logging.getLogger("loggerhead.controllers")
40
44
 
41
45
 
64
68
 
65
69
def date_time(value):
66
70
    if value is not None:
67
 
        return value.strftime('%Y-%m-%d %T')
 
71
        return value.strftime('%Y-%m-%d %H:%M:%S')
68
72
    else:
69
73
        return 'N/A'
70
74
 
127
131
    return _wrap_with_date_time_title(date, _displaydate(date))
128
132
 
129
133
 
130
 
class Container (object):
 
134
class Container(object):
131
135
    """
132
136
    Convert a dict into an object with attributes.
133
137
    """
134
138
 
135
139
    def __init__(self, _dict=None, **kw):
 
140
        self._properties = {}
136
141
        if _dict is not None:
137
142
            for key, value in _dict.iteritems():
138
143
                setattr(self, key, value)
149
154
        out += '}'
150
155
        return out
151
156
 
 
157
    def __getattr__(self, attr):
 
158
        """Used for handling things that aren't already available."""
 
159
        if attr.startswith('_') or attr not in self._properties:
 
160
            raise AttributeError('No attribute: %s' % (attr,))
 
161
        val = self._properties[attr](self, attr)
 
162
        setattr(self, attr, val)
 
163
        return val
 
164
 
 
165
    def _set_property(self, attr, prop_func):
 
166
        """Set a function that will be called when an attribute is desired.
 
167
 
 
168
        We will cache the return value, so the function call should be
 
169
        idempotent. We will pass 'self' and the 'attr' name when triggered.
 
170
        """
 
171
        if attr.startswith('_'):
 
172
            raise ValueError("Cannot create properties that start with _")
 
173
        self._properties[attr] = prop_func
 
174
 
152
175
 
153
176
def trunc(text, limit=10):
154
177
    if len(text) <= limit:
179
202
        return '%s at %s' % (username, domains[-2])
180
203
    return '%s at %s' % (username, domains[0])
181
204
 
 
205
def hide_emails(emails):
 
206
    """
 
207
    try to obscure any email address in a list of bazaar committers' names.
 
208
    """
 
209
    result = []
 
210
    for email in emails:
 
211
        result.append(hide_email(email))
 
212
    return result
182
213
 
183
214
# only do this if unicode turns out to be a problem
184
215
#_BADCHARS_RE = re.compile(ur'[\u007f-\uffff]')
207
238
 
208
239
    return: the same value recieved if not empty, and a '&nbsp;' if it is.
209
240
    """
210
 
 
211
 
 
212
241
    if s is None:
213
242
        return '&nbsp;'
214
243
    elif isinstance(s, int):
222
251
            s = s.decode('iso-8859-15')
223
252
        return s
224
253
 
 
254
HSC = HTMLStructureCleaner()
225
255
 
226
256
def fixed_width(s):
227
257
    """
238
268
            s = s.decode('utf-8')
239
269
        except UnicodeDecodeError:
240
270
            s = s.decode('iso-8859-15')
241
 
    return s.expandtabs().replace(' ', NONBREAKING_SPACE)
 
271
 
 
272
    s = cgi.escape(s).expandtabs().replace(' ', NONBREAKING_SPACE)
 
273
 
 
274
    return HSC.clean(s).replace('\n', '<br/>')
242
275
 
243
276
 
244
277
def fake_permissions(kind, executable):
302
335
 
303
336
    out = str(base)
304
337
    if (base < 100) and (dot != 0):
305
 
        out += '.%d' % (dot)
 
338
        out += '.%d' % (dot,)
306
339
    if divisor == KILO:
307
340
        out += 'K'
308
341
    elif divisor == MEG:
312
345
    return out
313
346
 
314
347
 
 
348
def local_path_from_url(url):
 
349
    """Convert Bazaar URL to local path, ignoring readonly+ prefix"""
 
350
    readonly_prefix = 'readonly+'
 
351
    if url.startswith(readonly_prefix):
 
352
        url = url[len(readonly_prefix):]
 
353
    return urlutils.local_path_from_url(url)
 
354
 
 
355
 
315
356
def fill_in_navigation(navigation):
316
357
    """
317
358
    given a navigation block (used by the template for the page header), fill
370
411
    """
371
412
    # Is our root directory itself a branch?
372
413
    if is_root:
373
 
        if view == 'directory':
374
 
            directory = 'files'
375
414
        breadcrumbs = [{
376
415
            'dir_name': path,
377
416
            'path': '',
435
474
    return new_decorator
436
475
 
437
476
 
438
 
# common threading-lock decorator
439
 
 
440
 
 
441
 
def with_lock(lockname, debug_name=None):
442
 
    if debug_name is None:
443
 
        debug_name = lockname
444
 
 
445
 
    @decorator
446
 
    def _decorator(unbound):
447
 
 
448
 
        def locked(self, *args, **kw):
449
 
            getattr(self, lockname).acquire()
450
 
            try:
451
 
                return unbound(self, *args, **kw)
452
 
            finally:
453
 
                getattr(self, lockname).release()
454
 
        return locked
455
 
    return _decorator
456
 
 
457
477
 
458
478
@decorator
459
479
def lsprof(f):
470
490
        now = time.time()
471
491
        msec = int(now * 1000) % 1000
472
492
        timestr = time.strftime('%Y%m%d%H%M%S',
473
 
                                time.localtime(now)) + ('%03d' % msec)
 
493
                                time.localtime(now)) + ('%03d' % (msec,))
474
494
        filename = f.__name__ + '-' + timestr + '.lsprof'
475
495
        cPickle.dump(stats, open(filename, 'w'), 2)
476
496
        return ret
542
562
    _reloader_environ_key = 'PYTHON_RELOADER_SHOULD_RUN'
543
563
 
544
564
    @classmethod
545
 
    def _turn_sigterm_into_systemexit(self):
 
565
    def _turn_sigterm_into_systemexit(cls):
546
566
        """
547
567
        Attempts to turn a SIGTERM exception into a SystemExit exception.
548
568
        """
556
576
        signal.signal(signal.SIGTERM, handle_term)
557
577
 
558
578
    @classmethod
559
 
    def is_installed(self):
560
 
        return os.environ.get(self._reloader_environ_key)
 
579
    def is_installed(cls):
 
580
        return os.environ.get(cls._reloader_environ_key)
561
581
 
562
582
    @classmethod
563
 
    def install(self):
 
583
    def install(cls):
564
584
        from paste import reloader
565
585
        reloader.install(int(1))
566
586
 
567
587
    @classmethod
568
 
    def restart_with_reloader(self):
 
588
    def restart_with_reloader(cls):
569
589
        """Based on restart_with_monitor from paste.script.serve."""
570
590
        print 'Starting subprocess with file monitor'
571
 
        while 1:
 
591
        while True:
572
592
            args = [sys.executable] + sys.argv
573
593
            new_environ = os.environ.copy()
574
 
            new_environ[self._reloader_environ_key] = 'true'
 
594
            new_environ[cls._reloader_environ_key] = 'true'
575
595
            proc = None
576
596
            try:
577
597
                try:
578
 
                    self._turn_sigterm_into_systemexit()
 
598
                    cls._turn_sigterm_into_systemexit()
579
599
                    proc = subprocess.Popen(args, env=new_environ)
580
600
                    exit_code = proc.wait()
581
601
                    proc = None
584
604
                    return 1
585
605
            finally:
586
606
                if (proc is not None
587
 
                    and hasattr(os, 'kill')):
 
607
                    and getattr(os, 'kill', None) is not None):
588
608
                    import signal
589
609
                    try:
590
610
                        os.kill(proc.pid, signal.SIGTERM)
596
616
            if exit_code != 3:
597
617
                return exit_code
598
618
            print '-'*20, 'Restarting', '-'*20
 
619
 
 
620
 
 
621
def convert_file_errors(application):
 
622
    """WSGI wrapper to convert some file errors to Paste exceptions"""
 
623
    def new_application(environ, start_response):
 
624
        try:
 
625
            return application(environ, start_response)
 
626
        except (IOError, OSError), e:
 
627
            import errno
 
628
            from paste import httpexceptions
 
629
            if e.errno == errno.ENOENT:
 
630
                raise httpexceptions.HTTPNotFound()
 
631
            elif e.errno == errno.EACCES:
 
632
                raise httpexceptions.HTTPForbidden()
 
633
            else:
 
634
                raise
 
635
    return new_application