projectrules.ai

Ruff Configuration Guide

LintingPythonCode QualityRuffDevelopment

Description

Ruff linting configuration and usage guidelines

Globs

*.py
---
description: Ruff linting configuration and usage guidelines
globs: *.py
---

# Ruff Configuration Guide

When you have questions about Ruff linting configuration, need help with linting rules, or want to run Ruff commands in this project, I can provide guidance based on the project's configuration.

## Running Ruff Commands

```bash
# Check linting issues in a file or directory
uv run ruff check path/to/file_or_dir

# Fix auto-fixable issues
uv run ruff check --fix path/to/file_or_dir

# Format code using Ruff formatter
uv run ruff format path/to/file_or_dir

# Automatically fix common issues (like D413 and I001)
uv run ruff check --fix --select D413,I001 path/to/file_or_dir

# Set up a pre-commit hook to automatically run linting (add to your Makefile or scripts)
make lint-fix  # Where this runs: uv run ruff check --fix . && uv run ruff format .
```

## Fixing Common Linting Errors

This project frequently encounters these specific linting errors:

### D413: Missing blank line after last section in docstring

This error occurs when there's no blank line after the last section in a docstring. For example:

```python
# Incorrect - Will trigger D413
def example_function(param1: str, param2: int) -> bool:
    """Check if parameters meet criteria.

    Args:
        param1: First parameter to check
        param2: Second parameter to check

    Returns:
        True if criteria are met, False otherwise"""  # No blank line after Returns section
    return param1 == "test" and param2 > 10

# Correct - Compliant with D413
def example_function(param1: str, param2: int) -> bool:
    """Check if parameters meet criteria.

    Args:
        param1: First parameter to check
        param2: Second parameter to check

    Returns:
        True if criteria are met, False otherwise

    """  # Note the blank line after the Returns section
    return param1 == "test" and param2 > 10
```

**Auto-fix command**: `uv run ruff check --fix --select D413 path/to/file_or_dir`

### I001: Unsorted imports

This error occurs when imports are not properly sorted according to the project's isort configuration. The project uses specific import sections:

1. Future imports
2. Standard library imports
3. Third-party imports
4. Pytest imports
5. First-party imports (project modules)
6. Local folder imports

```python
# Incorrect - Will trigger I001
import os
from typing import List, Optional
import pytest
from codegen_lab.utils import helper
import json
from pathlib import Path

# Correct - Compliant with I001
from __future__ import annotations  # Required import

import json
import os
from pathlib import Path
from typing import List, Optional

import pytest

from codegen_lab.utils import helper
```

**Auto-fix command**: `uv run ruff check --fix --select I001 path/to/file_or_dir`

### Preventing These Errors

To prevent these common errors during development:

1. **Set up editor auto-formatting**:
   - Configure your editor to format on save with Ruff
   - Use the provided VS Code settings in the "Editor Integration" section

2. **Use docstring templates**:
   - Add a docstring template to your editor for proper PEP257 formatting
   - Always include a blank line after the last section in your docstrings

3. **Create a pre-commit hook**:
   - Set up a pre-commit hook to automatically fix these issues before committing
   - This can be done with tools like pre-commit or in your CI/CD pipeline

4. **Batch fix during development**:
   - Run this command periodically during development to fix these issues:
     ```bash
     uv run ruff check --fix --select D413,I001 .
     ```

5. **VS Code workspace settings**:
   Add this to your .vscode/settings.json file:
   ```json
   {
       "editor.formatOnSave": true,
       "ruff.format.args": ["--preview"],
       "[python]": {
           "editor.defaultFormatter": "charliermarsh.ruff",
           "editor.formatOnSave": true,
           "editor.codeActionsOnSave": {
               "source.fixAll.ruff": "explicit",
               "source.organizeImports.ruff": "explicit"
           }
       }
   }
   ```

## Project Configuration Overview

This project uses Ruff with the following configuration:

### Basic Settings
- Target Python version: 3.12
- Line length: 120 characters
- Includes Python files (`.py`, `.pyi`) and Jupyter notebooks (`.ipynb`)
- Respects `.gitignore` for excluding files

### Selected Rule Categories
- `D`: pydocstyle (PEP257 convention)
- `E`: pycodestyle
- `F`: Pyflakes
- `UP`: pyupgrade
- `B`: flake8-bugbear
- `I`: isort
- `S`: bandit (security)
- `YTT`: flake8-2020
- `A`: flake8-builtins
- `C4`: flake8-comprehensions
- `T10`: flake8-debugger
- `SIM`: flake8-simplify
- `C90`: mccabe (complexity checking)
- `W`: pycodestyle warnings
- `PGH`: pygrep-hooks
- `RUF`: ruff-specific rules

### Major Ignore Rules
- `B008`: Function calls in default arguments
- `D417`: Not requiring documentation for every function parameter
- `E501`: Line length limitations
- `UP006/UP007`: Type annotation format rules
- `S101`: Allows `assert` statements
- `F401`: Unused imports in certain files
- `N812`: Lowercase imported as non-lowercase
- Many more rules listed in the configuration

### Special Configurations
- isort configuration with custom section ordering
- flake8-type-checking with runtime-evaluated settings for Pydantic
- Per-file ignores for specific file types
- Different mccabe complexity levels

### Per-file Ignores
Special ignore configurations are applied to:
- `__init__.py` files: `["F401", "E402"]`
- Test files: `["D", "C410", "S311", "S103"]`
- Type stub files: `["D", "E501", "E701", "I002"]`
- Documentation and example files: `["D"]`

## Using Ruff with Editor Integration

Most modern code editors provide Ruff integration:

### VS Code
Install the Ruff extension and add to settings.json:
```json
{
    "editor.formatOnSave": true,
    "ruff.enable": true,
    "ruff.format.args": ["--preview"],
    "[python]": {
        "editor.defaultFormatter": "charliermarsh.ruff",
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
            "source.fixAll.ruff": "explicit",
            "source.organizeImports.ruff": "explicit"
        }
    }
}
```

## Troubleshooting Common Issues

If you encounter linting errors, I can help you understand and fix them by:
1. Identifying which rule is triggering
2. Explaining the purpose of the rule
3. Suggesting ways to fix the issue or properly ignore it
4. Explaining when to use per-file ignores vs. inline ignores

## Ignore Strategies

### Inline Ignores
```python
# Example of inline ignore for a single rule
my_long_line = "..." # noqa: E501

# Example of inline ignore for multiple rules
from module import unused_import  # noqa: F401, E501
```

### File-level Ignores
Add to pyproject.toml:
```toml
[tool.ruff.lint.per-file-ignores]
"your/file/path.py" = ["E501", "F401"]
"tests/**/*.py" = ["D", "S101"]
```

### Global Ignores
To globally ignore rules in pyproject.toml:
```toml
[tool.ruff.lint]
ignore = ["E501", "B008"]
```

## Modern Ruff Configuration Structure

The latest Ruff configuration structure separates linting and formatting settings:

```toml
[tool.ruff]
# Basic settings
target-version = "py312"
line-length = 120
# Files to include
include = ["*.py", "*.pyi", "*.ipynb"]
exclude = [
    ".bzr",
    ".direnv",
    ".eggs",
    ".git",
    ".git-rewrite",
    ".hg",
    ".mypy_cache",
    ".nox",
    ".pants.d",
    ".pyenv",
    ".pytest_cache",
    ".pytype",
    ".ruff_cache",
    ".svn",
    ".tox",
    ".venv",
    ".vscode",
    "__pypackages__",
    "_build",
    "buck-out",
    "build",
    "dist",
    "node_modules",
    "venv",
]
# Allow imports relative to the "src" and "test" directories
src = ["src", "tests"]
# Allow unused variables when underscore-prefixed
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

# Linting-specific settings
[tool.ruff.lint]
select = [
    "D",    # pydocstyle (PEP257)
    "E",    # pycodestyle errors
    "F",    # pyflakes
    "UP",   # pyupgrade
    "B",    # flake8-bugbear
    "I",    # isort
    "S",    # bandit (security)
    "YTT",  # flake8-2020
    "A",    # flake8-builtins
    "C4",   # flake8-comprehensions
    "T10",  # flake8-debugger
    "SIM",  # flake8-simplify
    "C90",  # mccabe (complexity)
    "W",    # pycodestyle warnings
    "PGH",  # pygrep-hooks
    "RUF",  # ruff-specific rules
]
ignore = [
    "B008",  # Function calls in default arguments
    "D417",  # Missing argument descriptions in docstrings
    "E501",  # Line too long (handled by formatter)
    "UP006", # Type annotation format
    "UP007", # Type annotation format
    "S101",  # Use of assert detected
    "N812",  # Lowercase imported as non-lowercase
]
# Allow all rules to be auto-fixed
fixable = ["ALL"]
unfixable = []

# Maximum McCabe complexity allowed
[tool.ruff.lint.mccabe]
max-complexity = 10

# Pydocstyle configuration
[tool.ruff.lint.pydocstyle]
convention = "pep257"

# Flake8-type-checking configuration
[tool.ruff.lint.flake8-type-checking]
runtime-evaluated-decorators = [
    "pydantic.computed_field",
    "pydantic.model_validator"
]

# isort settings
[tool.ruff.lint.isort]
case-sensitive = true
force-single-line = false
force-sort-within-sections = true
known-first-party = ["codegen_lab"]
required-imports = ["from __future__ import annotations"]
combine-as-imports = true
split-on-trailing-comma = false
# Define sections for imports
sections = [
    "future",
    "standard-library",
    "third-party",
    "pytest",
    "first-party",
    "local-folder",
]
# Define known third-party libraries
known-third-party = [
    "better_exceptions",
    "fastapi",
    "pydantic",
    "rich",
    "tenacity",
    "uvicorn",
]
# Known pytest modules for the pytest section
known-pytest = ["pytest", "_pytest"]

# Per-file ignores
[tool.ruff.lint.per-file-ignores]
# Ignore unused imports and import order in __init__.py files
"__init__.py" = ["F401", "E402"]
# Ignore missing docstrings and some other rules in tests
"tests/**/*.py" = ["D", "C410", "S311", "S103"]
# Ignore missing docstrings and line length in type stubs
"**/*.pyi" = ["D", "E501", "E701", "I002"]
# Ignore missing docstrings in docs and examples
"docs/**/*.py" = ["D"]
"examples/**/*.py" = ["D"]

# Formatting settings (commented out in this project but included for completeness)
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "auto"
docstring-code-format = false
```

## Import Organization with isort

The project uses the following isort configuration via Ruff:

```toml
[tool.ruff.lint.isort]
case-sensitive = true
force-single-line = false
force-sort-within-sections = true
known-first-party = ["codegen_lab"]
required-imports = ["from __future__ import annotations"]
combine-as-imports = true
split-on-trailing-comma = false
# Define sections for imports
sections = [
    "future",
    "standard-library",
    "third-party",
    "pytest",
    "first-party",
    "local-folder",
]
# Define known third-party libraries
known-third-party = [
    "better_exceptions",
    "fastapi",
    "pydantic",
    "rich",
    "tenacity",
    "uvicorn",
]
# Known pytest modules for the pytest section
known-pytest = ["pytest", "_pytest"]
Ruff Configuration Guide