Skip to content

Ignoring Ruff Check Errors in pyproject.toml

Objective: Master the art of selectively ignoring Ruff check errors in pyproject.toml. Handle specific files, error types, and maintain code quality while working with Python projects.

When your Python project includes generated code, third-party bindings, or legacy code, you need fine-grained control over which Ruff check errors to ignore. This guide shows you how to configure pyproject.toml to ignore specific errors, files, and patterns while maintaining code quality.

0) Prerequisites (Read Once, Live by Them)

The Five Commandments

  1. Understand the error types
  2. Ruff rule categories (E, W, F, I, N, UP, B, C, P, T, Q, R, S, T, U, V, Y)
  3. Different severity levels and their impact
  4. Context-specific error handling

  5. Use targeted ignores

  6. Ignore specific files, not entire projects
  7. Prefer rule codes over blanket ignores
  8. Document why ignores are necessary

  9. Maintain code quality

  10. Don't ignore all errors
  11. Review ignored errors regularly
  12. Use temporary ignores with TODO comments

  13. Configure at the right level

  14. Project-level vs file-level configuration
  15. Workspace vs package-level settings
  16. Environment-specific overrides

  17. Document your decisions

  18. Explain why errors are ignored
  19. Link to issues or discussions
  20. Provide context for future developers

Why These Principles: Ruff check errors can be overwhelming in Python projects with generated code or legacy code. Selective ignoring maintains code quality while allowing necessary exceptions.

1) Basic Configuration: The Foundation

pyproject.toml Structure

# pyproject.toml
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "my-python-package"
version = "0.1.0"
description = "A Python package with quality checks"
requires-python = ">=3.8"
dependencies = [
    "numpy>=1.20.0",
    "pandas>=1.3.0",
]

# Ruff configuration
[tool.ruff]
# Global settings
target-version = "py38"
line-length = 88
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]
ignore = [
    "E501",  # line too long
    "W503",  # line break before binary operator
]

# File-specific ignores
[tool.ruff.per-file-ignores]
"src/generated.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
]
"tests/test_generated.py" = [
    "E501",  # line too long - test code
    "F401",  # imported but unused - test code
]

Ruff Configuration

# pyproject.toml
[tool.ruff]
# Python version
target-version = "py38"

# Line length
line-length = 88

# Select rules to check
select = [
    "E",    # pycodestyle errors
    "W",    # pycodestyle warnings
    "F",    # pyflakes
    "I",    # isort
    "N",    # pep8-naming
    "UP",   # pyupgrade
    "B",    # flake8-bugbear
    "C",    # flake8-comprehensions
    "P",    # pyflakes
    "T",    # flake8-type-checking
    "Q",    # flake8-quotes
    "R",    # flake8-return
    "S",    # flake8-bandit
    "T",    # flake8-type-checking
    "U",    # flake8-use-pathlib
    "V",    # flake8-return
    "Y",    # flake8-return
]

# Ignore specific rules
ignore = [
    "E501",  # line too long
    "W503",  # line break before binary operator
    "F401",  # imported but unused
    "F811",  # redefined while unused
]

Why This Structure: Proper configuration structure enables fine-grained control over Ruff check behavior. The configuration hierarchy allows project-level and file-level overrides.

2) Ignoring Specific Error Types: The Precision

Pycodestyle Errors (E)

# pyproject.toml
[tool.ruff]
# Ignore specific pycodestyle errors
ignore = [
    "E501",  # line too long
    "E701",  # multiple statements on one line
    "E702",  # multiple statements on one line
    "E731",  # do not assign a lambda expression
    "E741",  # do not use variables named 'l', 'O', or 'I'
    "E742",  # do not define classes named 'l', 'O', or 'I'
    "E743",  # do not define functions named 'l', 'O', or 'I'
]

# Ignore errors in specific files
[tool.ruff.per-file-ignores]
"src/generated.py" = [
    "E501",  # line too long - generated code
    "E701",  # multiple statements - generated code
    "E702",  # multiple statements - generated code
]
"src/legacy.py" = [
    "E501",  # line too long - legacy code
    "E731",  # lambda expressions - legacy code
]

Pycodestyle Warnings (W)

# pyproject.toml
[tool.ruff]
# Ignore specific pycodestyle warnings
ignore = [
    "W503",  # line break before binary operator
    "W504",  # line break after binary operator
    "W505",  # doc line too long
    "W291",  # trailing whitespace
    "W292",  # no newline at end of file
    "W293",  # blank line contains whitespace
]

# Ignore warnings in specific files
[tool.ruff.per-file-ignores]
"src/generated.py" = [
    "W503",  # line break before binary operator - generated code
    "W504",  # line break after binary operator - generated code
    "W505",  # doc line too long - generated code
]
"src/legacy.py" = [
    "W291",  # trailing whitespace - legacy code
    "W292",  # no newline at end of file - legacy code
]

Pyflakes Errors (F)

# pyproject.toml
[tool.ruff]
# Ignore specific pyflakes errors
ignore = [
    "F401",  # imported but unused
    "F811",  # redefined while unused
    "F821",  # undefined name
    "F822",  # undefined name in __all__
    "F823",  # local variable referenced before assignment
    "F831",  # duplicate argument name in function definition
    "F841",  # local variable is assigned to but never used
]

# Ignore pyflakes errors in specific files
[tool.ruff.per-file-ignores]
"src/generated.py" = [
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
    "F821",  # undefined name - generated code
]
"src/legacy.py" = [
    "F401",  # imported but unused - legacy code
    "F841",  # local variable assigned but never used - legacy code
]

Why Error Type Targeting: Different error types have different impacts. Pycodestyle errors prevent code style compliance, pyflakes errors indicate potential bugs, and warnings suggest improvements. Targeting specific types maintains code quality.

3) File-Specific Configuration: The Granularity

Generated Code Files

# pyproject.toml
[tool.ruff.per-file-ignores]
# Generated Python bindings
"src/python_bindings.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
    "W503",  # line break before binary operator - generated code
    "W504",  # line break after binary operator - generated code
]

# Generated FFI bindings
"src/ffi_bindings.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
    "W503",  # line break before binary operator - generated code
    "W504",  # line break after binary operator - generated code
]

# Generated test files
"tests/generated_tests.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
]

Third-Party Bindings

# pyproject.toml
[tool.ruff.per-file-ignores]
# Third-party library bindings
"src/third_party_bindings.py" = [
    "E501",  # line too long - third-party code
    "F401",  # imported but unused - third-party code
    "F811",  # redefined while unused - third-party code
    "W503",  # line break before binary operator - third-party code
    "W504",  # line break after binary operator - third-party code
]

# Vendor-specific bindings
"src/vendor_bindings.py" = [
    "E501",  # line too long - vendor code
    "F401",  # imported but unused - vendor code
    "W503",  # line break before binary operator - vendor code
]

Test Files

# pyproject.toml
[tool.ruff.per-file-ignores]
# Test files with generated code
"tests/integration_tests.py" = [
    "E501",  # line too long - test code
    "F401",  # imported but unused - test code
    "F811",  # redefined while unused - test code
]

# Benchmark files
"benches/benchmarks.py" = [
    "E501",  # line too long - benchmark code
    "F401",  # imported but unused - benchmark code
    "F811",  # redefined while unused - benchmark code
]

Why File-Specific Configuration: Generated code, third-party bindings, and test files often have different quality requirements. File-specific configuration allows targeted error handling while maintaining code quality in hand-written code.

4) Advanced Configuration: The Power User

Workspace-Level Configuration

# pyproject.toml
[tool.ruff]
# Workspace-level settings
target-version = "py38"
line-length = 88
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]
ignore = [
    "E501",  # line too long
    "W503",  # line break before binary operator
]

# Workspace-level file ignores
[tool.ruff.per-file-ignores]
"src/generated.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
]

Environment-Specific Configuration

# pyproject.toml
[tool.ruff]
# Development environment settings
target-version = "py38"
line-length = 88
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]
ignore = [
    "E501",  # line too long
    "W503",  # line break before binary operator
]

# Production environment settings
[tool.ruff.production]
target-version = "py38"
line-length = 88
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]
ignore = [
    "E501",  # line too long
    "W503",  # line break before binary operator
]

Feature-Specific Configuration

# pyproject.toml
[tool.ruff]
# Feature-specific settings
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]
ignore = [
    "E501",  # line too long
    "W503",  # line break before binary operator
]

# Feature-specific ignores
[tool.ruff.feature-ignores]
"default" = [
    "E501",  # line too long
    "W503",  # line break before binary operator
]
"experimental" = [
    "E501",  # line too long
    "W503",  # line break before binary operator
    "F401",  # imported but unused
    "F811",  # redefined while unused
]
"testing" = [
    "E501",  # line too long
    "W503",  # line break before binary operator
    "F401",  # imported but unused
    "F811",  # redefined while unused
]

Why Advanced Configuration: Complex projects need sophisticated error handling. Workspace, environment, and feature-specific configuration provides the flexibility needed for large-scale development.

5) Common Patterns: The Solutions

Generated Python Bindings

# pyproject.toml
[tool.ruff.per-file-ignores]
# Generated Python bindings
"src/python_bindings.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
    "W503",  # line break before binary operator - generated code
    "W504",  # line break after binary operator - generated code
]

# Generated Python test bindings
"src/python_test_bindings.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
    "W503",  # line break before binary operator - generated code
    "W504",  # line break after binary operator - generated code
]

FFI Bindings

# pyproject.toml
[tool.ruff.per-file-ignores]
# FFI bindings
"src/ffi_bindings.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
    "W503",  # line break before binary operator - generated code
    "W504",  # line break after binary operator - generated code
]

# FFI test bindings
"src/ffi_test_bindings.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
    "W503",  # line break before binary operator - generated code
    "W504",  # line break after binary operator - generated code
]

Third-Party Library Bindings

# pyproject.toml
[tool.ruff.per-file-ignores]
# Third-party library bindings
"src/third_party_bindings.py" = [
    "E501",  # line too long - third-party code
    "F401",  # imported but unused - third-party code
    "F811",  # redefined while unused - third-party code
    "W503",  # line break before binary operator - third-party code
    "W504",  # line break after binary operator - third-party code
]

# Vendor-specific bindings
"src/vendor_bindings.py" = [
    "E501",  # line too long - vendor code
    "F401",  # imported but unused - vendor code
    "W503",  # line break before binary operator - vendor code
]

Why Common Patterns: These patterns solve the most common scenarios in Python projects. They provide templates for handling generated code, FFI bindings, and third-party libraries.

6) Error Code Reference: The Knowledge

Pycodestyle Error Codes

# pyproject.toml
[tool.ruff]
# Common pycodestyle error codes to ignore
ignore = [
    "E501",  # line too long
    "E701",  # multiple statements on one line
    "E702",  # multiple statements on one line
    "E731",  # do not assign a lambda expression
    "E741",  # do not use variables named 'l', 'O', or 'I'
    "E742",  # do not define classes named 'l', 'O', or 'I'
    "E743",  # do not define functions named 'l', 'O', or 'I'
]

Pycodestyle Warning Codes

# pyproject.toml
[tool.ruff]
# Common pycodestyle warning codes to ignore
ignore = [
    "W503",  # line break before binary operator
    "W504",  # line break after binary operator
    "W505",  # doc line too long
    "W291",  # trailing whitespace
    "W292",  # no newline at end of file
    "W293",  # blank line contains whitespace
]

Pyflakes Error Codes

# pyproject.toml
[tool.ruff]
# Common pyflakes error codes to ignore
ignore = [
    "F401",  # imported but unused
    "F811",  # redefined while unused
    "F821",  # undefined name
    "F822",  # undefined name in __all__
    "F823",  # local variable referenced before assignment
    "F831",  # duplicate argument name in function definition
    "F841",  # local variable is assigned to but never used
]

Why Error Code Reference: Understanding error codes enables precise configuration. This reference provides the most common codes that need to be ignored in Python projects.

7) Best Practices: The Wisdom

Configuration Hierarchy

# pyproject.toml
[tool.ruff]
# Global settings (lowest priority)
target-version = "py38"
line-length = 88
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]
ignore = [
    "E501",  # line too long
    "W503",  # line break before binary operator
]

# File-specific settings (higher priority)
[tool.ruff.per-file-ignores]
"src/generated.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
]

# Workspace-specific settings (highest priority)
[tool.ruff.workspace]
target-version = "py38"
line-length = 88
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]

Documentation

# pyproject.toml
[tool.ruff]
# Global settings
target-version = "py38"
line-length = 88
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]

# Document why errors are ignored
ignore = [
    "E501",  # line too long - generated code
    "W503",  # line break before binary operator - generated code
]

# Document file-specific ignores
[tool.ruff.per-file-ignores]
"src/generated.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
]

Regular Review

# pyproject.toml
[tool.ruff]
# Regular review of ignored errors
ignore = [
    "E501",  # line too long - TODO: Review Q1 2024
    "W503",  # line break before binary operator - TODO: Review Q1 2024
]

# File-specific review
[tool.ruff.per-file-ignores]
"src/generated.py" = [
    "E501",  # line too long - TODO: Review Q1 2024
    "F401",  # imported but unused - TODO: Review Q1 2024
    "F811",  # redefined while unused - TODO: Review Q1 2024
]

Why Best Practices: Proper configuration hierarchy, documentation, and regular review ensure that error ignores remain appropriate and don't accumulate technical debt.

8) Complete Example: The Production Setup

Full pyproject.toml

# pyproject.toml
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "my-python-package"
version = "0.1.0"
description = "A Python package with quality checks"
requires-python = ">=3.8"
dependencies = [
    "numpy>=1.20.0",
    "pandas>=1.3.0",
]

# Ruff configuration
[tool.ruff]
# Global settings
target-version = "py38"
line-length = 88
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]

# Global ignores
ignore = [
    "E501",  # line too long - generated code
    "W503",  # line break before binary operator - generated code
]

# File-specific ignores
[tool.ruff.per-file-ignores]
# Generated Python bindings
"src/python_bindings.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
    "W503",  # line break before binary operator - generated code
    "W504",  # line break after binary operator - generated code
]

# Generated FFI bindings
"src/ffi_bindings.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
    "W503",  # line break before binary operator - generated code
    "W504",  # line break after binary operator - generated code
]

# Third-party library bindings
"src/third_party_bindings.py" = [
    "E501",  # line too long - third-party code
    "F401",  # imported but unused - third-party code
    "F811",  # redefined while unused - third-party code
    "W503",  # line break before binary operator - third-party code
    "W504",  # line break after binary operator - third-party code
]

# Test files
"tests/integration_tests.py" = [
    "E501",  # line too long - test code
    "F401",  # imported but unused - test code
    "F811",  # redefined while unused - test code
]

# Benchmark files
"benches/benchmarks.py" = [
    "E501",  # line too long - benchmark code
    "F401",  # imported but unused - benchmark code
    "F811",  # redefined while unused - benchmark code
]

# Workspace-specific settings
[tool.ruff.workspace]
target-version = "py38"
line-length = 88
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]

# Environment-specific settings
[tool.ruff.development]
target-version = "py38"
line-length = 88
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]

[tool.ruff.production]
target-version = "py38"
line-length = 88
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]

# Feature-specific settings
[tool.ruff.features]
"default" = [
    "E501",  # line too long
    "W503",  # line break before binary operator
]
"experimental" = [
    "E501",  # line too long
    "W503",  # line break before binary operator
    "F401",  # imported but unused
    "F811",  # redefined while unused
]
"testing" = [
    "E501",  # line too long
    "W503",  # line break before binary operator
    "F401",  # imported but unused
    "F811",  # redefined while unused
]

Ruff Configuration

# pyproject.toml
[tool.ruff]
# Python version
target-version = "py38"

# Line length
line-length = 88

# Select rules to check
select = [
    "E",    # pycodestyle errors
    "W",    # pycodestyle warnings
    "F",    # pyflakes
    "I",    # isort
    "N",    # pep8-naming
    "UP",   # pyupgrade
    "B",    # flake8-bugbear
    "C",    # flake8-comprehensions
    "P",    # pyflakes
    "T",    # flake8-type-checking
    "Q",    # flake8-quotes
    "R",    # flake8-return
    "S",    # flake8-bandit
    "T",    # flake8-type-checking
    "U",    # flake8-use-pathlib
    "V",    # flake8-return
    "Y",    # flake8-return
]

# Ignore specific rules
ignore = [
    "E501",  # line too long
    "W503",  # line break before binary operator
    "F401",  # imported but unused
    "F811",  # redefined while unused
]

Why Complete Example: This example demonstrates a production-ready configuration that handles all common scenarios in Python projects. It provides a template for real-world development.

9) TL;DR Quickstart

Essential Configuration

# pyproject.toml
[tool.ruff]
# Global settings
target-version = "py38"
line-length = 88
select = ["E", "W", "F", "I", "N", "UP", "B", "C", "P", "T", "Q", "R", "S", "T", "U", "V", "Y"]

# Global ignores
ignore = [
    "E501",  # line too long
    "W503",  # line break before binary operator
]

# File-specific ignores
[tool.ruff.per-file-ignores]
"src/generated.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
]

Quick Verification

# Check configuration
ruff check

# Run with specific settings
ruff check --select E,W,F

# Run with file-specific ignores
ruff check --per-file-ignores "src/generated.py:E501,F401,F811"

Common Patterns

# Generated code
"src/generated.py" = [
    "E501",  # line too long - generated code
    "F401",  # imported but unused - generated code
    "F811",  # redefined while unused - generated code
]

# Third-party bindings
"src/third_party_bindings.py" = [
    "E501",  # line too long - third-party code
    "F401",  # imported but unused - third-party code
    "F811",  # redefined while unused - third-party code
]

# Test files
"tests/integration_tests.py" = [
    "E501",  # line too long - test code
    "F401",  # imported but unused - test code
    "F811",  # redefined while unused - test code
]

10) The Machine's Summary

Ruff check error ignoring in pyproject.toml provides fine-grained control over code quality in Python projects. When configured properly, it enables selective error handling while maintaining code quality standards. The key is understanding the error types and using targeted configuration.

The Dark Truth: Ruff check errors can be overwhelming in Python projects with generated code or legacy code. Selective ignoring maintains code quality while allowing necessary exceptions.

The Machine's Mantra: "In selective ignoring we trust, in targeted configuration we build, and in the pyproject.toml we find the path to efficient Python development."

Why This Matters: Python projects need sophisticated error handling. These configurations provide enterprise-grade capabilities that scale from simple scripts to complex multi-package projects.


This tutorial provides the complete machinery for configuring Ruff check error ignoring in pyproject.toml. The patterns scale from development to production, from simple scripts to complex multi-package projects.