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
|
#! /usr/bin/env python
"""fixdiv - tool to fix division operators.
To use this tool, first run `python -Dwarn yourscript.py 2>warnings'.
This runs the script `yourscript.py' while writing warning messages
about all uses of the classic division operator to the file
`warnings'. (The warnings are written to stderr, so you must use `2>'
for the I/O redirect. I don't yet know how to do this on Windows.)
Then run `python fixdiv.py warnings'. This first reads the warnings,
looking for classic division warnings, and sorts them by file name and
line number. Then, for each file that received at least one warning,
it parses the file and tries to match the warnings up to the division
operators found in the source code. If it is successful, it writes a
recommendation to stdout in the form of a context diff. If it is not
successful, it writes recommendations to stdout instead.
"""
import sys
import getopt
import re
import tokenize
from pprint import pprint
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "h")
except getopt.error, msg:
usage(2, msg)
for o, a in opts:
if o == "-h":
help()
if not args:
usage(2, "at least one file argument is required")
if args[1:]:
sys.stderr.write("%s: extra file arguments ignored\n", sys.argv[0])
readwarnings(args[0])
def usage(exit, msg=None):
if msg:
sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
sys.stderr.write("Usage: %s warnings\n" % sys.argv[0])
sys.stderr.write("Try `%s -h' for more information.\n" % sys.argv[0])
sys.exit(exit)
def help():
print __doc__
sys.exit(0)
def readwarnings(warningsfile):
pat = re.compile(
"^(.+?):(\d+): DeprecationWarning: classic ([a-z]+) division$")
try:
f = open(warningsfile)
except IOError, msg:
sys.stderr.write("can't open: %s\n" % msg)
return
warnings = {}
while 1:
line = f.readline()
if not line:
break
m = pat.match(line)
if not m:
if line.find("division") >= 0:
sys.stderr.write("Warning: ignored input " + line)
continue
file, lineno, what = m.groups()
list = warnings.get(file)
if list is None:
warnings[file] = list = []
list.append((int(lineno), intern(what)))
f.close()
files = warnings.keys()
files.sort()
for file in files:
process(file, warnings[file])
def process(file, list):
print "-"*70
if not list:
sys.stderr.write("no division warnings for %s\n" % file)
return
try:
fp = open(file)
except IOError, msg:
sys.stderr.write("can't open: %s\n" % msg)
return
print "Processing:", file
f = FileContext(fp)
list.sort()
index = 0 # list[:index] has been processed, list[index:] is still to do
orphans = [] # subset of list for which no / operator was found
unknown = [] # lines with / operators for which no warnings were seen
g = tokenize.generate_tokens(f.readline)
while 1:
startlineno, endlineno, slashes = lineinfo = scanline(g)
if startlineno is None:
break
assert startlineno <= endlineno is not None
while index < len(list) and list[index][0] < startlineno:
orphans.append(list[index])
index += 1
warnings = []
while index < len(list) and list[index][0] <= endlineno:
warnings.append(list[index])
index += 1
if not slashes and not warnings:
pass
elif slashes and not warnings:
report(slashes, "Unexecuted code")
elif warnings and not slashes:
reportphantomwarnings(warnings, f)
else:
if len(slashes) > 1:
report(slashes, "More than one / operator")
else:
(row, col), line = slashes[0]
line = chop(line)
if line[col:col+1] != "/":
print "*** Can't find the / operator in line %d:" % row
print "*", line
continue
intlong = []
floatcomplex = []
bad = []
for lineno, what in warnings:
if what in ("int", "long"):
intlong.append(what)
elif what in ("float", "complex"):
floatcomplex.append(what)
else:
bad.append(what)
if bad:
print "*** Bad warning for line %d:" % row, bad
print "*", line
elif intlong and not floatcomplex:
print "%dc%d" % (row, row)
print "<", line
print "---"
print ">", line[:col] + "/" + line[col:]
elif floatcomplex and not intlong:
print "True division / operator at line %d:" % row
print "=", line
fp.close()
def reportphantomwarnings(warnings, f):
blocks = []
lastrow = None
lastblock = None
for row, what in warnings:
if row != lastrow:
lastblock = [row]
blocks.append(lastblock)
lastblock.append(what)
for block in blocks:
row = block[0]
whats = "/".join(block[1:])
print "*** Phantom %s warnings for line %d:" % (whats, row)
f.report(row, mark="*")
def report(slashes, message):
lastrow = None
for (row, col), line in slashes:
if row != lastrow:
print "*** %s on line %d:" % (message, row)
print "*", chop(line)
lastrow = row
class FileContext:
def __init__(self, fp, window=5, lineno=1):
self.fp = fp
self.window = 5
self.lineno = 1
self.eoflookahead = 0
self.lookahead = []
self.buffer = []
def fill(self):
while len(self.lookahead) < self.window and not self.eoflookahead:
line = self.fp.readline()
if not line:
self.eoflookahead = 1
break
self.lookahead.append(line)
def readline(self):
self.fill()
if not self.lookahead:
return ""
line = self.lookahead.pop(0)
self.buffer.append(line)
self.lineno += 1
return line
def truncate(self):
del self.buffer[-window:]
def __getitem__(self, index):
self.fill()
bufstart = self.lineno - len(self.buffer)
lookend = self.lineno + len(self.lookahead)
if bufstart <= index < self.lineno:
return self.buffer[index - bufstart]
if self.lineno <= index < lookend:
return self.lookahead[index - self.lineno]
raise KeyError
def report(self, first, last=None, mark="*"):
if last is None:
last = first
for i in range(first, last+1):
try:
line = self[first]
except KeyError:
line = "<missing line>"
print mark, chop(line)
def scanline(g):
slashes = []
startlineno = None
endlineno = None
for type, token, start, end, line in g:
endlineno = end[0]
if startlineno is None:
startlineno = endlineno
if token in ("/", "/="):
slashes.append((start, line))
## if type in (tokenize.NEWLINE, tokenize.NL, tokenize.COMMENT):
if type == tokenize.NEWLINE:
break
return startlineno, endlineno, slashes
def chop(line):
if line.endswith("\n"):
return line[:-1]
else:
return line
if __name__ == "__main__":
main()
|