| #!/usr/bin/env bash |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| # SHELLDOC-IGNORE |
| |
| add_test_type pylint |
| |
| PYLINT_TIMER=0 |
| |
| PYLINT=${PYLINT:-$(command -v pylint 2>/dev/null)} |
| # backward compatibility, do not use |
| PYLINT_PIP_CMD=$(command -v pip 2>/dev/null) |
| PYLINT_REQUIREMENTS=false |
| PYLINT_PIP_USER=true |
| PYLINT_IGNORE_BAD_OPTION_VALUE=true |
| |
| function pylint_usage |
| { |
| yetus_add_option "--pylint=<file>" "Filename of the pylint executable (default: ${PYLINT})" |
| yetus_add_option "--pylint-ignore-bad-option-value=<bool>" "Ignore 'bad-option-value' errors (default: ${PYLINT_IGNORE_BAD_OPTION_VALUE})" |
| yetus_add_option "--pylint-pip-cmd=<file>" "Command to use for pip when installing requirements.txt (default: ${PYLINT_PIP_CMD})" |
| yetus_add_option "--pylint-rcfile=<file>" "pylint configuration file" |
| yetus_add_option "--pylint-requirements=<bool>" "pip install requirements.txt (default: ${PYLINT_REQUIREMENTS})" |
| yetus_add_option "--pylint-use-user=<bool>" "Use --user for the requirements.txt (default: ${PYLINT_PIP_USER})" |
| |
| } |
| |
| function pylint_parse_args |
| { |
| local i |
| |
| for i in "$@"; do |
| case ${i} in |
| --pylint=*) |
| delete_parameter "${i}" |
| PYLINT=${i#*=} |
| ;; |
| --pylint-ignore-bad-option-value=*) |
| delete_parameter "${i}" |
| PYLINT_IGNORE_BAD_OPTION_VALUE=${i#*=} |
| ;; |
| --pylint-pip-cmd=*) |
| delete_parameter "${i}" |
| PYLINT_PIP_CMD=${i#*=} |
| ;; |
| --pylint-rcfile=*) |
| delete_parameter "${i}" |
| PYLINT_RCFILE=${i#*=} |
| ;; |
| --pylint-requirements=*) |
| delete_parameter "${i}" |
| PYLINT_REQUIREMENTS=${i#*=} |
| ;; |
| --pylint-use-user=*) |
| delete_parameter "${i}" |
| PYLINT_PIP_USER=${i#*=} |
| ;; |
| esac |
| done |
| } |
| |
| function pylint_filefilter |
| { |
| local filename=$1 |
| |
| if [[ ${filename} =~ \.py$ ]]; then |
| add_test pylint |
| fi |
| } |
| |
| function pylint_precheck |
| { |
| if ! verify_command "Pylint" "${PYLINT}"; then |
| add_vote_table_v2 0 pylint "" "Pylint was not available." |
| delete_test pylint |
| return 0 |
| fi |
| |
| if [[ "${PYLINT_REQUIREMENTS}" == true ]] && ! verify_command pip "${PYLINT_PIP_CMD}"; then |
| add_vote_table_v2 0 pylint "" "pip command not available. Will process without it." |
| fi |
| } |
| |
| function pylint_calcdiffs |
| { |
| column_calcdiffs "$@" |
| } |
| |
| function pylint_executor |
| { |
| declare repostatus=$1 |
| declare i |
| declare count |
| declare pylintStderr=${repostatus}-pylint-stderr.txt |
| declare oldpp |
| declare -a pylintopts |
| declare -a reqfiles |
| |
| if ! verify_needed_test pylint; then |
| return 0 |
| fi |
| |
| big_console_header "pylint plugin: ${BUILDMODE}" |
| |
| start_clock |
| |
| # add our previous elapsed to our new timer |
| # by setting the clock back |
| offset_clock "${PYLINT_TIMER}" |
| |
| if [[ "${PYLINT_REQUIREMENTS}" == true ]]; then |
| echo "Processing all requirements.txt files. Errors will be ignored." |
| |
| if [[ "${PYLINT_PIP_USER}" == true ]]; then |
| pylintopts=("--user") |
| fi |
| |
| reqfiles=() |
| for i in "${CHANGED_FILES[@]}"; do |
| dirname=$(yetus_abs "${i}") |
| dirname=$(dirname "${dirname}") |
| if [[ -f "${dirname}/requirements.txt" ]]; then |
| reqfiles+=("${dirname}/requirements.txt") |
| fi |
| done |
| yetus_sort_and_unique_array reqfiles |
| for i in "${reqfiles[@]}"; do |
| "${PYLINT_PIP_CMD}" install "${pylintopts[@]}" -r "${i}" || true |
| done |
| oldpp=${PYTHONPATH} |
| for i in "${HOME}/.local/lib/python"*/site-packages; do |
| export PYTHONPATH="${PYTHONPATH:+${PYTHONPATH}:}${i}" |
| done |
| fi |
| |
| pylintopts=() |
| |
| if [[ -n "${PYLINT_RCFILE}" ]] && [[ -f "${PYLINT_RCFILE}" ]]; then |
| pylintopts+=('--rcfile='"${PYLINT_RCFILE}") |
| fi |
| |
| pylintopts+=('--persistent=n') |
| pylintopts+=('--reports=n') |
| pylintopts+=('--score=n') |
| |
| echo "Running pylint against identified python scripts." |
| pushd "${BASEDIR}" >/dev/null || return 1 |
| for i in "${CHANGED_FILES[@]}"; do |
| if [[ ${i} =~ \.py$ && -f ${i} ]]; then |
| "${PYLINT}" "${pylintopts[@]}" \ |
| --msg-template='{path}:{line}:{column}:[{msg_id}({symbol}), {obj}] {msg}' "${i}" \ |
| >> "${PATCH_DIR}/${repostatus}-pylint-result.tmp" \ |
| 2>>"${PATCH_DIR}/${pylintStderr}" |
| fi |
| done |
| |
| if [[ ${PYLINT_IGNORE_BAD_OPTION_VALUE} == "true" ]]; then |
| "${GREP}" -v 'bad-option-value' \ |
| "${PATCH_DIR}/${repostatus}-pylint-result.tmp" \ |
| > "${PATCH_DIR}/${repostatus}-pylint-result.tmp.1" |
| mv "${PATCH_DIR}/${repostatus}-pylint-result.tmp.1" \ |
| "${PATCH_DIR}/${repostatus}-pylint-result.tmp" |
| fi |
| |
| # pylint likes to put extra headers and |
| # stuff, so need to look specifically for our content |
| "${GREP}" '^.*:.*:.*:' \ |
| "${PATCH_DIR}/${repostatus}-pylint-result.tmp" \ |
| > "${PATCH_DIR}/${repostatus}-pylint-result.txt" |
| rm "${PATCH_DIR}/${repostatus}-pylint-result.tmp" |
| |
| if [[ -n "${oldpp}" ]]; then |
| export PYTHONPATH=${oldpp} |
| fi |
| |
| if [[ -f ${PATCH_DIR}/${pylintStderr} ]]; then |
| count=$("${GREP}" -Evc "^(No config file found|Using config file)" "${PATCH_DIR}/${pylintStderr}") |
| if [[ ${count} -gt 0 ]]; then |
| add_vote_table_v2 -1 pylint "@@BASE@@/${pylintStderr}" "Error running pylint. Please check pylint stderr files." |
| return 1 |
| fi |
| fi |
| rm "${PATCH_DIR}/${pylintStderr}" 2>/dev/null |
| popd >/dev/null || return 1 |
| return 0 |
| } |
| |
| function pylint_preapply |
| { |
| declare retval |
| |
| if ! verify_needed_test pylint; then |
| return 0 |
| fi |
| |
| pylint_executor "branch" |
| retval=$? |
| |
| # keep track of how much as elapsed for us already |
| PYLINT_TIMER=$(stop_clock) |
| return ${retval} |
| } |
| |
| function pylint_postapply |
| { |
| if ! verify_needed_test pylint; then |
| return 0 |
| fi |
| |
| pylint_executor patch |
| |
| # shellcheck disable=SC2016 |
| PYLINT_VERSION=$("${PYLINT}" --version 2>/dev/null | "${GREP}" pylint | "${AWK}" '{print $NF}') |
| add_version_data pylint "${PYLINT_VERSION%,}" |
| |
| |
| root_postlog_compare \ |
| pylint \ |
| "${PATCH_DIR}/branch-pylint-result.txt" \ |
| "${PATCH_DIR}/patch-pylint-result.txt" |
| } |
| |
| function pylint_postcompile |
| { |
| declare repostatus=$1 |
| |
| if [[ "${repostatus}" = branch ]]; then |
| pylint_preapply |
| else |
| pylint_postapply |
| fi |
| } |