1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
|
#!/usr/bin/env python
# coding: utf-8
"""
python-creole
~~~~~~~~~~~~~
:copyleft: 2008-2011 by python-creole team, see AUTHORS for more details.
:license: GNU GPL v3 or above, see LICENSE for more details.
"""
import re
import warnings
from creole.parser.html_parser_config import BLOCK_TAGS, IGNORE_TAGS
from creole.html_tools.strip_html import strip_html
from creole.py3compat import TEXT_TYPE, BINARY_TYPE
from creole.shared.document_tree import DocNode, DebugList
from creole.shared.html_parser import HTMLParser
#------------------------------------------------------------------------------
block_re = re.compile(r'''
^<pre> \s* $
(?P<pre_block>
(\n|.)*?
)
^</pre> \s* $
[\s\n]*
''', re.VERBOSE | re.UNICODE | re.MULTILINE)
inline_re = re.compile(r'''
<pre>
(?P<pre_inline>
(\n|.)*?
)
</pre>
''', re.VERBOSE | re.UNICODE)
headline_tag_re = re.compile(r"h(\d)", re.UNICODE)
#------------------------------------------------------------------------------
class HtmlParser(HTMLParser):
"""
parse html code and create a document tree.
>>> p = HtmlParser()
>>> p.feed("<p>html <strong>code</strong></p>")
<DocNode document: None>
>>> p.debug()
________________________________________________________________________________
document tree:
================================================================================
p
data: 'html '
strong
data: 'code'
********************************************************************************
>>> p = HtmlParser()
>>> p.feed("<p>html1 <script>var foo='<em>BAR</em>';</script> html2</p>")
<DocNode document: None>
>>> p.debug()
________________________________________________________________________________
document tree:
================================================================================
p
data: 'html1 '
script
data: "var foo='<em>BAR</em>';"
data: ' html2'
********************************************************************************
"""
# placeholder html tag for pre cutout areas:
_block_placeholder = "blockdata"
_inline_placeholder = "inlinedata"
def __init__(self, debug=False):
HTMLParser.__init__(self)
self.debugging = debug
if self.debugging:
warnings.warn(
message="Html2Creole debug is on! warn every data append."
)
self.result = DebugList(self)
else:
self.result = []
self.blockdata = []
self.root = DocNode("document", None)
self.cur = self.root
self.__list_level = 0
def _pre_cut(self, data, type, placeholder):
if self.debugging:
print("append blockdata: %r" % data)
assert isinstance(data, TEXT_TYPE), "blockdata is not unicode"
self.blockdata.append(data)
id = len(self.blockdata) - 1
return '<%s type="%s" id="%s" />' % (placeholder, type, id)
def _pre_pre_inline_cut(self, groups):
return self._pre_cut(groups["pre_inline"], "pre", self._inline_placeholder)
def _pre_pre_block_cut(self, groups):
return self._pre_cut(groups["pre_block"], "pre", self._block_placeholder)
def _pre_pass_block_cut(self, groups):
content = groups["pass_block"].strip()
return self._pre_cut(content, "pass", self._block_placeholder)
_pre_pass_block_start_cut = _pre_pass_block_cut
def _pre_cut_out(self, match):
groups = match.groupdict()
for name, text in groups.items():
if text is not None:
if self.debugging:
print("%15s: %r (%r)" % (name, text, match.group(0)))
method = getattr(self, '_pre_%s_cut' % name)
return method(groups)
# data = match.group("data")
def feed(self, raw_data):
assert isinstance(raw_data, TEXT_TYPE), "feed data must be unicode!"
data = raw_data.strip()
# cut out <pre> and <tt> areas block tag areas
data = block_re.sub(self._pre_cut_out, data)
data = inline_re.sub(self._pre_cut_out, data)
# Delete whitespace from html code
data = strip_html(data)
if self.debugging:
print("_" * 79)
print("raw data:")
print(repr(raw_data))
print(" -" * 40)
print("cleaned data:")
print(data)
print("-" * 79)
# print(clean_data.replace(">", ">\n"))
# print("-"*79)
HTMLParser.feed(self, data)
return self.root
#-------------------------------------------------------------------------
def _upto(self, node, kinds):
"""
Look up the tree to the first occurence
of one of the listed kinds of nodes or root.
Start at the node node.
"""
while node is not None and node.parent is not None:
node = node.parent
if node.kind in kinds:
break
return node
def _go_up(self):
kinds = list(BLOCK_TAGS) + ["document"]
self.cur = self._upto(self.cur, kinds)
self.debug_msg("go up to", self.cur)
#-------------------------------------------------------------------------
def handle_starttag(self, tag, attrs):
self.debug_msg("starttag", "%r atts: %s" % (tag, attrs))
if tag in IGNORE_TAGS:
return
headline = headline_tag_re.match(tag)
if headline:
self.cur = DocNode(
"headline", self.cur, level=int(headline.group(1))
)
return
if tag in ("li", "ul", "ol"):
if tag in ("ul", "ol"):
self.__list_level += 1
self.cur = DocNode(tag, self.cur, None, attrs, level=self.__list_level)
elif tag in ("img", "br"):
# Work-a-round if img or br tag is not marked as startendtag:
# wrong: <img src="/image.jpg"> doesn't work if </img> not exist
# right: <img src="/image.jpg" />
DocNode(tag, self.cur, None, attrs)
else:
self.cur = DocNode(tag, self.cur, None, attrs)
def handle_data(self, data):
self.debug_msg("data", "%r" % data)
if isinstance(data, BINARY_TYPE):
data = unicode(data)
DocNode("data", self.cur, content=data)
def handle_charref(self, name):
self.debug_msg("charref", "%r" % name)
DocNode("charref", self.cur, content=name)
def handle_entityref(self, name):
self.debug_msg("entityref", "%r" % name)
DocNode("entityref", self.cur, content=name)
def handle_startendtag(self, tag, attrs):
self.debug_msg("startendtag", "%r atts: %s" % (tag, attrs))
attr_dict = dict(attrs)
if tag in (self._block_placeholder, self._inline_placeholder):
id = int(attr_dict["id"])
# block_type = attr_dict["type"]
DocNode(
"%s_%s" % (tag, attr_dict["type"]),
self.cur,
content=self.blockdata[id],
# attrs = attr_dict
)
else:
DocNode(tag, self.cur, None, attrs)
def handle_endtag(self, tag):
if tag in IGNORE_TAGS:
return
self.debug_msg("endtag", "%r" % tag)
if tag == "br": # handled in starttag
return
self.debug_msg("starttag", "%r" % self.get_starttag_text())
if tag in ("ul", "ol"):
self.__list_level -= 1
if tag in BLOCK_TAGS or self.cur is None:
self._go_up()
else:
self.cur = self.cur.parent
#-------------------------------------------------------------------------
def debug_msg(self, method, txt):
if not self.debugging:
return
print("%-8s %8s: %s" % (self.getpos(), method, txt))
def debug(self, start_node=None):
"""
Display the current document tree
"""
print("_" * 80)
if start_node == None:
start_node = self.root
print(" document tree:")
else:
print(" tree from %s:" % start_node)
print("=" * 80)
def emit(node, ident=0):
for child in node.children:
txt = "%s%s" % (" " * ident, child.kind)
if child.content:
txt += ": %r" % child.content
if child.attrs:
txt += " - attrs: %r" % child.attrs
if child.level != None:
txt += " - level: %r" % child.level
print(txt)
emit(child, ident + 4)
emit(start_node)
print("*" * 80)
if __name__ == '__main__':
import doctest
print(doctest.testmod())
# p = HtmlParser(debug=True)
# p.feed("""\
#<p><span>in span</span><br />
#<code>in code</code></p>
#""")
# p.debug()
|