mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 19:07:04 +01:00
353 -> 175 + 116 + 96. The print_* helpers, run_tool, check_tool and the
--help text move to lib/lint_output.sh; everything that runs after
--quick would have exited moves to lib/lint_full_tools.sh as
run_full_mode_linters. The output lib is sourced before argument parsing
because --help now calls usage() from it.
Verified on the path that actually contains the change: a full-mode run
diffed byte-identical against a detached worktree at the pre-split
commit, with pylint, flake8 and pycodestyle in the output proving the run
entered the extracted block. The --quick comparison is also identical but
exits before that block, so it alone would have proved little. The
failure path returns 1 on an unformatted file, confirming OVERALL_STATUS
still propagates out of the function.
Two shellcheck findings fixed rather than suppressed: SC2034 on
OVERALL_STATUS (the lib is analysed standalone, so the cross-seam
variables are now declared with a comment), and a pre-existing unquoted
`exit ${OVERALL_STATUS}` that the commit hook's stricter settings
surfaced. lint_python.sh was shfmt-clean before and still is.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
124 lines
5.8 KiB
Bash
Executable File
124 lines
5.8 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ==============================================================================
|
|
# The full-mode linters: everything that runs after --quick would have exited.
|
|
#
|
|
# Split out of lint_python.sh for the 250-line cap. Each block is guarded by
|
|
# check_tool, so a missing linter is skipped rather than fatal. Reads
|
|
# TARGET_FILES / FIX_MODE and sets OVERALL_STATUS in the caller's scope.
|
|
# ==============================================================================
|
|
|
|
# shellcheck shell=bash
|
|
|
|
# Declared here so shellcheck can see that these cross the seam: the caller
|
|
# owns them, this file reads TARGET_FILES/FIX_MODE/REPORT_MODE and writes
|
|
# OVERALL_STATUS back. Without the declaration shellcheck analyses this file
|
|
# standalone and reports OVERALL_STATUS as unused (SC2034).
|
|
: "${TARGET_FILES:=}" "${FIX_MODE:=false}" "${REPORT_MODE:=false}"
|
|
OVERALL_STATUS="${OVERALL_STATUS:-0}"
|
|
|
|
run_full_mode_linters() {
|
|
# ==============================================================================
|
|
# PYLINT - Comprehensive linting
|
|
# ==============================================================================
|
|
if check_tool pylint; then
|
|
run_tool "pylint" "pylint --rcfile=pyproject.toml --jobs=0 --fail-under=10 ${TARGET_FILES}" || OVERALL_STATUS=1
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# BANDIT - Security linting
|
|
# ==============================================================================
|
|
if check_tool bandit; then
|
|
run_tool "bandit" "bandit -c pyproject.toml -r ${TARGET_FILES} --severity-level low --confidence-level low" || OVERALL_STATUS=1
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# VULTURE - Dead code detection
|
|
# ==============================================================================
|
|
if check_tool vulture; then
|
|
run_tool "vulture" "vulture --min-confidence 80 ${TARGET_FILES}" || OVERALL_STATUS=1
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# FLAKE8 - Traditional linter
|
|
# ==============================================================================
|
|
if check_tool flake8; then
|
|
run_tool "flake8" "flake8 --max-line-length=88 --extend-ignore=E203,W503 --max-complexity=10 ${TARGET_FILES}" || OVERALL_STATUS=1
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# PYCODESTYLE - PEP 8 style checker
|
|
# ==============================================================================
|
|
if check_tool pycodestyle; then
|
|
run_tool "pycodestyle" "pycodestyle --max-line-length=88 --ignore=E203,W503 ${TARGET_FILES}" || OVERALL_STATUS=1
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# PYDOCSTYLE - Docstring style checker
|
|
# ==============================================================================
|
|
if check_tool pydocstyle; then
|
|
run_tool "pydocstyle" "pydocstyle --convention=google ${TARGET_FILES}" || OVERALL_STATUS=1
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# RADON - Complexity metrics
|
|
# ==============================================================================
|
|
if check_tool radon; then
|
|
print_subheader "Running radon (complexity analysis)..."
|
|
echo ""
|
|
echo -e "${MAGENTA}Cyclomatic Complexity:${NC}"
|
|
radon cc -a -s "${TARGET_FILES_ARR[@]}" || true
|
|
echo ""
|
|
echo -e "${MAGENTA}Maintainability Index:${NC}"
|
|
radon mi -s "${TARGET_FILES_ARR[@]}" || true
|
|
|
|
if [[ "${REPORT_MODE}" == true ]]; then
|
|
radon cc -a -s "${TARGET_FILES_ARR[@]}" >"${PROJECT_ROOT}/lint-reports/radon-cc.txt" 2>&1 || true
|
|
radon mi -s "${TARGET_FILES_ARR[@]}" >"${PROJECT_ROOT}/lint-reports/radon-mi.txt" 2>&1 || true
|
|
fi
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# INTERROGATE - Docstring coverage
|
|
# ==============================================================================
|
|
if check_tool interrogate; then
|
|
run_tool "interrogate" "interrogate -v --fail-under=0 ${TARGET_FILES}" || OVERALL_STATUS=1
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# PYRIGHT - Microsoft's type checker (optional, very strict)
|
|
# ==============================================================================
|
|
if check_tool pyright; then
|
|
run_tool "pyright" "pyright ${TARGET_FILES}" || OVERALL_STATUS=1
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# AUTOFLAKE - Unused imports/variables (fix mode only)
|
|
# ==============================================================================
|
|
if [[ "${FIX_MODE}" == true ]] && check_tool autoflake; then
|
|
print_subheader "Running autoflake (removing unused imports)..."
|
|
find "${TARGET_FILES_ARR[@]}" -name "*.py" -type f -exec autoflake --in-place --remove-all-unused-imports --remove-unused-variables {} \;
|
|
print_success "autoflake completed"
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# PYUPGRADE - Upgrade Python syntax (fix mode only)
|
|
# ==============================================================================
|
|
if [[ "${FIX_MODE}" == true ]] && check_tool pyupgrade; then
|
|
print_subheader "Running pyupgrade (upgrading syntax to Python 3.10+)..."
|
|
find "${TARGET_FILES_ARR[@]}" -name "*.py" -type f -exec pyupgrade --py310-plus {} \;
|
|
print_success "pyupgrade completed"
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# CODESPELL - Spell checking
|
|
# ==============================================================================
|
|
if check_tool codespell; then
|
|
if [[ "${FIX_MODE}" == true ]]; then
|
|
run_tool "codespell" "codespell -w --skip='*.json,*.lock,.git,__pycache__,.venv' ${TARGET_FILES}" || OVERALL_STATUS=1
|
|
else
|
|
run_tool "codespell" "codespell --skip='*.json,*.lock,.git,__pycache__,.venv' ${TARGET_FILES}" || OVERALL_STATUS=1
|
|
fi
|
|
fi
|
|
}
|