summaryrefslogtreecommitdiffstats
path: root/AppPkg/Applications/Python/Python-2.7.2/Tools/scripts/fixdiv.py
diff options
context:
space:
mode:
Diffstat (limited to 'AppPkg/Applications/Python/Python-2.7.2/Tools/scripts/fixdiv.py')
-rw-r--r--AppPkg/Applications/Python/Python-2.7.2/Tools/scripts/fixdiv.py380
1 files changed, 380 insertions, 0 deletions
diff --git a/AppPkg/Applications/Python/Python-2.7.2/Tools/scripts/fixdiv.py b/AppPkg/Applications/Python/Python-2.7.2/Tools/scripts/fixdiv.py
new file mode 100644
index 0000000000..c10f59dc88
--- /dev/null
+++ b/AppPkg/Applications/Python/Python-2.7.2/Tools/scripts/fixdiv.py
@@ -0,0 +1,380 @@
+#! /usr/bin/env python
+
+"""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 messages
+about all uses of the classic division operator to the file
+`warnings'. The warnings look like this:
+
+ <file>:<line>: DeprecationWarning: classic <type> division
+
+The warnings are written to stderr, so you must use `2>' for the I/O
+redirect. I know of no way to redirect stderr on Windows in a DOS
+box, so you will have to modify the script to set sys.stderr to some
+kind of log file if you want to do this on Windows.
+
+The warnings are not limited to the script; modules imported by the
+script may also trigger warnings. In fact a useful technique is to
+write a test script specifically intended to exercise all code in a
+particular 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 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
+its findings to stdout, preceded by a line of dashes and a line of the
+form:
+
+ Index: <file>
+
+If the only findings found are suggestions to change a / operator into
+a // 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 line
+ containing '---', and a line marked by '>'):
+
+ A / operator was found that should be changed to //. This is the
+ recommendation 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 the
+ recommendation 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 or
+ complex 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 could
+ be code that was never executed, or code that was only executed
+ with user-defined objects as arguments. You will have to
+ investigate further. Note that // can be overloaded separately from
+ /, using __floordiv__. True division can also be separately
+ overloaded, using __truediv__. Classic division should be the same
+ as either of those. (XXX should I add a warning for division on
+ user-defined objects, to disambiguate this case from code that was
+ never executed?)
+
+- 'Phantom ... warnings for line N', line marked by '*':
+
+ A warning was seen for a line not containing a / operator. The most
+ likely cause is a warning about code executed by 'exec' or eval()
+ (see note below), or an indirect invocation of the / operator, for
+ example via the div() function in the operator module. It could
+ also be caused by a change to the file between the time the test
+ script 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 a
+ statement split across multiple lines. Because the warnings
+ framework doesn't (and can't) show the offset within the line, and
+ the code generator doesn't always give the correct line number for
+ operations in a multi-line statement, we can't be sure whether all
+ operators in the statement were executed. To be on the safe side,
+ by default a warning is issued about this case. In practice, these
+ cases 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 module
+ reported 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 XYZ
+ division' 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 ever
+ generated for use of this operator.
+
+- This tool never looks at the / operator when a future division
+ statement is in effect; no warnings are generated in this case, and
+ because the tool only looks at files for which at least one classic
+ division warning was seen, it will never look at files containing a
+ future division statement.
+
+- Warnings may be issued for code not read from a file, but executed
+ using an exec statement or the eval() function. These may have
+ <string> in the filename position, in which case the fixdiv script
+ will attempt and fail to open a file named '<string>' and issue a
+ warning about this failure; or these may be reported as 'Phantom'
+ warnings (see above). You're on your own to deal with these. You
+ could make all recommended changes and add a future division
+ statement to all affected files, and then re-run the test script; it
+ should not issue any warnings. If there are any, and you have a
+ hard 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 which
+ the original script was run, otherwise it won't be able to open
+ files given by relative pathnames.
+"""
+
+import sys
+import getopt
+import re
+import tokenize
+
+multi_ok = 0
+
+def main():
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "hm")
+ except getopt.error, msg:
+ usage(msg)
+ return 2
+ for o, a in opts:
+ if o == "-h":
+ print __doc__
+ return
+ if o == "-m":
+ global multi_ok
+ multi_ok = 1
+ if not args:
+ usage("at least one file argument is required")
+ return 2
+ if args[1:]:
+ sys.stderr.write("%s: extra file arguments ignored\n", sys.argv[0])
+ warnings = readwarnings(args[0])
+ if warnings is None:
+ return 1
+ files = warnings.keys()
+ if not files:
+ print "No classic division warnings read from", args[0]
+ return
+ files.sort()
+ exit = None
+ for filename in files:
+ x = process(filename, warnings[filename])
+ exit = exit or x
+ return exit
+
+def 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 = ("^(.+?):(\d+): DeprecationWarning: "
+ "classic (int|long|float|complex) division$")
+
+def readwarnings(warningsfile):
+ prog = re.compile(PATTERN)
+ 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 = prog.match(line)
+ if not m:
+ if line.find("division") >= 0:
+ sys.stderr.write("Warning: ignored input " + line)
+ continue
+ filename, lineno, what = m.groups()
+ list = warnings.get(filename)
+ if list is None:
+ warnings[filename] = list = []
+ list.append((int(lineno), intern(what)))
+ f.close()
+ return warnings
+
+def process(filename, list):
+ print "-"*70
+ assert list # if this fails, readwarnings() is broken
+ try:
+ fp = open(filename)
+ except IOError, msg:
+ sys.stderr.write("can't open: %s\n" % msg)
+ return 1
+ print "Index:", filename
+ f = FileContext(fp)
+ list.sort()
+ index = 0 # list[:index] has been processed, list[index:] is still to do
+ 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
+ orphans = []
+ while index < len(list) and list[index][0] < startlineno:
+ orphans.append(list[index])
+ index += 1
+ if orphans:
+ reportphantomwarnings(orphans, f)
+ 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, "No conclusive evidence")
+ elif warnings and not slashes:
+ reportphantomwarnings(warnings, f)
+ else:
+ if len(slashes) > 1:
+ if not multi_ok:
+ rows = []
+ lastrow = None
+ for (row, col), line in slashes:
+ if row == lastrow:
+ continue
+ rows.append(row)
+ lastrow = row
+ assert rows
+ if len(rows) == 1:
+ print "*** More than one / operator in line", rows[0]
+ else:
+ print "*** More than one / operator per statement",
+ 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 = None
+ for (row, col), line in slashes:
+ if row == lastrow:
+ continue
+ lastrow = row
+ line = chop(line)
+ if line[col:col+1] != "/":
+ print "*** Can't find the / operator in line %d:" % row
+ print "*", line
+ continue
+ 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
+ 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 = 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 == tokenize.NEWLINE:
+ break
+ return startlineno, endlineno, slashes
+
+def chop(line):
+ if line.endswith("\n"):
+ return line[:-1]
+ else:
+ return line
+
+if __name__ == "__main__":
+ sys.exit(main())