awips2/deltaScripts/20.3.1/DR7887/py3tools/py2to3checker.sh
2022-05-05 12:34:50 -05:00

152 lines
5.7 KiB
Bash
Executable file

#!/bin/bash
# Python 2 to 3 compatibility checker script
#
# Author: tgurney
path="${1}"
if [[ "$1" == "" || "$2" != "" ]]; then
echo Usage: $0 FILE
echo Check for possible Python 2 to 3 compatibility issues in the specified
echo FILE. FILE may be a single Python file, or a directory. If a directory,
echo then all .py files in that directory tree will be checked.
echo
echo You will get best results by running 2to3 on your files before running
echo this, as this has little to no overlap with 2to3\'s fixers.
echo
echo Recommend dumping stdout and stderr of this script to a file for easier
echo review of the results.
echo
echo When checking for division operators, this script rejects Python files
echo that have invalid syntax. A traceback will be printed to stderr for
echo every such file. The division operator checking requires a python3
echo interpreter to exist on the \$PATH.
exit 1
fi
find_pattern() {
if [[ -d "${path}" ]]; then
find "${path}" -type f -regex ".*\.py$" -exec grep -ERHn "$1" '{}' \;
else
grep -ERHn "$1" "${path}"
fi
}
divscript=$(mktemp || exit 1)
cleanup_exit() {
rm -f "${divscript}"
exit 0
}
trap cleanup_exit SIGTERM
trap cleanup_exit SIGINT
cat > "${divscript}" << 'EOF'
from __future__ import print_function
import ast
import os
import sys
import traceback
def parse_file(path):
with open(path) as f:
contents = f.read()
tree = ast.parse(contents, os.path.abspath(path))
last_lineno = None
lines = None
for node in ast.walk(tree):
if hasattr(node, 'lineno'):
last_lineno = node.lineno
if isinstance(node, ast.Div):
if not lines:
lines = contents.split('\n')
print(path + ':' + str(last_lineno) + ':' + lines[last_lineno-1])
arg = sys.argv[1]
if os.path.isfile(arg):
try:
parse_file(arg)
except Exception as e:
sys.stderr.write(traceback.format_exc() + '\n')
sys.exit(0)
for root, _, filenames in os.walk(arg):
for filename in filenames:
if filename.endswith('.py'):
try:
parse_file(os.path.join(root, filename))
except Exception as e:
sys.stderr.write(traceback.format_exc() + '\n')
EOF
echo "----------------------------------------"
echo "Modules exceptions, sets, popen2 are gone -- rewrite your code to remove the use of these:"
echo
find_pattern "^\s*import.*[, ]\s*(exceptions|sets|popen2)"
find_pattern "^\s*from\s*(exceptions|sets|popen2)\s*import "
echo "----------------------------------------"
echo "Many methods have been removed from the string module -- rewrite your code to"
echo "remove the use of these. Most are now methods on str objects."
echo
find_pattern "[^A-Za-z0-9_]string\s*.\s*(ato[fil]|capitalize|expandtabs|r?find|r?index|count|lower|r?split|splitfields|join|joinfields|[lr]?strip|swapcase|translate|upper|[lr]just|center|zfill|replace)\s*\("
echo "----------------------------------------"
echo "Files that use the subprocess module -- make sure you are correctly"
echo "handling the bytes objects returned from subprocess methods:"
echo
find_pattern "^\s*from\s*subprocess\s*import "
find_pattern "[^A-Za-z0-9_]subprocess\s*[.]"
echo "----------------------------------------"
echo "Built-in 'file' type is gone. Use the 'open' built-in function instead:"
echo
find_pattern "[^A-Za-z0-9_]file\s*\("
echo "----------------------------------------"
echo "'Scientific' and 'pupynere' libraries have been removed. Replace with netcdf4:"
echo
find_pattern "^\s*import.*[, ]\s*(pupynere|scientific)"
find_pattern "^\s*from\s*(scientific|pupynere)\s*import "
echo "----------------------------------------"
echo "Uses of time.mktime require a tuple. Make sure the argument is a tuple:"
echo
find_pattern "^\s*from\s*time\s*import.*[, ]mktime"
find_pattern "time\s*[.]\s*mktime\("
echo "----------------------------------------"
echo "numpy.getbuffer is gone; replace with builtin memoryview:"
echo
find_pattern "^\s*from\s*numpy\s*import.*[, ]getbuffer"
find_pattern "(numpy|np)\s*[.]\s*getbuffer\("
echo "----------------------------------------"
echo "list.sort method uses key functions instead of comparison functions."
echo "Can use functools.cmp_to_key to assist in fixing these:"
echo
find_pattern "[.]\s*sort\s*\([^)]" | grep -Ev "[.]\s*sort\s*\(\s*(key|reverse)\s*="
echo "----------------------------------------"
echo "Builtin 'sorted' method uses key functions instead of comparison functions."
echo "Can use functools.cmp_to_key to assist in fixing these:"
echo
find_pattern "[^A-Za-z0-9_.]sorted\s*\(\s*.*?," | grep -Ev "sorted\s*\(.*?,\s*key\s*="
echo "----------------------------------------"
echo "types.FileType is gone, replace with io.IOBase:"
echo
find_pattern "^\s*from\s*types\s*import.*[, ]FileType"
find_pattern "types\s*[.]\s*FileType"
echo "----------------------------------------"
echo "pickle dump/load returns bytes, make sure you are handling them as bytes and not strings"
echo
find_pattern "from\s*(cP|p)ickle\s*import.*[, ](dumps?|loads?)"
find_pattern "(cP|p)ickle\s*.\s*(dumps?|loads?)\("
echo "----------------------------------------"
echo "Built-in 'exec' function behaves differently from the old 'exec' statement."
echo "You will have to make changes to your code, as exec'd code can no longer"
echo "modify the value of variables in the caller's namespace."
echo "See the migration guide for more details."
echo
find_pattern "[^A-Za-z0-9_]exec\s*\("
echo "----------------------------------------"
echo "Division operator usages - check these to make sure one or the other of"
echo "the operands is a float, and if not, change to //"
echo
python3 ${divscript} ${path}
cleanup_exit