Skip to content

Linting Guide

Code quality checks and linting for the Are You Not Entertained (AYNE) project using Ruff and other tools.

Quick Start

# Run all linting checks
uv run ruff check src/ scripts/ tests/

# Auto-fix issues where possible
uv run ruff check --fix src/ scripts/ tests/

# Type checking
uv run mypy src/ scripts/ tests/ --ignore-missing-imports

# Security scan
uv run bandit -r src/

Ruff Linter

Ruff is an extremely fast Python linter written in Rust, combining the functionality of multiple tools.

Basic Usage

# Lint all source code
uv run ruff check src/

# Lint specific files
uv run ruff check src/ayne/config/schema.py

# Show detailed output
uv run ruff check src/ --output-format=full

# Auto-fix safe issues
uv run ruff check --fix src/ scripts/ tests/

What Ruff Checks

Ruff implements rules from multiple linters:

  • pycodestyle (E, W) - PEP 8 style violations
  • pyflakes (F) - Logical errors and undefined names
  • isort (I) - Import sorting
  • pydocstyle (D) - Docstring conventions
  • pylint (PL) - Advanced code analysis
  • And many more - See Ruff rules

Configuration

Settings in pyproject.toml:

[tool.ruff]
line-length = 100
target-version = "py311"
fix = true

[tool.ruff.lint]
select = ["E", "W", "F", "I", "B", "C4", "D"]
ignore = [
    "E501",   # Line too long (handled by formatter)
    "B008",   # Function call in argument defaults
    "D203",   # 1 blank line before class (conflicts with D211)
    "D213",   # Multi-line docstring summary second line
]

[tool.ruff.lint.pydocstyle]
convention = "google"

Type Checking with Mypy

Static type checking catches bugs before runtime.

Mypy Usage

# Type check source code
uv run mypy src/

# Include scripts and tests
uv run mypy src/ scripts/ tests/

# Ignore missing imports
uv run mypy src/ --ignore-missing-imports

# Show error codes
uv run mypy src/ --show-error-codes

Mypy Configuration

Settings in pyproject.toml:

[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
no_implicit_optional = true

[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false

Type Hint Guidelines

Always use type hints for function signatures:

# Good
def process_data(year: int, month: int) -> pd.DataFrame:
    """Process data for period."""
    pass

# Bad
def process_data(year, month):
    """Process data for period."""
    pass

Use typing module for complex types:

from typing import Optional, List, Dict, Tuple
from pathlib import Path

def load_files(
    directory: Path,
    patterns: List[str],
    limit: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
    """Load files matching patterns."""
    pass

Security Scanning with Bandit

Bandit finds common security issues in Python code.

Bandit Usage

# Scan source code
uv run bandit -r src/

# Exclude test files
uv run bandit -r src/ -x tests/

# Show only high severity
uv run bandit -r src/ -ll

# Generate detailed report
uv run bandit -r src/ -f html -o reports/security.html

Bandit Configuration

Settings in pyproject.toml:

[tool.bandit]
exclude_dirs = ["tests", "scripts"]
skips = [
    "B101",  # assert_used (OK in tests)
]

Common Security Issues

Hardcoded passwords:

# Bad
password = "hardcoded_value"

# Good
password = os.environ.get("DB_PASSWORD")

SQL injection:

# Bad
query = f"SELECT * FROM users WHERE id = {user_id}"

# Good
query = "SELECT * FROM users WHERE id = ?"
cursor.execute(query, (user_id,))

Import Sorting

Ruff handles import sorting automatically.

Manual Import Sorting

# Sort imports with Ruff
uv run ruff check --select I --fix src/ scripts/ tests/

Import Order

  1. Standard library imports
  2. Third-party imports
  3. Local application imports
# Standard library
import os
import sys
from pathlib import Path

# Third-party
import pandas as pd
import numpy as np
from pydantic import BaseModel

# Local
from ayne.config.schema import Settings
from ayne.utils.calendar import get_days_in_month

Running All Checks

Individual Commands

# Lint with Ruff
uv run ruff check src/ scripts/ tests/

# Format check
uv run ruff format --check src/ scripts/ tests/

# Type check
uv run mypy src/ scripts/ tests/ --ignore-missing-imports

# Security scan
uv run bandit -r src/

Combined Script

Create lint.ps1:

# Run all linting checks
Write-Host "Running Ruff linter..." -ForegroundColor Cyan
uv run ruff check src/ scripts/ tests/
if ($LASTEXITCODE -ne 0) { exit 1 }

Write-Host "`nRunning type checker..." -ForegroundColor Cyan
uv run mypy src/ scripts/ tests/ --ignore-missing-imports
if ($LASTEXITCODE -ne 0) { exit 1 }

Write-Host "`nRunning security scanner..." -ForegroundColor Cyan
uv run bandit -r src/ -ll
if ($LASTEXITCODE -ne 0) { exit 1 }

Write-Host "`nAll checks passed!" -ForegroundColor Green

CI/CD Integration

Linting runs automatically in GitHub Actions:

- name: Run ruff linting
  run: |
    uv run ruff check src/ scripts/ tests/

- name: Run type checking
  run: |
    uv run mypy src/ scripts/ tests/ --ignore-missing-imports

- name: Run security scan
  run: |
    uv run bandit -r src/

See .github/workflows/python-lint.yml, .github/workflows/python-typecheck.yml, and .github/workflows/security-audit.yml for complete workflows.

IDE Integration

VS Code

Install extensions:

  • Ruff (charliermarsh.ruff)
  • Pylance (ms-python.vscode-pylance)

Settings in .vscode/settings.json:

{
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff",
    "editor.codeActionsOnSave": {
      "source.fixAll.ruff": "explicit",
      "source.organizeImports.ruff": "explicit"
    }
  },
  "ruff.lint.enable": true,
  "ruff.format.enable": true,
  "python.linting.enabled": true,
  "python.linting.mypyEnabled": true
}

PyCharm

  1. Install Ruff plugin from marketplace
  2. Enable Mypy in Settings → Tools → Python Integrated Tools
  3. Configure to run on save

Common Issues

Ruff Not Found

# Install dev dependencies
uv sync

# Verify installation
uv run ruff --version

Type Errors with Third-Party Libraries

# Install type stubs
uv pip install types-boto3
uv pip install pandas-stubs

# Or ignore missing imports
uv run mypy src/ --ignore-missing-imports

Import Order Conflicts

# Let Ruff fix automatically
uv run ruff check --select I --fix src/ scripts/ tests/

Pre-commit Hooks

Automatically lint before commits:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.1.8
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.7.1
    hooks:
      - id: mypy
        additional_dependencies: [types-all]

Install hooks:

uv run pre-commit install

Best Practices

  1. Lint early and often - Run checks before committing
  2. Fix issues immediately - Don't accumulate technical debt
  3. Use auto-fix - Let tools handle simple fixes
  4. Understand warnings - Don't blindly ignore issues
  5. Configure appropriately - Adjust rules for your project
  6. Integrate with IDE - Get real-time feedback

Resources