Source

trac-ticketlinks / trac / util.py

Full commit
# -*- coding: iso8859-1 -*-
#
# Copyright (C) 2003, 2004 Edgewall Software
# Copyright (C) 2003, 2004 Jonas Borgström <jonas@edgewall.com>
#
# Trac is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# Trac is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Author: Jonas Borgström <jonas@edgewall.com>

import os
import sys
import time
import tempfile
import StringIO
from types import *

TRUE =  ['yes', '1', 1, 'true',  'on',  'aye']
FALSE = ['no',  '0', 0, 'false', 'off', 'nay']

CRLF = '\r\n'

def svn_date_to_string(date, pool):
    from svn import util
    date_seconds = util.svn_time_from_cstring(date,
                                              pool) / 1000000
    return time.strftime('%x %X', time.localtime(date_seconds))

def enum_selector (db, sql, name, selected=None,default_empty=0):
    out = StringIO.StringIO()
    out.write ('<select size="1" name="%s">' % name)

    cursor = db.cursor ()
    cursor.execute (sql)

    if default_empty:
        out.write ('<option></option>')
    while 1:
        row = cursor.fetchone()
        if not row:
            break
        if selected == row[0]:
            out.write ('<option selected>%s</option>' % row[0])
        else:
            out.write ('<option>%s</option>' % row[0])

    out.write ('</select>')
    return out.getvalue()

def wiki_escape_newline(text):
    return text.replace(os.linesep, '[[BR]]' + os.linesep)

def escape(text):
    """Escapes &, <, > and \""""
    if not text:
        return ''
    return str(text).replace('&', '&amp;') \
                    .replace('<', '&lt;') \
                    .replace('>', '&gt;') \
                    .replace('"', '&#34;')

def get_first_line(text, maxlen):
    """
    returns the first line of text. If the line is longer then
    maxlen characters it is truncated. The line is also html escaped.
    """
    lines = text.splitlines()
    line  = lines[0]
    if len(lines) > 1:
        return escape(line[:maxlen] + '...')
    elif len(line) > maxlen-3:
        return escape(line[:maxlen] + '...')
    else:
        return escape(line)

def lstrip(text, skip):
    """Python2.1 doesn't support custom skip characters"""
    while text:
        if text[0] in skip:
            text = text[1:]
        else:
            break
    return text

def rstrip(text, skip):
    """Python2.1 doesn't support custom skip characters"""
    while text:
        if text[-1] in skip:
            text = text[:-1]
        else:
            break
    return text

def to_utf8(text, charset='iso-8859-15'):
    """Convert a string to utf-8, assume the encoding is either utf-8 or latin1"""
    try:
        # Do nothing if it's already utf-8
        u = unicode(text, 'utf-8')
        return text
    except UnicodeError:
        try:
            # Use the user supplied charset if possible
            u = unicode(text, charset)
        except UnicodeError:
            # This should always work
            u = unicode(text, 'iso-8859-15')
        return u.encode('utf-8')

def href_join(u1, *tail):
    """Join a list of url components and removes redundant '/' characters"""
    for u2 in tail:
        u1 = rstrip(u1, '/') + '/' + lstrip(u2, '/')
    return u1

def sql_escape(text):
    """
    Escapes the given string so that it can be safely used in an SQL
    statement
    """
    return text.replace("'", "''").replace("\\", "\\\\")

def add_to_hdf(obj, hdf, prefix):
    """
    Adds an object to the given HDF under the specified prefix.
    Lists and dictionaries are expanded, all other objects are added
    as strings.
    """
    if type(obj) is DictType:
        for k in obj.keys():
            add_to_hdf(obj[k], hdf, '%s.%s' % (prefix, k))
    elif type(obj) is ListType:
        for i in range(len(obj)):
            add_to_hdf(obj[i], hdf, '%s.%d' % (prefix, i))
    else:
        hdf.setValue(prefix, str(obj))

def add_dictlist_to_hdf(list, hdf, prefix):
    """ Deprecated. Use add_to_hdf instead. """
    idx = 0
    for item in list:
        for key in item.keys():
            hdf.setValue('%s.%d.%s' % (prefix, idx, key), str(item[key]))
        idx = idx + 1

def add_dict_to_hdf(dict, hdf, prefix):
    """ Deprecated. Use add_to_hdf instead. """
    for key in dict.keys():
        hdf.setValue('%s.%s' % (prefix, key), str(dict[key]))

def sql_to_hdf (db, sql, hdf, prefix):
    """
    executes a sql query and insert the first result column
    into the hdf at the given prefix
    """
    cursor = db.cursor ()
    cursor.execute (sql)
    idx = 0
    while 1:
        row = cursor.fetchone()
        if not row:
            break
        hdf.setValue('%s.%d.name' % (prefix, idx), row[0])
        idx = idx + 1

def hdf_add_if_missing(hdf, prefix, value):
    """Loop through the hdf values and add @value if id doesn't exist"""
    node = hdf.getObj(prefix + '.0')
    i = 0
    while node:
        child = node.child()
        if child and child.value() == value:
            return
        node = node.next()
        i += 1
    hdf.setValue(prefix + '.%d.name' % i, value)

def shorten_line(text, maxlen = 75):
    if not text:
        return ''
    i = text.find('[[BR]]')
    j = text.find('\n')
    if i > -1 and i < maxlen:
        shortline = text[:i]+' ...'
    elif j > -1 and j < maxlen:
        shortline = text[:j]+' ...'
    elif len(text) < maxlen:
        shortline = text
    else:
        i = text[:maxlen].rfind(' ')
        if i == -1:
            i = maxlen
        shortline = text[:i]+' ...'
    return shortline

def hex_entropy(bytes=32):
    import md5
    import random
    return md5.md5(str(random.random() + time.time())).hexdigest()[:bytes]

def pretty_size(size):
    if size < 1024:
        return '%d bytes' % size
    elif size < 1024 * 1024:
        return '%d kB' % (size / 1024)
    else:
        return '%d MB' % (size / 1024 / 1024)

def pretty_age(then):
    """Calculate age (inaccurately, only for decorative purposes ;-) for
    prettyprinting."""
    units = ((3600 * 24 * 365, 'year',   'years'),
             (3600 * 24 * 30,  'month',  'months'),
             (3600 * 24 * 7,   'week',   'weeks'),
             (3600 * 24,       'day',    'days'),
             (3600,            'hour',   'hours'),
             (60,              'minute', 'minutes'))
    now = time.time()
    age_s = int(now - then)
    if age_s < 60:
        return '%i second%s' % (age_s, age_s > 1 and 's' or '')
    for u, unit, unit_plural in units:
        r = int(age_s / u)
        if r:
            return '%i %s' % (r, r == 1 and unit or unit_plural)
    return ''

def create_unique_file(path):
    """Create a new file. An index is added if the path exists"""
    parts = os.path.splitext(path)
    idx = 1
    while 1:
        try:
            flags = os.O_CREAT + os.O_WRONLY + os.O_EXCL
            if hasattr(os, 'O_BINARY'):
                flags += os.O_BINARY
            return path, os.fdopen(os.open(path, flags), 'w')
        except OSError:
            idx += 1
            # A sanity check
            if idx > 100:
                raise Exception('Failed to create unique name: ' + path)
            path = '%s.%d%s' % (parts[0], idx, parts[1])

def get_reporter_id(req):
    name = req.session.get('name', None)
    email = req.session.get('email', None)
    
    if req.authname != 'anonymous':
        return req.authname
    elif name and email:
        return '%s <%s>' % (name, email)
    elif not name and email:
        return email
    else:
        return req.authname


class TracError(Exception):
    def __init__(self, message, title=None, show_traceback=0):
        Exception.__init__(self, message)
        self.message = message
        self.title = title
        self.show_traceback = show_traceback


class NaivePopen:
   """
   This is a deadlock-safe version of popen that returns
   an object with errorlevel, out (a string) and err (a string).
   (capturestderr may not work under windows.)
   Example: print Popen3('grep spam','\n\nhere spam\n\n').out
   """
   def __init__(self,command,input=None,capturestderr=None):
       outfile=tempfile.mktemp()
       command="( %s ) > %s" % (command,outfile)
       if input:
           infile=tempfile.mktemp()
           open(infile,"w").write(input)
           command=command+" <"+infile
       if capturestderr:
           errfile=tempfile.mktemp()
           command=command+" 2>"+errfile
       self.errorlevel=os.system(command) >> 8
       self.out=open(outfile,"r").read()
       os.remove(outfile)
       if input:
           os.remove(infile)
       self.err = None
       if capturestderr:
           self.err=open(errfile,"r").read()
           os.remove(errfile)


def mydict(items):
    """dict() doesn't exist in python 2.1"""
    d = {}           
    for k, v in items:
        d[k] = v
    return d


if __name__ == '__main__ ':
    pass
    #print pre