feat: Introduce isort and Python-based code quality tools (#446)

This commit is contained in:
Jens Bergmann
2025-03-10 06:09:49 +01:00
committed by GitHub
parent 4243ba698a
commit cfe2b900c7
5 changed files with 132 additions and 2 deletions

View File

@@ -56,6 +56,7 @@ dev = [
"pylint",
"mypy",
"pyright",
"isort>=5.13.2",
# security:
"bandit",
# packaging:
@@ -83,8 +84,8 @@ app = "python -m kleinanzeigen_bot"
compile.cmd = "python -O -m PyInstaller pyinstaller.spec --clean"
compile.env = {PYTHONHASHSEED = "1", SOURCE_DATE_EPOCH = "0"} # https://pyinstaller.org/en/stable/advanced-topics.html#creating-a-reproducible-build
debug = "python -m pdb -m kleinanzeigen_bot"
format = "autopep8 --recursive --in-place src tests --verbose"
lint = {shell = "pylint -v src tests && autopep8 -v --exit-code --recursive --diff src tests && mypy" }
format = "python -m scripts.format_code"
lint = "python -m scripts.lint_code"
audit = "bandit -c pyproject.toml -r src"
test = "python -m pytest --capture=tee-sys -v"
utest = "python -m pytest --capture=tee-sys -v -m 'not itest'"
@@ -266,3 +267,15 @@ filterwarnings = [
"ignore:Exception ignored in:pytest.PytestUnraisableExceptionWarning",
"ignore::DeprecationWarning"
]
# Add isort configuration to format imports
[tool.isort]
profile = "black"
line_length = 160
combine_as_imports = true
combine_star = true
multi_line_output = 3
force_grid_wrap = 0
ensure_newline_before_comments = true
no_lines_before = ["LOCALFOLDER"]
combine_straight_imports = true

1
scripts/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Helper scripts for development tasks."""

33
scripts/format_code.py Normal file
View File

@@ -0,0 +1,33 @@
#!/usr/bin/env python3
"""Helper script to format Python code using isort and autopep8."""
import subprocess, sys
from scripts.git_utils import get_modified_python_files
def format_files() -> int:
"""Format Python files using isort and autopep8.
Returns:
int: 0 on success, non-zero on failure
"""
try:
# Format imports in modified files
py_files = get_modified_python_files()
if py_files:
subprocess.run(['isort', *py_files], check=True)
# Format all files with autopep8
subprocess.run(
['autopep8', '--recursive', '--in-place', 'src', 'tests', '--verbose'],
check=True
)
return 0
except subprocess.CalledProcessError as e:
print(f"Error during formatting: {e}", file=sys.stderr)
return 1
if __name__ == '__main__':
sys.exit(format_files())

18
scripts/git_utils.py Normal file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env python3
"""Utility functions for git operations."""
import subprocess
from collections.abc import Sequence
def get_modified_python_files() -> Sequence[str]:
"""Get list of modified Python files from git.
Returns:
Sequence[str]: List of modified Python file paths
"""
git_cmd = ['git', 'diff', '--name-only', '--diff-filter=ACMR', 'HEAD']
result = subprocess.run(git_cmd, capture_output=True, text=True, check=True)
if not result.stdout.strip():
return []
return [f for f in result.stdout.splitlines() if f.endswith('.py')]

65
scripts/lint_code.py Normal file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""Helper script to lint Python code using multiple tools."""
import subprocess, sys
from collections.abc import Sequence
from scripts.git_utils import get_modified_python_files
def run_tool(cmd: Sequence[str], tool_name: str) -> tuple[int, str]:
"""Run a lint tool and return its exit code and error message.
Args:
cmd: Command to run as list of strings
tool_name: Name of the tool for error messages
Returns:
tuple[int, str]: tuple of (exit_code, error_message)
"""
try:
subprocess.run(list(cmd), check=True)
return 0, ""
except subprocess.CalledProcessError as e:
return e.returncode, f"{tool_name} fehlgeschlagen\n"
def lint_files() -> int:
"""Run all linting tools.
Returns:
int: Number of errors encountered
"""
errors = 0
error_messages = []
# Run isort on modified files
py_files = get_modified_python_files()
if py_files:
code, msg = run_tool(['isort', *py_files], 'isort')
errors += code
if msg:
error_messages.append(msg)
# Run other linting tools
tools = [
(['pylint', '-v', 'src', 'tests'], 'pylint'),
(['autopep8', '-v', '--in-place', '--recursive', 'src', 'tests'], 'autopep8'),
(['mypy', 'src', 'tests'], 'mypy')
]
for cmd, name in tools:
code, msg = run_tool(cmd, name)
errors += code
if msg:
error_messages.append(msg)
# Print all error messages at the end
if error_messages:
print("\n".join(error_messages), file=sys.stderr)
return errors
if __name__ == '__main__':
sys.exit(lint_files())