Skip to main content

Overview

The Linter class provides a type-safe wrapper around Ruff, Astral’s extremely fast Python linter and formatter written in Rust. Ruff combines the functionality of multiple tools (Flake8, Black, isort, and more) into a single, high-performance package.

Location

pyrig.rig.tools.linter.Linter (rig/tools/linter.py:17)

Quick Start

from pyrig.rig.tools.linter import Linter

# Check code for issues
Linter.I.check_args().run()

# Check and automatically fix issues
Linter.I.check_fix_args().run()

# Format code
Linter.I.format_args().run()

Linting

Basic Check

def check_args(self, *args: str) -> Args:
    """Construct ruff check arguments.
    
    Args:
        *args: Check command arguments.
    
    Returns:
        Args for 'ruff check'.
    """
    return self.args("check", *args)

# Usage - check entire project
Linter.I.check_args().run()

# Check specific directory
Linter.I.check_args("src/").run()

# Check specific files
Linter.I.check_args("src/main.py", "tests/test_main.py").run()

# Check with additional options
Linter.I.check_args("--select", "E,F", "--ignore", "E501").run()

Check with Auto-Fix

def check_fix_args(self, *args: str) -> Args:
    """Construct ruff check arguments with auto-fix.
    
    Args:
        *args: Check command arguments.
    
    Returns:
        Args for 'ruff check --fix'.
    """
    return self.check_args("--fix", *args)

# Usage - fix all auto-fixable issues
Linter.I.check_fix_args().run()

# Fix issues in specific directory
Linter.I.check_fix_args("src/").run()

# Fix with unsafe fixes enabled
Linter.I.check_fix_args("--unsafe-fixes").run()

Formatting

Format Code

def format_args(self, *args: str) -> Args:
    """Construct ruff format arguments.
    
    Args:
        *args: Format command arguments.
    
    Returns:
        Args for 'ruff format'.
    """
    return self.args("format", *args)

# Usage - format entire project
Linter.I.format_args().run()

# Format specific directory
Linter.I.format_args("src/").run()

# Format specific files
Linter.I.format_args("src/main.py", "tests/test_main.py").run()

# Check formatting without making changes
Linter.I.format_args("--check").run()

# Show diff instead of applying changes
Linter.I.format_args("--diff").run()

Common Workflows

Pre-commit Check

from pyrig.rig.tools.linter import Linter

def pre_commit_check():
    """Run linting and formatting checks before commit."""
    # Check for linting issues
    result = Linter.I.check_args().run(check=False)
    if result.returncode != 0:
        print("Linting errors found")
        return False
    
    # Check formatting
    result = Linter.I.format_args("--check").run(check=False)
    if result.returncode != 0:
        print("Formatting issues found")
        return False
    
    return True

if pre_commit_check():
    print("All checks passed")
else:
    print("Please fix issues before committing")

Auto-fix and Format

from pyrig.rig.tools.linter import Linter

def fix_all():
    """Automatically fix all linting and formatting issues."""
    # Fix auto-fixable linting issues
    print("Fixing linting issues...")
    Linter.I.check_fix_args().run()
    
    # Format code
    print("Formatting code...")
    Linter.I.format_args().run()
    
    # Check for remaining issues
    result = Linter.I.check_args().run(check=False)
    if result.returncode == 0:
        print("All issues fixed!")
    else:
        print("Some issues require manual attention")

fix_all()

CI/CD Check

from pyrig.rig.tools.linter import Linter
import sys

def ci_check():
    """Run comprehensive checks for CI/CD."""
    success = True
    
    # Check linting (no auto-fix in CI)
    print("Running linter...")
    result = Linter.I.check_args().run(check=False)
    if result.returncode != 0:
        print("✗ Linting failed")
        success = False
    else:
        print("✓ Linting passed")
    
    # Check formatting
    print("Checking formatting...")
    result = Linter.I.format_args("--check").run(check=False)
    if result.returncode != 0:
        print("✗ Formatting check failed")
        success = False
    else:
        print("✓ Formatting check passed")
    
    if not success:
        sys.exit(1)

ci_check()

Advanced Usage

Select Specific Rules

# Check only specific rule categories
Linter.I.check_args(
    "--select", "E,F,W",  # pycodestyle errors, Pyflakes, warnings
).run()

# Ignore specific rules
Linter.I.check_args(
    "--ignore", "E501,W503"  # line too long, line break before operator
).run()

# Enable specific rules
Linter.I.check_args(
    "--extend-select", "B,C90"  # flake8-bugbear, McCabe complexity
).run()

Configuration Files

Ruff automatically reads configuration from pyproject.toml or ruff.toml:
# pyproject.toml
[tool.ruff]
line-length = 88
target-version = "py311"

[tool.ruff.lint]
select = ["E", "F", "W", "I", "N", "B", "C90"]
ignore = ["E501"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
Ruff will use these settings automatically:
# Uses configuration from pyproject.toml
Linter.I.check_args().run()
Linter.I.format_args().run()

Output Formats

# JSON output for parsing
Linter.I.check_args("--output-format", "json").run()

# GitHub Actions format
Linter.I.check_args("--output-format", "github").run()

# Grouped by file
Linter.I.check_args("--output-format", "grouped").run()

Checking Exit Codes

from pyrig.rig.tools.linter import Linter

# Check returns non-zero if issues found
result = Linter.I.check_args().run(check=False)
if result.returncode != 0:
    print(f"Found {len(result.stdout.splitlines())} issues")
    print(result.stdout)
else:
    print("No issues found")

# Format returns non-zero if files would be reformatted
result = Linter.I.format_args("--check").run(check=False)
if result.returncode != 0:
    print("Files need formatting")
    # Show which files need formatting
    Linter.I.format_args("--check", "--diff").run(check=False)

Integration with Package Manager

from pyrig.rig.tools.linter import Linter
from pyrig.rig.tools.package_manager import PackageManager

# Ensure ruff is installed
PackageManager.I.add_dev_dependencies_args("ruff").run()

# Run linter via uv run (ensures correct environment)
PackageManager.I.run_args("ruff", "check").run()
PackageManager.I.run_args("ruff", "format").run()

# Or use the Linter wrapper directly
Linter.I.check_fix_args().run()
Linter.I.format_args().run()

Customization

Override methods to customize linter behavior:
from pyrig.rig.tools.linter import Linter
from pyrig.src.processes import Args

class StrictLinter(Linter):
    """Linter with strict defaults."""
    
    def check_args(self, *args: str) -> Args:
        """Always use strict mode."""
        return super().check_args(
            "--select", "ALL",  # Enable all rules
            "--ignore", "E501,D",  # Except line length and docstrings
            *args
        )
    
    def format_args(self, *args: str) -> Args:
        """Format with specific line length."""
        return super().format_args(
            "--line-length", "100",
            *args
        )

# Use custom linter
StrictLinter.I.check_args().run()
StrictLinter.I.format_args().run()

Tool Configuration

# Get tool name
Linter.I.name()  # Returns: 'ruff'

# Get tool group
Linter.I.group()  # Returns: 'code-quality'

# Get badge URLs for README
badge_url, project_url = Linter.I.badge_urls()
# badge_url: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
# project_url: https://github.com/astral-sh/ruff

# Get dev dependencies
Linter.I.dev_dependencies()  # Returns: ('ruff',)

Complete Example

from pyrig.rig.tools.linter import Linter
from pyrig.rig.tools.version_controller import VersionController

def cleanup_code():
    """Clean up code before committing."""
    print("Checking for uncommitted changes...")
    if not VersionController.I.has_unstaged_diff():
        print("No changes to clean up")
        return
    
    print("Fixing linting issues...")
    result = Linter.I.check_fix_args().run(check=False)
    if result.returncode != 0:
        print("Some linting issues require manual attention")
    
    print("Formatting code...")
    Linter.I.format_args().run()
    
    print("Running final checks...")
    lint_result = Linter.I.check_args().run(check=False)
    format_result = Linter.I.format_args("--check").run(check=False)
    
    if lint_result.returncode == 0 and format_result.returncode == 0:
        print("✓ Code is clean!")
        
        # Commit the changes
        if VersionController.I.has_unstaged_diff():
            VersionController.I.add_all_args().run()
            VersionController.I.commit_no_verify_args(
                msg="style: fix linting and formatting"
            ).run()
    else:
        print("✗ Some issues remain")

cleanup_code()

Ruff Rules Reference

Common rule categories:
  • E - pycodestyle errors
  • F - Pyflakes
  • W - pycodestyle warnings
  • I - isort
  • N - pep8-naming
  • D - pydocstyle
  • B - flake8-bugbear
  • A - flake8-builtins
  • C90 - McCabe complexity
  • S - flake8-bandit (security)
  • T20 - flake8-print
  • RUF - Ruff-specific rules
See the Ruff rules documentation for complete list.

See Also

Ruff Documentation

Official Ruff documentation

Type Checker

Ty type checker wrapper

Tools Overview

Learn about the Tool pattern

Project Tester

Run tests with pytest

Build docs developers (and LLMs) love