formencode.rewritingparser
Covered: 75 lines
Missed: 60 lines
Skipped 23 lines
Percent: 55 %
  1
import HTMLParser
  2
import re
  3
import cgi
  4
from htmlentitydefs import name2codepoint
  6
def html_quote(v):
  7
    if v is None:
  8
        return ''
  9
    elif hasattr(v, '__html__'):
 10
        return v.__html__()
 11
    elif isinstance(v, basestring):
 12
        return cgi.escape(v, 1)
 13
    else:
 14
        if hasattr(v, '__unicode__'):
 15
            v = unicode(v)
 16
        else:
 17
            v = str(v)
 18
        return cgi.escape(v, 1)
 20
class RewritingParser(HTMLParser.HTMLParser):
 22
    listener = None
 23
    skip_next = False
 25
    def __init__(self):
 26
        self._content = []
 27
        HTMLParser.HTMLParser.__init__(self)
 29
    def feed(self, data):
 30
        self.data_is_str = isinstance(data, str)
 31
        self.source = data
 32
        self.lines = data.split('\n')
 33
        self.source_pos = 1, 0
 34
        if self.listener:
 35
            self.listener.reset()
 36
        HTMLParser.HTMLParser.feed(self, data)
 38
    _entityref_re = re.compile('&([a-zA-Z][-.a-zA-Z\d]*);')
 39
    _charref_re = re.compile('&#(\d+|[xX][a-fA-F\d]+);')
 41
    def unescape(self, s):
 42
        s = self._entityref_re.sub(self._sub_entityref, s)
 43
        s = self._charref_re.sub(self._sub_charref, s)
 44
        return s
 46
    def _sub_entityref(self, match):
 47
        name = match.group(1)
 48
        if name not in name2codepoint:
 51
            return match.group(0)
 52
        return unichr(name2codepoint[name])
 54
    def _sub_charref(self, match):
 55
        num = match.group(1)
 56
        if num.lower().startswith('x'):
 57
            num = int(num[1:], 16)
 58
        else:
 59
            num = int(num)
 60
        return unichr(num)
 62
    def handle_misc(self, whatever):
 63
        self.write_pos()
 64
    handle_charref = handle_misc
 65
    handle_entityref = handle_misc
 66
    handle_data = handle_misc
 67
    handle_comment = handle_misc
 68
    handle_decl = handle_misc
 69
    handle_pi = handle_misc
 70
    unknown_decl = handle_misc
 71
    handle_endtag = handle_misc
 73
    def write_tag(self, tag, attrs, startend=False):
 74
        attr_text = ''.join([' %s="%s"' % (n, html_quote(v))
 75
                             for (n, v) in attrs
 76
                             if not n.startswith('form:')])
 77
        if startend:
 78
            attr_text += " /"
 79
        self.write_text('<%s%s>' % (tag, attr_text))
 81
    def skip_output(self):
 82
        return False
 84
    def write_pos(self):
 85
        cur_line, cur_offset = self.getpos()
 86
        if self.skip_output():
 87
            self.source_pos = self.getpos()
 88
            return
 89
        if self.skip_next:
 90
            self.skip_next = False
 91
            self.source_pos = self.getpos()
 92
            return
 93
        if cur_line == self.source_pos[0]:
 94
            self.write_text(
 95
                self.lines[cur_line-1][self.source_pos[1]:cur_offset])
 96
        else:
 97
            self.write_text(
 98
                self.lines[self.source_pos[0]-1][self.source_pos[1]:])
 99
            self.write_text('\n')
100
            for i in range(self.source_pos[0]+1, cur_line):
101
                self.write_text(self.lines[i-1])
102
                self.write_text('\n')
103
            self.write_text(self.lines[cur_line-1][:cur_offset])
104
        self.source_pos = self.getpos()
106
    def write_text(self, text):
107
        self._content.append(text)
109
    def get_attr(self, attr, name, default=None):
110
        for n, value in attr:
111
            if n.lower() == name:
112
                return value
113
        return default
115
    def set_attr(self, attr, name, value):
116
        for i in range(len(attr)):
117
            if attr[i][0].lower() == name:
118
                attr[i] = (name, value)
119
                return
120
        attr.append((name, value))
122
    def del_attr(self, attr, name):
123
        for i in range(len(attr)):
124
            if attr[i][0].lower() == name:
125
                del attr[i]
126
                break
128
    def add_class(self, attr, class_name):
129
        current = self.get_attr(attr, 'class', '')
130
        new = current + ' ' + class_name
131
        self.set_attr(attr, 'class', new.strip())
133
    def text(self):
134
        try:
135
            return self._text
136
        except AttributeError:
137
            raise Exception(
138
                "You must .close() a parser instance before getting "
139
                "the text from it")
141
    def _get_text(self):
142
        try:
143
            return ''.join([
144
                t for t in self._content if not isinstance(t, tuple)])
145
        except UnicodeDecodeError, e:
146
            if self.data_is_str:
147
                e.reason += (
148
                    " the form was passed in as an encoded string, but "
149
                    "some data or error messages were unicode strings; "
150
                    "the form should be passed in as a unicode string")
151
            else:
152
                e.reason += (
153
                    " the form was passed in as an unicode string, but "
154
                    "some data or error message was an encoded string; "
155
                    "the data and error messages should be passed in as "
156
                    "unicode strings")
157
            raise