mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 19:07:04 +01:00
Split lint_python.sh: output helpers and the full-mode linters
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
This commit is contained in:
parent
4ff7a0bf15
commit
97a722d2b2
19
docs/superpowers/contracts/lint-python-split-2026-08-16.json
Normal file
19
docs/superpowers/contracts/lint-python-split-2026-08-16.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"title": "Split meta/lint_python.sh under the 250-line cap",
|
||||
"objective": "Bring meta/lint_python.sh (353 lines) under the repo-wide 250-line cap by moving its output helpers and its full-mode linter block into two sourced libs, with the entry script keeping configuration, argument parsing, the quick-mode tools and the summary.",
|
||||
"acceptance_criteria": [
|
||||
"bash ~/utils/scripts/check_file_length.sh reports no violation for meta/lint_python.sh or either new lib.",
|
||||
"A full-mode run (no --quick) produces byte-identical output to the same run at the pre-split commit, with paths normalised -- and the run demonstrably enters the extracted block, evidenced by pylint, flake8 and pycodestyle appearing in the output.",
|
||||
"A --quick run produces identical output and exit 0.",
|
||||
"The failure path still exits 1: a deliberately unformatted Python file makes the script report failing tools and return non-zero, proving OVERALL_STATUS propagates out of the extracted function.",
|
||||
"shellcheck -x on the entry and shellcheck on both libs exit 0 with no suppression added.",
|
||||
"meta/lint_python.sh was shfmt-clean before the change and remains shfmt-clean after it."
|
||||
],
|
||||
"out_of_scope": [
|
||||
"Adding, removing or reconfiguring any linter.",
|
||||
"Changing flag semantics or the default target paths.",
|
||||
"Reformatting the entry script's existing style.",
|
||||
"Installing the optional linters (bandit, vulture, pydocstyle, radon, interrogate, pyright) that are absent on this machine."
|
||||
],
|
||||
"verifier": "bash meta/lint_python.sh scripts/check_requirements_sync.py (expects exit 0, 'All linting checks passed!', with pylint/flake8/pycodestyle among the passing tools), diffed against the same command run in a detached worktree at the pre-split commit; plus bash meta/lint_python.sh on an unformatted file expecting exit 1; plus shellcheck, shfmt -d and check_file_length.sh on all three files."
|
||||
}
|
||||
49
docs/superpowers/evidence/lint-python-split-2026-08-16.json
Normal file
49
docs/superpowers/evidence/lint-python-split-2026-08-16.json
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"intent": "Bring meta/lint_python.sh (353 lines) under the 250-line cap by extracting the output helpers and the full-mode linter block into sourced libs.",
|
||||
"scope": [
|
||||
"meta/lint_python.sh",
|
||||
"meta/lib/lint_output.sh (new)",
|
||||
"meta/lib/lint_full_tools.sh (new)",
|
||||
"Non-goal: no linter added, removed or reconfigured; no flag behaviour changed"
|
||||
],
|
||||
"changes": [
|
||||
"Move print_header/print_subheader/print_success/print_warning/print_error/print_info/run_tool/check_tool into lib/lint_output.sh, plus the --help text as usage().",
|
||||
"Move the post-quick-mode linters (pylint, bandit, vulture, flake8, pycodestyle, pydocstyle, radon, interrogate, pyright, autoflake, pyupgrade, codespell) into lib/lint_full_tools.sh as run_full_mode_linters().",
|
||||
"Source lint_output.sh immediately after SCRIPT_DIR, before argument parsing, because --help now calls usage() from it."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "bash meta/lint_python.sh --quick scripts/check_requirements_sync.py, before and after, full output diffed",
|
||||
"result": "pass",
|
||||
"evidence": "exit 0 both times, output identical. Note this path exits before the extracted full-mode block, so on its own it does NOT cover the biggest part of the change."
|
||||
},
|
||||
{
|
||||
"command": "bash meta/lint_python.sh scripts/check_requirements_sync.py (full mode), against a detached worktree at the pre-split commit, paths normalised",
|
||||
"result": "pass",
|
||||
"evidence": "FULL OUTPUT IDENTICAL. Six tools ran and passed -- ruff-lint, ruff-format-check, mypy, pylint, flake8, pycodestyle -- so the run genuinely entered run_full_mode_linters rather than skipping it."
|
||||
},
|
||||
{
|
||||
"command": "bash meta/lint_python.sh <a deliberately unformatted .py> (failure path)",
|
||||
"result": "pass",
|
||||
"evidence": "exit 1, with ruff-format-check, pylint, flake8 and pycodestyle all reporting issues. Confirms OVERALL_STATUS still propagates out of the extracted function; the temp file was removed afterwards."
|
||||
},
|
||||
{
|
||||
"command": "bash -n on all three; shellcheck -x on the entry; shellcheck on both libs; shfmt -d on all three",
|
||||
"result": "pass",
|
||||
"evidence": "All exit 0. SC2034 (OVERALL_STATUS appears unused, because shellcheck analyses the lib standalone) was fixed by declaring the cross-seam variables at the top of the lib with a comment, not suppressed. lint_python.sh was shfmt-clean at HEAD and is still clean; the one dirty new lib was formatted, banner count 2 before and after, and the full run re-diffed identical afterwards."
|
||||
},
|
||||
{
|
||||
"command": "bash ~/utils/scripts/check_file_length.sh on all three",
|
||||
"result": "pass",
|
||||
"evidence": "353 -> 175 (entry) + 116 (full tools) + 96 (output helpers), all under 250."
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"Both libs depend on caller state (TARGET_FILES, FIX_MODE, REPORT_MODE, PROJECT_ROOT, FAILED_TOOLS, OVERALL_STATUS and the colour variables). Documented in each header; the full-mode lib also declares them so the dependency is visible to shellcheck.",
|
||||
"Optional linters (bandit, vulture, pydocstyle, radon, interrogate, pyright) are not installed here, so their check_tool branches were skipped in both baseline and post-split runs. The identical-output comparison therefore does not exercise them -- but it does not exercise them before the split either, and the blocks moved verbatim."
|
||||
],
|
||||
"rollback": [
|
||||
"git revert the commit; both libs disappear and lint_python.sh returns to 353 lines.",
|
||||
"Run `bash meta/lint_python.sh scripts/check_requirements_sync.py` and confirm exit 0 with 'All linting checks passed!'."
|
||||
]
|
||||
}
|
||||
123
meta/lib/lint_full_tools.sh
Executable file
123
meta/lib/lint_full_tools.sh
Executable file
@ -0,0 +1,123 @@
|
||||
#!/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
|
||||
}
|
||||
95
meta/lib/lint_output.sh
Executable file
95
meta/lib/lint_output.sh
Executable file
@ -0,0 +1,95 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==============================================================================
|
||||
# Output formatting and tool-running helpers for lint_python.sh.
|
||||
#
|
||||
# Split out to keep the entry script under the 250-line cap. The seam passes
|
||||
# state: run_tool appends to the caller's FAILED_TOOLS array and reads
|
||||
# REPORT_MODE and PROJECT_ROOT, all of which lint_python.sh defines before
|
||||
# sourcing this file. The colour variables come from the caller too.
|
||||
# ==============================================================================
|
||||
|
||||
# shellcheck shell=bash
|
||||
|
||||
usage() {
|
||||
echo "Usage: $1 [OPTIONS] [FILES...]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --fix, -f Auto-fix issues where possible"
|
||||
echo " --quick, -q Quick mode (ruff + mypy only)"
|
||||
echo " --report, -r Generate detailed reports to ./lint-reports/"
|
||||
echo " --help, -h Show this help message"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $1 # Lint all Python files"
|
||||
echo " $1 --fix # Lint and auto-fix"
|
||||
echo " $1 PYTHON/ # Lint specific directory"
|
||||
echo " $1 --quick --fix # Quick lint with auto-fix"
|
||||
}
|
||||
|
||||
print_header() {
|
||||
echo ""
|
||||
echo -e "${BOLD}${BLUE}══════════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BOLD}${BLUE} $1${NC}"
|
||||
echo -e "${BOLD}${BLUE}══════════════════════════════════════════════════════════════${NC}"
|
||||
}
|
||||
|
||||
print_subheader() {
|
||||
echo ""
|
||||
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||
echo -e "${CYAN} $1${NC}"
|
||||
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}ℹ${NC} $1"
|
||||
}
|
||||
|
||||
run_tool() {
|
||||
local tool_name="$1"
|
||||
local tool_cmd="$2"
|
||||
local report_file="${PROJECT_ROOT}/lint-reports/${tool_name}.txt"
|
||||
|
||||
print_subheader "Running ${tool_name}..."
|
||||
|
||||
if [[ "${REPORT_MODE}" == true ]]; then
|
||||
if eval "${tool_cmd}" 2>&1 | tee "${report_file}"; then
|
||||
print_success "${tool_name} passed"
|
||||
return 0
|
||||
else
|
||||
print_error "${tool_name} found issues (see ${report_file})"
|
||||
FAILED_TOOLS+=("${tool_name}")
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
if eval "${tool_cmd}"; then
|
||||
print_success "${tool_name} passed"
|
||||
return 0
|
||||
else
|
||||
print_error "${tool_name} found issues"
|
||||
FAILED_TOOLS+=("${tool_name}")
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_tool() {
|
||||
if command -v "$1" &>/dev/null; then
|
||||
return 0
|
||||
else
|
||||
print_warning "$1 not found, skipping..."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
@ -25,6 +25,10 @@ BOLD='\033[1m'
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="${SCRIPT_DIR}"
|
||||
|
||||
# Sourced before argument parsing: --help calls usage() from here.
|
||||
# shellcheck source=./lib/lint_output.sh
|
||||
source "${SCRIPT_DIR}/lib/lint_output.sh"
|
||||
PYTHON_PATHS=(
|
||||
"PYTHON"
|
||||
"articles"
|
||||
@ -73,19 +77,7 @@ while [[ $# -gt 0 ]]; do
|
||||
shift
|
||||
;;
|
||||
--help | -h)
|
||||
echo "Usage: $0 [OPTIONS] [FILES...]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --fix, -f Auto-fix issues where possible"
|
||||
echo " --quick, -q Quick mode (ruff + mypy only)"
|
||||
echo " --report, -r Generate detailed reports to ./lint-reports/"
|
||||
echo " --help, -h Show this help message"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 # Lint all Python files"
|
||||
echo " $0 --fix # Lint and auto-fix"
|
||||
echo " $0 PYTHON/ # Lint specific directory"
|
||||
echo " $0 --quick --fix # Quick lint with auto-fix"
|
||||
usage "$0"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
@ -111,77 +103,6 @@ fi
|
||||
OVERALL_STATUS=0
|
||||
FAILED_TOOLS=()
|
||||
|
||||
# ==============================================================================
|
||||
# Helper functions
|
||||
# ==============================================================================
|
||||
|
||||
print_header() {
|
||||
echo ""
|
||||
echo -e "${BOLD}${BLUE}══════════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BOLD}${BLUE} $1${NC}"
|
||||
echo -e "${BOLD}${BLUE}══════════════════════════════════════════════════════════════${NC}"
|
||||
}
|
||||
|
||||
print_subheader() {
|
||||
echo ""
|
||||
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||
echo -e "${CYAN} $1${NC}"
|
||||
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}ℹ${NC} $1"
|
||||
}
|
||||
|
||||
run_tool() {
|
||||
local tool_name="$1"
|
||||
local tool_cmd="$2"
|
||||
local report_file="${PROJECT_ROOT}/lint-reports/${tool_name}.txt"
|
||||
|
||||
print_subheader "Running ${tool_name}..."
|
||||
|
||||
if [[ "${REPORT_MODE}" == true ]]; then
|
||||
if eval "${tool_cmd}" 2>&1 | tee "${report_file}"; then
|
||||
print_success "${tool_name} passed"
|
||||
return 0
|
||||
else
|
||||
print_error "${tool_name} found issues (see ${report_file})"
|
||||
FAILED_TOOLS+=("${tool_name}")
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
if eval "${tool_cmd}"; then
|
||||
print_success "${tool_name} passed"
|
||||
return 0
|
||||
else
|
||||
print_error "${tool_name} found issues"
|
||||
FAILED_TOOLS+=("${tool_name}")
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_tool() {
|
||||
if command -v "$1" &>/dev/null; then
|
||||
return 0
|
||||
else
|
||||
print_warning "$1 not found, skipping..."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# Main linting process
|
||||
# ==============================================================================
|
||||
@ -227,108 +148,9 @@ if [[ "${QUICK_MODE}" == true ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# ==============================================================================
|
||||
# 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
|
||||
# shellcheck source=./lib/lint_full_tools.sh
|
||||
source "${SCRIPT_DIR}/lib/lint_full_tools.sh"
|
||||
run_full_mode_linters
|
||||
|
||||
# ==============================================================================
|
||||
# Summary
|
||||
@ -346,7 +168,7 @@ if [[ ${OVERALL_STATUS} -ne 0 ]]; then
|
||||
print_info "Detailed reports saved to: ${PROJECT_ROOT}/lint-reports/"
|
||||
fi
|
||||
print_info "Run with --fix to auto-fix issues where possible"
|
||||
exit ${OVERALL_STATUS}
|
||||
exit "${OVERALL_STATUS}"
|
||||
else
|
||||
print_success "All linting checks passed!"
|
||||
exit 0
|
||||
|
||||
Loading…
Reference in New Issue
Block a user