Source / extensions /

import re
import cgi
from roundup import hyperdb
from roundup.cgi.templating import register_propclass, StringHTMLProperty

# pre-hg migration
substitutions = [
    #  r12345, r 12345, rev12345, rev 12345, revision12345, revision 12345
     r'<a href="\g<revision>'

    # Lib/, Modules/somemodule.c, Doc/somedocfile.rst, ...
     r'<a href="'

def make_file_link(match):
    baseurl = ''
    sep ='sep')
    path ='path')
    npath = path.replace('\\', '/')  # normalize the path separators
    lnum ='lnum') or ''  # the match includes the ':'
    if not npath.endswith('/'):
        # files without and with line number
        if not lnum:
            return '<a href="%s%s">%s%s</a>' % (baseurl, npath, sep, path)
            return '<a href="%s%s#l%s">%s%s%s</a>' % (baseurl, npath, lnum[1:],
                                                      sep, path, lnum)
        # dirs
        return '<a href="%s%s">%s%s</a>%s' % (baseurl, npath, sep, path, lnum)

def guess_version(path):
    """Search for Python version hints in the file path."""
    match ='((?<=[Pp]ython)[23]\d|[23]\.\d)', path)
    if not match:
        return 'default'
    version =
    if '.' not in version:
        version = '.'.join(version)
    if version in ['2.5', '2.6', '2.7', '3.1', '3.2', '3.3']:
        return version
    return 'default'

def make_traceback_link(match):
    """Convert the file/line in the traceback lines in a link."""
    baseurl = ''
    path ='path')  # first part of the path
    branch = guess_version('fullpath'))  # guessed branch
    file ='file')  # second part after Lib/
    nfile = file.replace('\\', '/')  # normalize the path separators
    lnum ='lnum')
    return ('File "%s<a href="%s%s/Lib/%s#l%s">%s</a>", line %s' %
            (path, baseurl, branch, nfile, lnum, file, lnum))

def make_pep_link(match):
    text =
    pepnum =
    return '<a href="">%s</a>' % (pepnum, text)

# these regexs have test in tests/

seps = r'\b(?<![-/?&;=_])'  # these chars should not precede the targets
substitutions = [
    # deadbeeffeed  (hashes with exactly twelve or forty chars)
    (re.compile(r'%s(?P<revision>[a-fA-F0-9]{40})\b' % seps),
     r'<a href="\g<revision>">\g<revision></a>'),
    (re.compile(r'%s(?P<revision>[a-fA-F0-9]{12})\b' % seps),
     r'<a href="\g<revision>">\g<revision></a>'),

    # r12345, r 12345, rev12345, rev. 12345, revision12345, revision 12345
    (re.compile(r'%s(?P<revstr>r\.?(ev\.?(ision)?)?\s*)(?P<revision>\d{4,})' % seps),
     r'<a href="\g<revision>">\g<revstr>\g<revision></a>'),

    # Lib/, Lib/, Modules/somemodule.c:123, ...

    # traceback lines: File "Lib/", line 123 in some_func
    # note: this regex is not 100% accurate, it might get the wrong part of
    # the path or link to non-existing files, but it usually works fine
    (re.compile(r'File "(?P<fullpath>(?P<path>[-.\w/\\:]+(?<!var)[/\\][Ll]ib[/\\]'
                r'(?!.*site-packages)(python[\d.]*[/\\])?)(?P<file>[-.\w/\\]+?\.py))", '
                r'line (?P<lnum>\d{1,5})'),

    # PEP 8, PEP8, PEP 0008, ...
    (re.compile(r'%s\b(?<![/=-])PEP\s*(\d{1,4})(?!/)\b' % seps, re.I),

    # devguide
    (re.compile(r'%s(devguide(?:/\w+(?:.html)?(?:#[\w-]+)?)?)' % seps),
     r'<a href="\1">\1</a>'),

# if the issue number is too big the db will explode -- limit it to 7 digits
issue_re = re.compile(r'(?P<text>(\#|\b(?<![-/_])issue)\s*(?P<id>1?\d{1,6}))\b', re.I)

class PyDevStringHTMLProperty(StringHTMLProperty):
    def _hyper_repl(self, match):
        Override the original method and change it to still linkify URLs and
        emails but avoid linkification of issues and other items
        (except messages and files).
            return self._hyper_repl_url(match, '<a href="%s">%s</a>%s')
            return self._hyper_repl_email(match, '<a href="mailto:%s">%s</a>')
        elif (len('id')) < 10 and
    'class') and
    'class').lower() in ('msg', 'file')):
            # linkify msgs but not issues and other things
            return self._hyper_repl_item(match,
                '<a href="%(cls)s%(id)s">%(item)s</a>')
            # just return the matched text

    def pydev_hyperlinked(self):
        """Create python-dev-specific links."""
        # first do the normal linkification (without linkify the issues)
        message =  self.plain(hyperlink=1)
        # then add links for revision numbers and paths
        for cre, replacement in substitutions:
            message = cre.sub(replacement, message)
        # finally add links for issues
        message = issue_re.sub(self._linkify_issue, message)
        return message

    def _linkify_issue(self, match):
        """Turn an issue (e.g. 'issue 1234' or '#1234') to an HTML link"""
        template = ('<a class="%(status)s" title="[%(status)s] %(title)s" '
        issue_id ='id')
        text ='text')
        cl = self._db.issue
        # check if the issue exists
        if not cl.hasnode(issue_id):
            return text
        # get the title
        title = cgi.escape(cl.get(issue_id, 'title').replace('"', "'"))
        status_id = cl.get(issue_id, 'status')
        # get the name of the status
        if status_id is not None:
            status = self._db.status.get(status_id, 'name')
            status = 'none'
        return template % dict(issue_id=issue_id, title=title,
                               status=status, text=text)

noise_changes = re.compile('(nosy_count|message_count)\: \d+\.0( -> \d+\.0)?')

def clean_count(history):
    history = noise_changes.sub('', history).replace('<td><br />', '<td>')
    return history

def init(instance):
    register_propclass(hyperdb.String, PyDevStringHTMLProperty)
    instance.registerUtil('clean_count', clean_count)