From 1ab5214e0cf9a25ca30292b409f60e739a72d5e9 Mon Sep 17 00:00:00 2001 From: Christian Herdtweck Date: Thu, 8 Aug 2019 11:53:48 +0200 Subject: [PATCH] Bring back source check script Old script was deleted a few commits ago for version 1.5 (commit 81f7f6c7a16bc5e63228996cedf8ac83f45f3717). This is a modified version from i2n avocado source. --- pycheck.sh | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 245 insertions(+), 0 deletions(-) create mode 100755 pycheck.sh diff --git a/pycheck.sh b/pycheck.sh new file mode 100755 index 0000000..6ac2594 --- /dev/null +++ b/pycheck.sh @@ -0,0 +1,245 @@ +#!/bin/bash +set -u + +# pycheck.sh +# check a python source code file using +# pylint, pycodestyle (former pep8) and pydocstyle + +# debug mode: +#set -x + +readonly base_dir="$(realpath -m $(dirname $0))" +readonly src_dir=$base_dir/src +readonly test_dir=$base_dir/test + +readonly pylint_init_hook=" +import sys; +sys.path.insert(0, 'base_dir'); +" +readonly codestyle_ignores="E501,W503,E226,E265" +readonly docstyle_ignores="D212,D205,D400,D401,D203,D105,D301,D302" + +usage_print_msg () +{ + printf "usage: %s --all { FILENAME | --branch NAME }\n" "$0" + printf "\n" + printf "\twhere\n" + printf "\t\tFILENAME file to check (expects the basename\n" + printf "\t\t without suffix)\n" + printf "\t\t--all enable all warnings\n" + printf "\t\t--branch NAME a git branch to check changed files\n" + printf "\t\t against\n" + printf "\n" +} + +usage () { + local out=1 + case "${1:-stdout}" in + 2|err|stderr) out=2 ;; + esac + usage_print_msg >&"${out}"; +} + +# check params +pylint_Wall=no +lookup_mode=file +target_name="" +while : ; do + arg="${1:-}" + case "${arg}" in + "") break ;; + --help) + usage + exit 0 + ;; + --all) + echo "Enabling all pylint warnings" + pylint_Wall=yes + ;; + --branch) + shift + arg="${1:-}" + if [ -z "${arg}" ]; then + printf "ERROR: --branch specified but no name given\n\n" >&2 + usage err + exit 1 + fi + target_name="${arg}" + lookup_mode=branch + ;; + *) + if [ -z "${target_name}" ]; then + target_name="${arg%%.py}" + else + printf "ERROR: filename ${target_name} already specified\n" >&2 + printf "ERROR: please specify a single file name to check\n\n" >&2 + usage err + exit 1 + fi + ;; + esac + shift +done + +if [ -z "${target_name}" ]; then + printf "ERROR: no file name or branch specified\n\n" >&2 + usage err + exit 1 +fi + +# check for presence of pylint-3 +pylint-3 --version +if [ $? = 0 ]; then + pylint_bin=pylint-3 +else + pylint --version + pylint_bin=pylint +fi + +function has_errors { + local found_errors=no + # append extra rules passed to the function + codestyle_all_ignores="$codestyle_ignores,${2:-}" + + # run pylint + local pylint_flags=(-E) + if [ "${pylint_Wall}" = yes ]; then + pylint_flags=(--disable=logging-format-interpolation \ + --variable-rgx='(?:[[a-z_][a-z0-9_]{2,30}$|vm)') + fi + echo "pylint:" + if ! ${pylint_bin} ${pylint_flags[@]} --reports=no --init-hook="$pylint_init_hook" "$1"; then + found_errors=yes + fi + echo + + # run pycodestyle, former pep8 (or fall back to pep8 if not available) + style_checker="pycodestyle" + command -v $style_checker >/dev/null 2>&1 || style_checker=pep8 + + echo "pycodestyle|pep8:" + if ! $style_checker "$1" --ignore="$codestyle_all_ignores"; then + found_errors=yes + fi + echo + + # run pydocstyle + echo "pydocstyle:" + if ! pydocstyle "$1" --ignore=$docstyle_ignores; then + found_errors=yes + fi + echo + + [ "${found_errors}" = yes ] +} + +declare -a found=() + +valid_branch () +{ + local needle="${1:-master}" + git rev-parse --verify "${needle}" &>/dev/null +} + +check_to_branch () +{ + local br="${1:-master}" + + if ! valid_branch ${br}; then + printf "ERROR: branch “%s” not known to Git\n\n" "${br}" + usage err + exit 1 + fi + + local allfiles=( $(git diff --name-only --diff-filter=d "${br}") ) + local pyfiles=( ) + for f in ${allfiles[@]}; do + if [[ "$f" =~ \.py$ ]]; then pyfiles+=( "$f" ); fi + done + if [ "${#pyfiles[@]}" -eq 0 ]; then + printf "ERROR: no Python files (*.py) changed between HEAD and\n" + printf "ERROR: asked branch “%s”\n\n" "${br}" + printf "ERROR: %d files changed:\n" ${#allfiles[@]} + for f in ${allfiles[@]}; do + printf "\t\t× %s\n" "$f" + done + printf "\n" + usage err + exit 1 + fi + + printf "checking %d files for tiny and understandable mistakes\n" ${#pyfiles[@]} + local i=0 + for py in ${pyfiles[@]}; do + printf "[%d/%d] checking %s\n" $(( ++i )) ${#pyfiles[@]} "${py}" + if has_errors "${py}"; then + found+=( "${py}→BAD" ) + else + found+=( "${py}→GOOD" ) + fi + done +} + +case "${lookup_mode}" in + file) + files=$(find $base_dir \( -path "${src_dir}/*" -name "${target_name}.py" \) \ + -or \( -path "${test_dir}/*" -name "${target_name}.py" \) ) + + for f in $files; do + echo "Found file ${f}" + if has_errors "${f}"; then + found+=( "${f}→BAD" ) + else + found+=( "${f}→GOOD" ) + fi + done + + # error if not found as utility either + if [ "${#found[@]}" -eq 0 ]; then + echo "Could not find ${target_name} in src/test dirs!" + echo + usage stderr + exit 2 + fi + ;; + branch) + check_to_branch "${target_name}" + ;; + *) + # can’t happen + printf "ERROR: internal error\n" + printf "ERROR: please run “git blame \"%s\"” to determine who" "$0" + printf "ERROR: is responsible for this mess\n\n" + exit 1 +esac + +declare -r wd=$(tput cols) + +printf "\n" +printf "·%.0s" $(seq 1 ${wd}) +printf "\n\n" +printf "$0 completed sucessfully\n" +i=0 +good=0 +for result in ${found[@]} ; do + (( ++i )) + old_IFS="${IFS}" + IFS=→ + read file verdict <<<"${result}" + IFS="${old_IFS}" + if [ "${verdict}" = GOOD ]; then + (( ++good )) + fi + printf "\t%d : %s\t→ %s\n" "$i" "${file}" "${verdict}" +done +printf "\n" +printf "summary: %d files inspected, %d tested good, %d bad\n\n" \ + $i ${good} $(( i - good )) + +printf "·%.0s" $(seq 1 ${wd}) +printf "\n\n" + +if [ ${good} -ne $i ]; then + exit 1 +fi + -- 1.7.1