Skip to content

Instantly share code, notes, and snippets.

@LewisGaul
Last active April 23, 2021 11:43
Show Gist options
  • Save LewisGaul/1b332d26a0dd5051cb374faab81e9e90 to your computer and use it in GitHub Desktop.
Save LewisGaul/1b332d26a0dd5051cb374faab81e9e90 to your computer and use it in GitHub Desktop.
Check which versions a Python file is valid syntax for
#!/bin/bash
#
# Script to check which versions of python a file is valid syntax for.
#
# The intention is to catch things like 'print' as a keyword (python2), type
# hints, f-strings or async that aren't valid on 3.5, or other new syntax such
# as pattern matching.
#
# This can be useful for identifying python2 files, and should give zero false
# positives for incompatibilities. However, it won't catch things like misssing
# imports or missing builtins, so it may be worth also searching for things
# like 'xrange' or using 'pylint --errors-only'.
#
# This uses a hardcoded array of Python executables 'PYTHON_EXES' to check
# against, which may need changing.
#
set -e
shopt -s globstar
PYTHON_EXES=(
"$HOME/venv27/bin/python"
"$HOME/venv35/bin/python3"
"$HOME/venv36/bin/python3"
"$HOME/venv37/bin/python3"
"$HOME/venv38/bin/python3"
"$HOME/venv39/bin/python3"
)
usage () {
echo -n "\
Usage: $(basename "$0") [-h|--help] [FILE|DIR ...]
Wrapper around 'python3 -m py_compile' to check for invalid syntax for
different python versions.
"
exit 0
}
get_python_version () {
"$1" --version 2>&1 | grep -o -E "[2-4].[0-9]+"
}
CHECK_FILES=()
while [[ $# -gt 0 ]]; do
case "$1" in
-h | --help )
usage
;;
*)
for path in "$@"; do
if [[ -f $path ]]; then
CHECK_FILES+=("$path")
else
py_files=(${path%/}/**/*.py)
if [[ -f ${py_files[0]} ]]; then
CHECK_FILES+=("${py_files[@]}")
fi
fi
done
break
;;
esac
done
if [[ ${#CHECK_FILES[@]} == 0 ]]; then
CHECK_FILES=(./**/*.py)
fi
echo "Proceeding to check ${#CHECK_FILES[@]} files with ${#PYTHON_EXES[@]} python exes" >&2
for python_exe in "${PYTHON_EXES[@]}"; do
python_version=$(get_python_version "$python_exe")
rm "errors-$python_version.txt" 2>/dev/null || true
rm "incompatible_files-$python_version.txt" 2>/dev/null || true
touch "errors-$python_version.txt"
touch "incompatible_files-$python_version.txt"
done
for file in "${CHECK_FILES[@]}"; do
echo
echo "$file"
for python_exe in "${PYTHON_EXES[@]}"; do
python_version=$(get_python_version "$python_exe")
errors_file="errors-$python_version.txt"
list_file="incompatible_files-$python_version.txt"
if "$python_exe" -m py_compile "$file" 2>> "$errors_file"; then
echo -n " $python_version"
# break # May wish to break as soon as a valid version is found
else
echo "$file" >> "$list_file"
fi
done
echo
done
echo
echo "See created files 'incompatible_files-<version>.txt' and 'errors-<version>.txt'" >&2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment