#! /usr/bin/env python3"""fixdiv - tool to fix division operators.To use this tool, first run `python -Qwarnall yourscript.py 2>warnings'.This runs the script `yourscript.py' while writing warning messagesabout all uses of the classic division operator to the file`warnings'. The warnings look like this:<file>:<line>: DeprecationWarning: classic <type> divisionThe warnings are written to stderr, so you must use `2>' for the I/Oredirect. I know of no way to redirect stderr on Windows in a DOSbox, so you will have to modify the script to set sys.stderr to somekind of log file if you want to do this on Windows.The warnings are not limited to the script; modules imported by thescript may also trigger warnings. In fact a useful technique is towrite a test script specifically intended to exercise all code in aparticular module or set of modules.Then run `python fixdiv.py warnings'. This first reads the warnings,looking for classic division warnings, and sorts them by file name andline number. Then, for each file that received at least one warning,it parses the file and tries to match the warnings up to the divisionoperators found in the source code. If it is successful, it writesits findings to stdout, preceded by a line of dashes and a line of theform:Index: <file>If the only findings found are suggestions to change a / operator intoa // operator, the output is acceptable input for the Unix 'patch'program.Here are the possible messages on stdout (N stands for a line number):- A plain-diff-style change ('NcN', a line marked by '<', a linecontaining '---', and a line marked by '>'):A / operator was found that should be changed to //. This is therecommendation when only int and/or long arguments were seen.- 'True division / operator at line N' and a line marked by '=':A / operator was found that can remain unchanged. This is therecommendation when only float and/or complex arguments were seen.- 'Ambiguous / operator (..., ...) at line N', line marked by '?':A / operator was found for which int or long as well as float orcomplex arguments were seen. This is highly unlikely; if it occurs,you may have to restructure the code to keep the classic semantics,or maybe you don't care about the classic semantics.- 'No conclusive evidence on line N', line marked by '*':A / operator was found for which no warnings were seen. This couldbe code that was never executed, or code that was only executedwith user-defined objects as arguments. You will have toinvestigate further. Note that // can be overloaded separately from/, using __floordiv__. True division can also be separatelyoverloaded, using __truediv__. Classic division should be the sameas either of those. (XXX should I add a warning for division onuser-defined objects, to disambiguate this case from code that wasnever executed?)- 'Phantom ... warnings for line N', line marked by '*':A warning was seen for a line not containing a / operator. The mostlikely cause is a warning about code executed by 'exec' or eval()(see note below), or an indirect invocation of the / operator, forexample via the div() function in the operator module. It couldalso be caused by a change to the file between the time the testscript was run to collect warnings and the time fixdiv was run.- 'More than one / operator in line N'; or'More than one / operator per statement in lines N-N':The scanner found more than one / operator on a single line, or in astatement split across multiple lines. Because the warningsframework doesn't (and can't) show the offset within the line, andthe code generator doesn't always give the correct line number foroperations in a multi-line statement, we can't be sure whether alloperators in the statement were executed. To be on the safe side,by default a warning is issued about this case. In practice, thesecases are usually safe, and the -m option suppresses these warning.- 'Can't find the / operator in line N', line marked by '*':This really shouldn't happen. It means that the tokenize modulereported a '/' operator but the line it returns didn't contain a '/'character at the indicated position.- 'Bad warning for line N: XYZ', line marked by '*':This really shouldn't happen. It means that a 'classic XYZdivision' warning was read with XYZ being something other than'int', 'long', 'float', or 'complex'.Notes:- The augmented assignment operator /= is handled the same way as the/ operator.- This tool never looks at the // operator; no warnings are evergenerated for use of this operator.- This tool never looks at the / operator when a future divisionstatement is in effect; no warnings are generated in this case, andbecause the tool only looks at files for which at least one classicdivision warning was seen, it will never look at files containing afuture division statement.- Warnings may be issued for code not read from a file, but executedusing the exec() or eval() functions. These may have<string> in the filename position, in which case the fixdiv scriptwill attempt and fail to open a file named '<string>' and issue awarning about this failure; or these may be reported as 'Phantom'warnings (see above). You're on your own to deal with these. Youcould make all recommended changes and add a future divisionstatement to all affected files, and then re-run the test script; itshould not issue any warnings. If there are any, and you have ahard time tracking down where they are generated, you can use the-Werror option to force an error instead of a first warning,generating a traceback.- The tool should be run from the same directory as that from whichthe original script was run, otherwise it won't be able to openfiles given by relative pathnames."""import sysimport getoptimport reimport tokenizemulti_ok = 0def main():try:opts, args = getopt.getopt(sys.argv[1:], "hm")except getopt.error as msg:usage(msg)return 2for o, a in opts:if o == "-h":print(__doc__)returnif o == "-m":global multi_okmulti_ok = 1if not args:usage("at least one file argument is required")return 2if args[1:]:sys.stderr.write("%s: extra file arguments ignored\n", sys.argv[0])warnings = readwarnings(args[0])if warnings is None:return 1files = list(warnings.keys())if not files:print("No classic division warnings read from", args[0])returnfiles.sort()exit = Nonefor filename in files:x = process(filename, warnings[filename])exit = exit or xreturn exitdef usage(msg):sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))sys.stderr.write("Usage: %s [-m] warnings\n" % sys.argv[0])sys.stderr.write("Try `%s -h' for more information.\n" % sys.argv[0])PATTERN = (r"^(.+?):(\d+): DeprecationWarning: "r"classic (int|long|float|complex) division$")def readwarnings(warningsfile):prog = re.compile(PATTERN)try:f = open(warningsfile)except IOError as msg:sys.stderr.write("can't open: %s\n" % msg)returnwarnings = {}while 1:line = f.readline()if not line:breakm = prog.match(line)if not m:if line.find("division") >= 0:sys.stderr.write("Warning: ignored input " + line)continuefilename, lineno, what = m.groups()list = warnings.get(filename)if list is None:warnings[filename] = list = []list.append((int(lineno), sys.intern(what)))f.close()return warningsdef process(filename, list):print("-"*70)assert list # if this fails, readwarnings() is brokentry:fp = open(filename)except IOError as msg:sys.stderr.write("can't open: %s\n" % msg)return 1print("Index:", filename)f = FileContext(fp)list.sort()index = 0 # list[:index] has been processed, list[index:] is still to dog = tokenize.generate_tokens(f.readline)while 1:startlineno, endlineno, slashes = lineinfo = scanline(g)if startlineno is None:breakassert startlineno <= endlineno is not Noneorphans = []while index < len(list) and list[index][0] < startlineno:orphans.append(list[index])index += 1if orphans:reportphantomwarnings(orphans, f)warnings = []while index < len(list) and list[index][0] <= endlineno:warnings.append(list[index])index += 1if not slashes and not warnings:passelif slashes and not warnings:report(slashes, "No conclusive evidence")elif warnings and not slashes:reportphantomwarnings(warnings, f)else:if len(slashes) > 1:if not multi_ok:rows = []lastrow = Nonefor (row, col), line in slashes:if row == lastrow:continuerows.append(row)lastrow = rowassert rowsif len(rows) == 1:print("*** More than one / operator in line", rows[0])else:print("*** More than one / operator per statement", end=' ')print("in lines %d-%d" % (rows[0], rows[-1]))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)lastrow = Nonefor (row, col), line in slashes:if row == lastrow:continuelastrow = rowline = chop(line)if line[col:col+1] != "/":print("*** Can't find the / operator in line %d:" % row)print("*", line)continueif 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)elif intlong and floatcomplex:print("*** Ambiguous / operator (%s, %s) at line %d:" % ("|".join(intlong), "|".join(floatcomplex), row))print("?", line)fp.close()def reportphantomwarnings(warnings, f):blocks = []lastrow = Nonelastblock = Nonefor 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 = Nonefor (row, col), line in slashes:if row != lastrow:print("*** %s on line %d:" % (message, row))print("*", chop(line))lastrow = rowclass FileContext:def __init__(self, fp, window=5, lineno=1):self.fp = fpself.window = 5self.lineno = 1self.eoflookahead = 0self.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 = 1breakself.lookahead.append(line)def readline(self):self.fill()if not self.lookahead:return ""line = self.lookahead.pop(0)self.buffer.append(line)self.lineno += 1return linedef __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 KeyErrordef report(self, first, last=None, mark="*"):if last is None:last = firstfor i in range(first, last+1):try:line = self[first]except KeyError:line = "<missing line>"print(mark, chop(line))def scanline(g):slashes = []startlineno = Noneendlineno = Nonefor type, token, start, end, line in g:endlineno = end[0]if startlineno is None:startlineno = endlinenoif token in ("/", "/="):slashes.append((start, line))if type == tokenize.NEWLINE:breakreturn startlineno, endlineno, slashesdef chop(line):if line.endswith("\n"):return line[:-1]else:return lineif __name__ == "__main__":sys.exit(main())
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。