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
|
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Codetags finder
~~~~~~~~~~~~~~~
Find code tags in specified files and/or directories
and create a report in HTML format.
:copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys, os, re
import getopt
from os.path import join, abspath, isdir, isfile
TAGS = set(('XXX', 'TODO', 'FIXME', 'HACK'))
tag_re = re.compile(
r'(?P<tag>\b' + r'\b|\b'.join(TAGS) + r'\b)\s*'
r'(?: \( (?P<who> .*? ) \) )?'
r'\s*:?\s* (?P<what> .*? ) \s* $',
re.X)
binary_re = re.compile('[\x00-\x06\x0E-\x1F]')
def escape_html(text):
return text.replace('&', '&'). \
replace('<', '<'). \
replace('>', '>'). \
replace('"', '"')
def process_file(store, filename):
try:
f = open(filename, 'r')
except (IOError, OSError):
return False
llmatch = 0
try:
for lno, line in enumerate(f):
# just some random heuristics to filter out binary files
if lno < 100 and binary_re.search(line):
return False
m = tag_re.search(line)
if m:
store.setdefault(filename, []).append({
'lno': lno+1,
'tag': m.group('tag'),
'who': m.group('who') or '',
'what': escape_html(m.group('what')),
})
# 'what' cannot start at column 0
llmatch = m.start('what')
elif llmatch:
# continuation lines
# XXX: this is Python centric, doesn't work for
# JavaScript, for example.
if line[:llmatch].replace('#', '').isspace():
cont = line[llmatch:].strip()
if cont:
store[filename][-1]['what'] += ' ' + escape_html(cont)
continue
llmatch = 0
return True
finally:
f.close()
def main():
try:
gopts, args = getopt.getopt(sys.argv[1:], "vo:i:")
except getopt.GetoptError:
print ("Usage: %s [-v] [-i ignoredir]* [-o reportfile.html] "
"path ..." % sys.argv[0])
return 2
opts = {}
for opt, val in gopts:
if opt == '-i':
val = abspath(val)
opts.setdefault(opt, []).append(val)
if not args:
args = ['.']
if '-o' in opts:
output = abspath(opts['-o'][-1])
else:
output = abspath('tags.html')
verbose = '-v' in opts
store = {}
gnum = 0
num = 0
for path in args:
print "Searching for code tags in %s, please wait." % path
if isfile(path):
gnum += 1
if process_file(store, path):
if verbose:
print path + ": found %d tags" % \
(path in store and len(store[path]) or 0)
num += 1
else:
if verbose:
print path + ": binary or not readable"
continue
elif not isdir(path):
continue
for root, dirs, files in os.walk(path):
if '-i' in opts and abspath(root) in opts['-i']:
del dirs[:]
continue
if '.svn' in dirs:
dirs.remove('.svn')
for fn in files:
gnum += 1
if gnum % 50 == 0 and not verbose:
sys.stdout.write('.')
sys.stdout.flush()
fn = join(root, fn)
if fn.endswith('.pyc') or fn.endswith('.pyo'):
continue
elif '-i' in opts and abspath(fn) in opts['-i']:
continue
elif abspath(fn) == output:
continue
if fn[:2] == './': fn = fn[2:]
if process_file(store, fn):
if verbose:
print fn + ": found %d tags" % \
(fn in store and len(store[fn]) or 0)
num += 1
else:
if verbose:
print fn + ": binary or not readable"
print
print "Processed %d of %d files. Found %d tags in %d files." % (
num, gnum, sum(len(fitem) for fitem in store.itervalues()), len(store))
if not store:
return 0
HTML = '''\
<html>
<head>
<title>Code tags report</title>
<style type="text/css">
body { font-family: Trebuchet MS,Verdana,sans-serif;
width: 80%%; margin-left: auto; margin-right: auto; }
table { width: 100%%; border-spacing: 0;
border: 1px solid #CCC; }
th { font-weight: bold; background-color: #DDD }
td { padding: 2px 5px 2px 5px;
vertical-align: top; }
.tr0 { background-color: #EEEEEE; }
.tr1 { background-color: #F6F6F6; }
.tag { text-align: center; font-weight: bold; }
.tr0 .tag { background-color: #FFEEEE; }
.tr1 .tag { background-color: #FFDDDD; }
.head { padding-top: 10px; font-size: 100%%; font-weight: bold }
.XXX { color: #500; }
.FIXME { color: red; }
.TODO { color: #880; }
</style>
</head>
<body>
<h1>Code tags report for %s</h1>
<table>
<tr><th>Line</th><th>Tag</th><th>Who</th><th>Description</th></tr>
%s
</table>
</body>
</html>
'''
TABLE = '\n<tr><td class="head" colspan="4">File: %s</td>\n'
TR = ('<tr class="tr%d"><td class="lno">%%(lno)d</td>'
'<td class="tag %%(tag)s">%%(tag)s</td>'
'<td class="who">%%(who)s</td><td class="what">%%(what)s</td></tr>')
f = file(output, 'w')
table = '\n'.join(TABLE % fname +
'\n'.join(TR % (no % 2,) % entry
for no, entry in enumerate(store[fname]))
for fname in sorted(store))
f.write(HTML % (', '.join(map(abspath, args)), table))
f.close()
print "Report written to %s." % output
return 0
if __name__ == '__main__':
sys.exit(main())
|