118
118
VOID_ELEMENTS = set (['area' , 'base' , 'br' , 'col' , 'embed' , 'hr' , 'img' , 'input' , 'keygen' ,
119
119
'link' , 'menuitem' , 'meta' , 'param' , 'source' , 'track' , 'wbr' ])
120
120
121
- # simplified HTML parser.
122
- # this is possible because we are dealing with very regular HTML from rustdoc;
123
- # we only have to deal with i) void elements and ii) empty attributes.
121
+
124
122
class CustomHTMLParser (HTMLParser ):
123
+ """simplified HTML parser.
124
+
125
+ this is possible because we are dealing with very regular HTML from
126
+ rustdoc; we only have to deal with i) void elements and ii) empty
127
+ attributes."""
125
128
def __init__ (self , target = None ):
126
129
HTMLParser .__init__ (self )
127
130
self .__builder = target or ET .TreeBuilder ()
131
+
128
132
def handle_starttag (self , tag , attrs ):
129
133
attrs = dict ((k , v or '' ) for k , v in attrs )
130
134
self .__builder .start (tag , attrs )
131
- if tag in VOID_ELEMENTS : self .__builder .end (tag )
135
+ if tag in VOID_ELEMENTS :
136
+ self .__builder .end (tag )
137
+
132
138
def handle_endtag (self , tag ):
133
139
self .__builder .end (tag )
140
+
134
141
def handle_startendtag (self , tag , attrs ):
135
142
attrs = dict ((k , v or '' ) for k , v in attrs )
136
143
self .__builder .start (tag , attrs )
137
144
self .__builder .end (tag )
145
+
138
146
def handle_data (self , data ):
139
147
self .__builder .data (data )
148
+
140
149
def handle_entityref (self , name ):
141
150
self .__builder .data (entitydefs [name ])
151
+
142
152
def handle_charref (self , name ):
143
153
code = int (name [1 :], 16 ) if name .startswith (('x' , 'X' )) else int (name , 10 )
144
154
self .__builder .data (unichr (code ).encode ('utf-8' ))
155
+
145
156
def close (self ):
146
157
HTMLParser .close (self )
147
158
return self .__builder .close ()
148
159
149
160
Command = namedtuple ('Command' , 'negated cmd args lineno' )
150
161
151
- # returns a generator out of the file object, which
152
- # - removes `\\` then `\n` then a shared prefix with the previous line then optional whitespace;
153
- # - keeps a line number (starting from 0) of the first line being concatenated.
162
+
154
163
def concat_multi_lines (f ):
164
+ """returns a generator out of the file object, which
165
+ - removes `\\ ` then `\n ` then a shared prefix with the previous line then
166
+ optional whitespace;
167
+ - keeps a line number (starting from 0) of the first line being
168
+ concatenated."""
155
169
lastline = None # set to the last line when the last line has a backslash
156
170
firstlineno = None
157
171
catenated = ''
@@ -162,7 +176,8 @@ def concat_multi_lines(f):
162
176
if lastline is not None :
163
177
maxprefix = 0
164
178
for i in xrange (min (len (line ), len (lastline ))):
165
- if line [i ] != lastline [i ]: break
179
+ if line [i ] != lastline [i ]:
180
+ break
166
181
maxprefix += 1
167
182
line = line [maxprefix :].lstrip ()
168
183
@@ -184,11 +199,14 @@ def concat_multi_lines(f):
184
199
(?P<cmd>[A-Za-z]+(?:-[A-Za-z]+)*)
185
200
(?P<args>.*)$
186
201
''' , re .X )
202
+
203
+
187
204
def get_commands (template ):
188
205
with open (template , 'rUb' ) as f :
189
206
for lineno , line in concat_multi_lines (f ):
190
207
m = LINE_PATTERN .search (line )
191
- if not m : continue
208
+ if not m :
209
+ continue
192
210
193
211
negated = (m .group ('negated' ) == '!' )
194
212
cmd = m .group ('cmd' )
@@ -198,17 +216,22 @@ def get_commands(template):
198
216
args = shlex .split (args )
199
217
yield Command (negated = negated , cmd = cmd , args = args , lineno = lineno + 1 )
200
218
219
+
201
220
def _flatten (node , acc ):
202
- if node .text : acc .append (node .text )
221
+ if node .text :
222
+ acc .append (node .text )
203
223
for e in node :
204
224
_flatten (e , acc )
205
- if e .tail : acc .append (e .tail )
225
+ if e .tail :
226
+ acc .append (e .tail )
227
+
206
228
207
229
def flatten (node ):
208
230
acc = []
209
231
_flatten (node , acc )
210
232
return '' .join (acc )
211
233
234
+
212
235
def normalize_xpath (path ):
213
236
if path .startswith ('//' ):
214
237
return '.' + path # avoid warnings
@@ -218,6 +241,7 @@ def normalize_xpath(path):
218
241
raise RuntimeError ('Non-absolute XPath is not supported due to \
219
242
the implementation issue.' )
220
243
244
+
221
245
class CachedFiles (object ):
222
246
def __init__ (self , root ):
223
247
self .root = root
@@ -267,6 +291,7 @@ def get_tree(self, path):
267
291
self .trees [path ] = tree
268
292
return self .trees [path ]
269
293
294
+
270
295
def check_string (data , pat , regexp ):
271
296
if not pat :
272
297
return True # special case a presence testing
@@ -277,6 +302,7 @@ def check_string(data, pat, regexp):
277
302
pat = ' ' .join (pat .split ())
278
303
return pat in data
279
304
305
+
280
306
def check_tree_attr (tree , path , attr , pat , regexp ):
281
307
path = normalize_xpath (path )
282
308
ret = False
@@ -287,9 +313,11 @@ def check_tree_attr(tree, path, attr, pat, regexp):
287
313
continue
288
314
else :
289
315
ret = check_string (value , pat , regexp )
290
- if ret : break
316
+ if ret :
317
+ break
291
318
return ret
292
319
320
+
293
321
def check_tree_text (tree , path , pat , regexp ):
294
322
path = normalize_xpath (path )
295
323
ret = False
@@ -300,9 +328,11 @@ def check_tree_text(tree, path, pat, regexp):
300
328
continue
301
329
else :
302
330
ret = check_string (value , pat , regexp )
303
- if ret : break
331
+ if ret :
332
+ break
304
333
return ret
305
334
335
+
306
336
def check (target , commands ):
307
337
cache = CachedFiles (target )
308
338
for c in commands :
@@ -323,7 +353,8 @@ def check(target, commands):
323
353
ret = check_tree_attr (cache .get_tree (c .args [0 ]), pat , attr , c .args [2 ], regexp )
324
354
else : # normalized text
325
355
pat = c .args [1 ]
326
- if pat .endswith ('/text()' ): pat = pat [:- 7 ]
356
+ if pat .endswith ('/text()' ):
357
+ pat = pat [:- 7 ]
327
358
ret = check_tree_text (cache .get_tree (c .args [0 ]), pat , c .args [2 ], regexp )
328
359
else :
329
360
raise RuntimeError ('Invalid number of @{} arguments \
@@ -348,4 +379,3 @@ def check(target, commands):
348
379
raise SystemExit (1 )
349
380
else :
350
381
check (sys .argv [1 ], get_commands (sys .argv [2 ]))
351
-
0 commit comments