#!/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. # # define test binaries + versions FLAKE8_BUILD="flake8" MINIMUM_FLAKE8="3.9.0" MINIMUM_MYPY="0.920" MYPY_BUILD="mypy" PYTEST_BUILD="pytest" PYTHON_EXECUTABLE="${PYTHON_EXECUTABLE:-python3}" BLACK_BUILD="$PYTHON_EXECUTABLE -m black" function satisfies_min_version { local provided_version="$1" local expected_version="$2" echo "$( "$PYTHON_EXECUTABLE" << EOM from setuptools.extern.packaging import version print(version.parse('$provided_version') >= version.parse('$expected_version')) EOM )" } function compile_python_test { local COMPILE_STATUS= local COMPILE_REPORT= if [[ ! "$1" ]]; then echo "No python files found! Something is very wrong -- exiting." exit 1; fi # compileall: https://docs.python.org/3/library/compileall.html echo "starting python compilation test..." COMPILE_REPORT=$( ("$PYTHON_EXECUTABLE" -B -mcompileall -q -l -x "[/\\\\][.]git" $1) 2>&1) COMPILE_STATUS=$? if [ $COMPILE_STATUS -ne 0 ]; then echo "Python compilation failed with the following errors:" echo "$COMPILE_REPORT" echo "$COMPILE_STATUS" exit "$COMPILE_STATUS" else echo "python compilation succeeded." echo fi } function mypy_annotation_test { local MYPY_REPORT= local MYPY_STATUS= echo "starting mypy annotations test..." MYPY_REPORT=$( ($MYPY_BUILD \ --config-file python/mypy.ini \ --cache-dir /tmp/.mypy_cache/ \ python/pyspark) 2>&1) MYPY_STATUS=$? if [ "$MYPY_STATUS" -ne 0 ]; then echo "annotations failed mypy checks:" echo "$MYPY_REPORT" echo "$MYPY_STATUS" exit "$MYPY_STATUS" else echo "annotations passed mypy checks." echo fi } function mypy_data_test { local PYTEST_REPORT= local PYTEST_STATUS= echo "starting mypy data test..." $PYTHON_EXECUTABLE -c "import importlib.util; import sys; \ sys.exit(0 if importlib.util.find_spec('pytest_mypy_plugins') else 1)" if [ $? -ne 0 ]; then echo "pytest-mypy-plugins missing. Skipping for now." return fi PYTEST_REPORT=$( (MYPYPATH=python $PYTEST_BUILD \ -c dev/pyproject.toml \ --rootdir python \ --mypy-only-local-stub \ --mypy-ini-file python/mypy.ini \ python/pyspark ) 2>&1) PYTEST_STATUS=$? if [ "$PYTEST_STATUS" -ne 0 ]; then echo "annotations failed data checks:" echo "$PYTEST_REPORT" echo "$PYTEST_STATUS" exit "$PYTEST_STATUS" else echo "annotations passed data checks." echo fi } function mypy_examples_test { local MYPY_REPORT= local MYPY_STATUS= echo "starting mypy examples test..." MYPY_REPORT=$( (MYPYPATH=python $MYPY_BUILD \ --config-file python/mypy.ini \ --exclude "mllib/*" \ examples/src/main/python/) 2>&1) MYPY_STATUS=$? if [ "$MYPY_STATUS" -ne 0 ]; then echo "examples failed mypy checks:" echo "$MYPY_REPORT" echo "$MYPY_STATUS" exit "$MYPY_STATUS" else echo "examples passed mypy checks." echo fi } function mypy_test { if ! hash "$MYPY_BUILD" 2> /dev/null; then echo "The $MYPY_BUILD command was not found. Skipping for now." return fi _MYPY_VERSION=($($MYPY_BUILD --version)) MYPY_VERSION="${_MYPY_VERSION[1]}" EXPECTED_MYPY="$(satisfies_min_version $MYPY_VERSION $MINIMUM_MYPY)" if [[ "$EXPECTED_MYPY" == "False" ]]; then echo "The minimum mypy version needs to be $MINIMUM_MYPY. Your current version is $MYPY_VERSION. Skipping for now." return fi mypy_annotation_test mypy_examples_test mypy_data_test } function flake8_test { local FLAKE8_VERSION= local EXPECTED_FLAKE8= local FLAKE8_REPORT= local FLAKE8_STATUS= if ! hash "$FLAKE8_BUILD" 2> /dev/null; then echo "The flake8 command was not found. Skipping for now." return fi _FLAKE8_VERSION=($($FLAKE8_BUILD --version)) FLAKE8_VERSION="${_FLAKE8_VERSION[0]}" EXPECTED_FLAKE8="$(satisfies_min_version $FLAKE8_VERSION $MINIMUM_FLAKE8)" if [[ "$EXPECTED_FLAKE8" == "False" ]]; then echo "\ The minimum flake8 version needs to be $MINIMUM_FLAKE8. Your current version is $FLAKE8_VERSION flake8 checks failed." exit 1 fi echo "starting $FLAKE8_BUILD test..." FLAKE8_REPORT=$( ($FLAKE8_BUILD --append-config dev/tox.ini --count --show-source --statistics .) 2>&1) FLAKE8_STATUS=$? if [ "$FLAKE8_STATUS" -ne 0 ]; then echo "flake8 checks failed:" echo "$FLAKE8_REPORT" echo "$FLAKE8_STATUS" exit "$FLAKE8_STATUS" else echo "flake8 checks passed." echo fi } function black_test { local BLACK_REPORT= local BLACK_STATUS= # Skip check if black is not installed. $BLACK_BUILD 2> /dev/null if [ $? -ne 0 ]; then echo "The $BLACK_BUILD command was not found. Skipping black checks for now." echo return fi echo "starting black test..." BLACK_REPORT=$( ($BLACK_BUILD --config dev/pyproject.toml --check python/pyspark dev) 2>&1) BLACK_STATUS=$? if [ "$BLACK_STATUS" -ne 0 ]; then echo "black checks failed:" echo "$BLACK_REPORT" echo "Please run 'dev/reformat-python' script." echo "$BLACK_STATUS" exit "$BLACK_STATUS" else echo "black checks passed." echo fi } SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" SPARK_ROOT_DIR="$(dirname "${SCRIPT_DIR}")" pushd "$SPARK_ROOT_DIR" &> /dev/null PYTHON_SOURCE="$(git ls-files '*.py')" compile_python_test "$PYTHON_SOURCE" black_test flake8_test mypy_test echo echo "all lint-python tests passed!" popd &> /dev/null