Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
hyperb1iss avatar

Ruff

  • 16 installs
  • 25 repo stars
  • Updated July 31, 2026
  • hyperb1iss/hyperskills

Lints, formats, and fixes Python code with ruff, a single Rust binary replacing Flake8, Black, isort, and pyupgrade, plus its built-in language server.

About

Covers linting, formatting, and analyzing Python with ruff. A developer uses it for ruff check/format/fix, configuring ruff.toml, noqa/per-file-ignores, or the built-in ruff server.

  • Three tools in one: check, format, analyze graph
  • Built-in ruff server replaces deprecated ruff-lsp

Ruff by the numbers

  • 16 all-time installs (skills.sh)
  • +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
  • Ranked #195 of 290 Python skills by installs in the Skillselion catalog
  • Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/hyperb1iss/hyperskills --skill ruff

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs16
repo stars25
Last updatedJuly 31, 2026
Repositoryhyperb1iss/hyperskills

What it does

Lints, formats, and fixes Python code with ruff, a single Rust binary replacing Flake8, Black, isort, and pyupgrade, plus its built-in language server.

Files

SKILL.mdMarkdownGitHub ↗

ruff: Python Linter & Formatter

ruff (v0.15.12, Apr 2026) is three tools in one Rust binary: linter (ruff check), formatter (ruff format), and dependency analyzer (ruff analyze graph). It replaces Flake8, Black, isort, pyupgrade, and dozens more.

The built-in language server (ruff server) replaces the deprecated ruff-lsp package (archived Dec 2025).

Invocation

uv run ruff ...     # Project dependency (pinned version)
uvx ruff ...        # One-off (latest)
ruff ...            # Global install

Rule Selection: The Critical Decision

Default rules are minimal: only ["E4", "E7", "E9", "F"], catches syntax errors and undefined names but misses most quality rules. You almost certainly need to extend this.

select vs extend-select

CommandBehavior
select = ["E", "F", "B"]Replaces entire default set. Only these run.
extend-select = ["B"]Adds to whatever select provides (or defaults)

Config inheritance trap: When a child config specifies select, the parent's ignore list is discarded. This surprises people with monorepo setups.

Specificity wins: More specific prefixes override less specific ones. select = ["E"] + ignore = ["E501"] enables all E rules except E501.

Recommended Selection Strategy

New project, start broad:

[tool.ruff.lint]
select = [
    "E", "W",    # pycodestyle
    "F",         # Pyflakes
    "I",         # isort
    "N",         # pep8-naming
    "UP",        # pyupgrade
    "B",         # flake8-bugbear
    "SIM",       # flake8-simplify
    "TC",        # flake8-type-checking
    "RUF",       # Ruff-specific
]
ignore = ["E501"]  # Let formatter handle line length

Library / open source, maximum strictness:

[tool.ruff.lint]
select = ["ALL"]
ignore = [
    # Formatter conflicts (MUST disable)
    "W191", "E111", "E114", "E117",
    "D206", "D300",
    "Q000", "Q001", "Q002", "Q003", "Q004",
    "COM812", "COM819",
    # Pydocstyle conflicts
    "D203", "D213",
    # Overly strict
    "D100", "D104",
    "ANN101", "ANN102",
    "FBT", "ERA001",
    "E501",
]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101", "D", "ANN", "ARG"]
"scripts/**" = ["T20", "INP001"]
"**/__init__.py" = ["F401", "D104"]

Legacy migration, incremental:

[tool.ruff.lint]
select = ["E4", "E7", "E9", "F"]
extend-select = [
    "I",      # Step 1: import sorting (safe, auto-fixable)
    "UP",     # Step 2: pyupgrade (mostly auto-fixable)
    # "B",    # Step 3: uncomment when ready
]

The ALL Selector

select = ["ALL"] enables every stable rule. Ruff auto-disables conflicting pairs (D203/D211, D212/D213), but being explicit is better practice. Preview rules require preview = true and are not included even with ALL.

Formatter Behavior

Configuration

[tool.ruff.format]
quote-style = "double"           # "double" | "single" | "preserve"
indent-style = "space"           # "space" | "tab"
skip-magic-trailing-comma = false
docstring-code-format = true     # Format code in docstrings
preview = false                  # Enable 2026 style guide

Rules That CONFLICT With the Formatter

When using ruff format, these lint rules should be avoided:

ignore = [
    "W191", "E111", "E114", "E117",  # Indentation
    "D206", "D300",                   # Docstring formatting
    "Q000", "Q001", "Q002", "Q003", "Q004",  # Quotes
    "COM812", "COM819",               # Commas
]

Also avoid ISC002 in Ruff's documented formatter-conflict case: ISC002 selected, ISC001 not selected, and flake8-implicit-str-concat.allow-multiline = false.

Known Deviations from Black

Ruff targets >99.9% parity with Black but has 23 intentional divergences. The most impactful:

DeviationRuffBlack
F-string interiorsFormats {expr} contents (stable since 0.9.0)Does not touch f-string interiors
Pragma comments (# noqa, # type:)Excluded from line widthCounted in line width
Implicit string concatMerges when fits on one lineSplits more aggressively
Blank lines at block startRemoves themPreserves them (Black 24+)
Trailing commentsExpands statement to keep comment closeCollapses, moves comment to end
Single-element tuplesAlways parenthesizesRemoves parens when safe

E501 and the Formatter

The formatter makes best-effort line wrapping, it cannot always succeed. Comments, long strings, and URLs may exceed line-length. Either ignore E501 or set lint.pycodestyle.max-line-length higher than line-length.

Fix Safety Model

ruff check --fix .                    # Safe fixes only
ruff check --fix --unsafe-fixes .     # Include unsafe (review first!)
ruff check --fix --diff .             # Preview changes before applying
SafetyMeaningExample
SafeCannot change runtime behaviorReordering imports
UnsafeMay change behaviorlist(x)[0] -> next(iter(x)) changes exception type

Override per-rule:

[tool.ruff.lint]
extend-safe-fixes = ["RUF015"]       # Promote to safe
extend-unsafe-fixes = ["F401"]        # Demote to unsafe (require --unsafe-fixes)

Suppression System

# Line-level
import os  # noqa: F401

# Block-level (new in 0.15.0)
# ruff: disable[E501]
LONG_VALUE = "..."
# ruff: enable[E501]

# File-level
# ruff: noqa: F401, E501
ruff check --select RUF100 --fix .    # Clean up unused noqa comments
ruff check --add-noqa .               # Auto-add noqa to all violations

Preview Mode

Preview is a staging area for new rules and formatter changes.

[tool.ruff.lint]
preview = true       # Expands defaults from 59 to 412 rules
explicit-preview-rules = true  # Require individual opt-in even with preview on

Preview rules are NOT activated by prefix selection or ALL, they require preview mode enabled. Use explicit-preview-rules = true to control which preview rules activate individually.

Dependency Graph Analysis

ruff analyze graph src/                          # File dependency graph (JSON)
ruff analyze graph --direction=dependents src/   # Reverse graph
ruff analyze graph --detect-string-imports src/  # Include dynamic imports

Use cases: selective test running, dead code detection, circular import detection.

Configuration

File precedence: .ruff.toml > ruff.toml > pyproject.toml (nearest wins, no merging across levels).

Falls back to ~/.config/ruff/ruff.toml when no project config exists.

[tool.ruff]
target-version = "py312"         # Inferred from requires-python if unset
line-length = 88
src = ["src", "tests"]           # First-party import classification
required-version = "==0.15.12"   # Pin version with a PEP 440 specifier
extend = "../pyproject.toml"     # Inherit parent config

[tool.ruff.lint.isort]
known-first-party = ["myproject"]
combine-as-imports = true

[tool.ruff.lint.pydocstyle]
convention = "google"            # "google" | "numpy" | "pep257"

[tool.ruff.lint.flake8-type-checking]
runtime-evaluated-base-classes = ["pydantic.BaseModel"]
runtime-evaluated-decorators = ["attrs.define"]

For the complete rule catalog snapshot, see references/rules.md. For full configuration reference, see references/configuration.md.

Debugging

ruff check --show-settings .     # Dump resolved config
ruff check --show-files .        # List files that would be checked
ruff check --statistics .        # Count violations per rule
ruff rule E501                   # Explain a specific rule
ruff linter                      # List all available linters

Non-Obvious Gotchas

GotchaExplanation
TCH -> TC renameTCH prefix is now legacy alias for TC. Use TC in new configs
No third-party pluginsRuff re-implements Flake8 plugins in Rust. Cannot install additional ones
isort differencesSome edge cases differ from real isort (aliased imports, inline comments)
Notebooks: per-cell scopeE402 checked per-cell, not per-file. Each cell is its own module scope
--fix can break codeEven "safe" fixes can break dynamic Python. Review diffs for F401, UP, B rules
ruff-lsp is deadUse ruff server (built into binary). The separate ruff-lsp package was archived Dec 2025
Range formattingruff format --range=10:1-20:1 formats only lines 10-20 (single file, not notebooks)

Anti-Patterns

Anti-PatternFix
Blanket # noqa on every lineFix the violations or use per-file-ignores
select = ["ALL"] with no ignoreAlways pair with formatter conflict rules and overly strict rules
Running ruff format before ruff check --fixLint fixes first (may reorder imports), then format
Using ruff-lspSwitch to ruff server (built-in, maintained)
Ignoring E501 without using formatterEither use ruff format OR enforce E501, not neither
select in child config without knowing it resetsUse extend-select to preserve parent's rule set

What This Skill is NOT

  • Not a replacement for ruff --help or ruff rule <CODE> for specific rule docs
  • Not for type checking (use ty)
  • Not for package management (use uv)
  • Not for third-party Flake8 plugins that ruff hasn't re-implemented

Related skills

Pythonbackend

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.