changelogs/0.15.x.md
Released on 2026-02-03.
Check out the blog post for a migration guide and overview of the changes!
Ruff now formats your code according to the 2026 style guide. See the formatter section below or in the blog post for a detailed list of changes.
The linter now supports block suppression comments. For example, to suppress N803 for all parameters in this function:
# ruff: disable[N803]
def foo(
legacyArg1,
legacyArg2,
legacyArg3,
legacyArg4,
): ...
# ruff: enable[N803]
See the documentation for more details.
The ruff:alpine Docker image is now based on Alpine 3.23 (up from 3.21).
The ruff:debian and ruff:debian-slim Docker images are now based on Debian 13 "Trixie" instead of Debian 12 "Bookworm."
Binaries for the ppc64 (64-bit big-endian PowerPC) architecture are no longer included in our releases. It should still be possible to build Ruff manually for this platform, if needed.
Ruff now resolves all extended configuration files before falling back on a default Python version.
The following rules have been stabilized and are no longer in preview:
blocking-http-call-httpx-in-async-function
(ASYNC212)blocking-path-method-in-async-function
(ASYNC240)blocking-input-in-async-function
(ASYNC250)map-without-explicit-strict
(B912)if-exp-instead-of-or-operator
(FURB110)single-item-membership-test
(FURB171)missing-maxsplit-arg (PLC0207)unnecessary-lambda (PLW0108)unnecessary-empty-iterable-within-deque-call
(RUF037)in-empty-collection (RUF060)legacy-form-pytest-raises
(RUF061)non-octal-permissions (RUF064)invalid-rule-code (RUF102)invalid-suppression-comment
(RUF103)unmatched-suppression-comment
(RUF104)replace-str-enum (UP042)The following behaviors have been stabilized:
--output-format flag is now respected when running Ruff in --watch mode, and the full output format is now used by default, matching the regular CLI output.builtin-attribute-shadowing (A003) now detects the use of shadowed built-in names in additional contexts like decorators, default arguments, and other attribute definitions.duplicate-union-member (PYI016) now considers typing.Optional when searching for duplicate union members.split-static-string (SIM905) now offers an autofix when the maxsplit argument is provided, even without a sep argument.dict-get-with-none-default (SIM910) now applies to more types of key expressions.super-call-with-parameters (UP008) now has a safe fix when it will not delete comments.unnecessary-default-type-args (UP043) now applies to stub (.pyi) files on Python versions before 3.13.This release introduces the new 2026 style guide, with the following changes:
except clauses will now be removed on Python 3.14 and
later (#20768)as captures in match statements (#21176)Apply formatting to Markdown code blocks (#22470, #22990, #22996)
See the documentation for more details.
fix_title sub-diagnostic appears (#23044)FastAPI] Add sub-diagnostic explaining why a fix was unavailable (FAST002) (#22565)flake8-annotations] Don't suggest NoReturn for functions raising NotImplementedError (ANN201, ANN202, ANN205, ANN206) (#21311)pyupgrade] Make fix unsafe if it deletes comments (UP017) (#22873)pyupgrade] Make fix unsafe if it deletes comments (UP020) (#22872)pyupgrade] Make fix unsafe if it deletes comments (UP033) (#22871)refurb] Do not add abc.ABC if already present (FURB180) (#22234)refurb] Make fix unsafe if it deletes comments (FURB110) (#22768)ruff] Add sub-diagnostics with permissions (RUF064) (#22972)didOpen instead of .ipynb file extension (#22810)--color CLI option to force colored output (#22806)- stdin convention in CLI help text (#22817)refurb] Change example to re.search with ^ anchor (FURB167) (#22984)pydocstyle] Clarify which quote styles are allowed (D300) (#22825)flake8-bugbear] Improve docs for no-explicit-stacklevel (B028) (#22538)Released on 2026-02-12.
airflow] Add ruff rules to catch deprecated Airflow imports for Airflow 3.1 (AIR321) (#22376)airflow] Third positional parameter not named ti_key should be flagged for BaseOperatorLink.get_link (AIR303) (#22828)flake8-gettext] Fix false negatives for plural argument of ngettext (INT001, INT002, INT003) (#21078)pyflakes] Fix infinite loop in preview fix for unused-import (F401) (#23038)pygrep-hooks] Detect non-existent mock methods in standalone expressions (PGH005) (#22830)pylint] Allow dunder submodules and improve diagnostic range (PLC2701) (#22804)pyupgrade] Improve diagnostic range for tuples (UP024) (#23013)refurb] Check subscripts in tuple do not use lambda parameters in reimplemented-operator (FURB118) (#23079)ruff] Detect mutable defaults in field calls (RUF008) (#23046)ruff] Ignore std cmath.inf (RUF069) (#23120)ruff] New rule float-equality-comparison (RUF069) (#20585)pycon Markdown code blocks (#23112)FAST001 (#23224)FAST003 autofix (#23227)InitVar for __post_init__ that references PEP 695 type parameters (#23226)RUF020 target for multiline fixes (#23210)UP007 target for multiline fixes (#23208)pyupgrade] Fix syntax error on string with newline escape and comment (UP037) (#22968)ruff instead of Ruff as the program name in GitHub output format (#23240)PT006] Fix syntax error when unpacking nested tuples in parametrize fixes (#22441) (#22464)airflow] Catch deprecated attribute access from context key for Airflow 3.0 (AIR301) (#22850)airflow] Capture deprecated arguments and a decorator (AIR301) (#23170)flake8-boolean-trap] Add multiprocessing.Value to excluded functions for FBT003 (#23010)flake8-bugbear] Add a secondary annotation showing the previous occurrence (B033) (#22634)flake8-type-checking] Add sub-diagnostic showing the runtime use of an annotation (TC004) (#23091)isort] Support configurable import section heading comments (#23151)ruff] Improve the diagnostic for RUF012 (#23202)format --check --silent (#17736)runtime-evaluated-decorators example (#23187)Released on 2026-02-19.
Expand the default rule set (#23385)
In preview, Ruff now enables a significantly expanded default rule set of 412 rules, up from the stable default set of 59 rules. The new rules are mostly a superset of the stable defaults, with the exception of these rules, which are removed from the preview defaults:
multiple-imports-on-one-line (E401)module-import-not-at-top-of-file (E402)module-import-not-at-top-of-file (E701)multiple-statements-on-one-line-semicolon (E702)useless-semicolon (E703)none-comparison (E711)true-false-comparison (E712)not-in-test (E713)not-is-test (E714)type-comparison (E721)lambda-assignment (E731)ambiguous-variable-name (E741)ambiguous-class-name (E742)ambiguous-function-name (E743)undefined-local-with-import-star (F403)undefined-local-with-import-star-usage (F405)undefined-local-with-nested-import-star-usage (F406)forward-annotation-syntax-error (F722)If you use preview and prefer the old defaults, you can restore them with configuration like:
# ruff.toml
[lint]
select = ["E4", "E7", "E9", "F"]
# pyproject.toml
[tool.ruff.lint]
select = ["E4", "E7", "E9", "F"]
If you do give them a try, feel free to share your feedback in the GitHub discussion!
[flake8-pyi] Also check string annotations (PYI041) (#19023)
flake8-async] Fix in_async_context logic (#23426)ruff] Fix for RUF102 should delete entire comment (#23380)ruff] Suppress diagnostic for strings with backslashes in interpolations before Python 3.12 (RUF027) (#21069)flake8-bugbear] Fix B023 false positive for immediately-invoked lambdas (#23294)flake8-executable] Allow global flags in uv shebangs (EXE003) (#22582)pyupgrade] Fix handling of typing.{io,re} (UP035) (#23131)ruff] Detect PLC0207 on chained str.split() calls (#23275)noqa warning (#23270)Q004 to the list of conflicting rules (#23340)ruff] Expand lint.external docs and add sub-diagnostic (RUF100, RUF102) (#23268)Released on 2026-02-26.
Drop explicit support for .qmd file extension (#23572)
This can now be enabled instead by setting the extension option:
# ruff.toml
extension = { qmd = "markdown" }
# pyproject.toml
[tool.ruff]
extension = { qmd = "markdown" }
Include configured extensions in file discovery (#23400)
[flake8-bandit] Allow suspicious imports in TYPE_CHECKING blocks (S401-S415) (#23441)
[flake8-bugbear] Allow B901 in pytest hook wrappers (#21931)
[flake8-import-conventions] Add missing conventions from upstream (ICN001, ICN002) (#21373)
[pydocstyle] Add rule to enforce docstring section ordering (D420) (#23537)
[pylint] Implement swap-with-temporary-variable (PLR1712) (#22205)
[ruff] Add unnecessary-assign-before-yield (RUF070) (#23300)
[ruff] Support file-level noqa in RUF102 (#23535)
[ruff] Suppress diagnostic for invalid f-strings before Python 3.12 (RUF027) (#23480)
[flake8-bandit] Don't flag BaseLoader/CBaseLoader as unsafe (S506) (#23510)
I002 and PYI025 (#23352)pyflakes] Fix false positive for @overload from lint.typing-modules (F811) (#23357)pyupgrade] Fix false positive for TypeVar default before Python 3.12 (UP046) (#23540)pyupgrade] Fix handling of \N in raw strings (UP032) (#22149)Render sub-diagnostics in the GitHub output format (#23455)
[flake8-bugbear] Tag certain B007 diagnostics as unnecessary (#23453)
[ruff] Ignore unknown rule codes in RUF100 (#23531)
These are now flagged by RUF102 instead.
pydocstyle] Fix double comma in description of D404 (#23440)find_ruff_bin) for parity with uv (#23406)Released on 2026-02-26.
This is a follow-up release to 0.15.3 that resolves a panic when the new rule PLR1712 was enabled with any rule that analyzes definitions, such as many of the ANN or D rules.
pyflakes] Suppress false positive in F821 for names used before del in stub files (#23550)Released on 2026-03-05.
perflint] Extend PERF102 to comprehensions and generators (#23473)refurb] Fix FURB101 and FURB103 false positives when I/O variable is used later (#23542)ruff] Add fix for none-not-at-end-of-union (RUF036) (#22829)ruff] Fix false positive for re.split with empty string pattern (RUF055) (#23634)fastapi] Handle callable class dependencies with __call__ method (FAST003) (#23553)pydocstyle] Fix numpy section ordering (D420) (#23685)pyflakes] Fix false positive for names shadowing re-exports (F811) (#23356)pyupgrade] Avoid inserting redundant None elements in UP045 (#23459)Released on 2026-03-12.
lazy import parsing (#23755)airflow] Flag Variable.get() calls outside of task execution context (AIR003) (#23584)airflow] Flag runtime-varying values in DAG/task constructor arguments (AIR304) (#23631)flake8-bugbear] Implement delattr-with-constant (B043) (#23737)flake8-tidy-imports] Add TID254 to enforce lazy imports (#23777)flake8-tidy-imports] Allow users to ban lazy imports with TID254 (#23847)isort] Retain lazy keyword when sorting imports (#23762)pyupgrade] Add from __future__ import annotations automatically (UP006) (#23260)refurb] Support newline parameter in FURB101 for Python 3.13+ (#23754)ruff] Add os-path-commonprefix (RUF071) (#23814)ruff] Add unsafe fix for os-path-commonprefix (RUF071) (#23852)ruff] Limit RUF036 to typing contexts; make it unsafe for non-typing-only (#23765)ruff] Use starred unpacking for RUF017 in Python 3.15+ (#23789)--add-noqa creating unwanted leading whitespace (#23773)--add-noqa breaking shebangs (#23577)flake8-annotations] Fix stack overflow in ANN401 on quoted annotations with escape sequences (#23912)pep8-naming] Check naming conventions in match pattern bindings (N806, N815, N816) (#23899)perflint] Fix comment duplication in fixes (PERF401, PERF403) (#23729)pyupgrade] Properly trigger super change in nested class (UP008) (#22677)ruff] Avoid syntax errors in RUF036 fixes (#23764)flake8-bandit] Flag S501 with requests.request (#23873)flake8-executable] Fix WSL detection in non-Docker containers (#22879)flake8-print] Ignore pprint calls with stream= (#23787)flake8-bugbear] Fix misleading description for B904 (#23731)Released on 2026-03-19.
pycodestyle] Recognize pyrefly: as a pragma comment (E501) (#24019)pylint] Improve phrasing (PLC0208) (#24033)Released on 2026-03-26.
ruff] New rule unnecessary-if (RUF050) (#24114)ruff] New rule useless-finally (RUF072) (#24165)ruff] New rule f-string-percent-format (RUF073): warn when using % operator on an f-string (#24162)pyflakes] Recognize frozendict as a builtin for Python 3.15+ (#24100)flake8-async] Use fully-qualified anyio.lowlevel import in autofix (ASYNC115) (#24166)flake8-bandit] Check tuple arguments for partial paths in S607 (#24080)pyflakes] Skip undefined-name (F821) for conditionally deleted variables (#24088)E501/W505/formatter: Exclude nested pragma comments from line width calculation (#24071)%foo? parsing in IPython assignment expressions (#24152)analyze graph: resolve string imports that reference attributes, not just modules (#24058)eradicate] ignore ty: ignore comments in ERA001 (#24192)flake8-bandit] Treat sys.executable as trusted input in S603 (#24106)flake8-self] Recognize Self annotation and self assignment in SLF001 (#24144)pyflakes] F507: Fix false negative for non-tuple RHS in %-formatting (#24142)refurb] Parenthesize generator arguments in FURB142 fixer (#24200)extend-ignore and extend-select settings documentation (#24064)Released on 2026-04-02.
pyflakes] Flag annotated variable redeclarations as F811 in preview mode (#24244)ruff] Allow dunder-named assignments in non-strict mode for RUF067 (#24089)flake8-errmsg] Avoid shadowing existing msg in fix for EM101 (#24363)flake8-simplify] Ignore pre-initialization references in SIM113 (#24235)pycodestyle] Fix W391 fixes for consecutive empty notebook cells (#24236)pyupgrade] Fix UP008 nested class matching (#24273)pyupgrade] Ignore strings with string-only escapes (UP012) (#16058)ruff] RUF072: skip formfeeds on dedent (#24308)ruff] Avoid re-using symbol in RUF024 fix (#24316)ruff] Parenthesize expression in RUF050 fix (#24234)flake8-simplify] Suppress SIM105 for except* before Python 3.12 (#23869)pyflakes] Extend F507 to flag %-format strings with zero placeholders (#24215)pyupgrade] UP018 should detect more unnecessarily wrapped literals (UP018) (#24093)pyupgrade] Fix UP008 callable scope handling to support lambdas (#24274)ruff] RUF010: Mark fix as unsafe when it deletes a comment (#24270)nested-string-quote-style formatting option (#24312)flake8-bugbear] Clarify RUF071 fix safety for non-path string comparisons (#24149)flake8-type-checking] Clarify import cycle wording for TC001/TC002/TC003 (#24322)| (#24343)Released on 2026-04-09.
flake8-logging] Allow closures in except handlers (LOG004) (#24464)flake8-self] Make SLF diagnostics robust to non-self-named variables (#24281)flake8-simplify] Make the fix for collapsible-if safe in preview (SIM102) (#24371)E502 fixes in f-strings and t-strings (#24410)dedent_to (#24381)pyupgrade] Fix panic caused by handling of octals (UP012) (#24390)ruff] Treat f-string interpolation as potential side effect (RUF019) (#24426)Released on 2026-04-16.
ruff] Ignore RUF029 when function is decorated with asynccontextmanager (#24642)airflow] Implement airflow-xcom-pull-in-template-string (AIR201) (#23583)flake8-bandit] Fix S103 false positives and negatives in mask analysis (#24424)flake8-async] Omit overridden methods for ASYNC109 (#24648)flake8-async] Add override mention to ASYNC109 docs (#24666)vim.lsp.config (#24577)Released on 2026-04-24.
#ruff:file-ignore file-level suppressions (#23599)#ruff:ignore logical-line suppressions (#23404)airflow] Implement task-branch-as-short-circuit (AIR004) (#23579)flake8-bugbear] Fix break/continue handling in loop-iterator-mutation (B909) (#24440)pylint] Fix PLC2701 for type parameter scopes (#24576)pandas-vet] Suggest .array as well in PD011 (#24805)pylint] Fix PLR0124 description not to claim self-comparison always returns the same value (#24749)pyupgrade] Expand docs on reusable TypeVars and scoping (UP046) (#24153)Released on 2026-05-14.
pylint] Standardize diagnostic message (PLR0914, PLR0917) (#24996)F811 false positive for class methods (#24933)eradicate] Fix false positive for lines with leading whitespace (ERA001) (#25122)flake8-pyi] Fix false positive for f-string debug specifier (PYI016) (#24098)PYI034 for in-place operations to enclosing class (#24511)global (#24902)isort] Avoid constructing glob::Patterns for literal known modules (#25123)line-length setting (#24962)D203 to rules that conflict with the formatter (#25044)COM819 and formatter interaction (#25045)NotImplemented is a value, not an exception (F901) (#25054)Released on 2026-05-21.
airflow] Implement airflow-task-implicit-multiple-outputs (AIR202) (#25152)flake8-use-pathlib] Mark PTH101 fix as unsafe when first argument is a class attribute annotated as int (#25086)pylint] Implement too-many-try-statements (W0717) (#23970)ruff] Add incorrect-decorator-order (RUF074) (#23461)ruff] Add fallible-context-manager (RUF075) (#22844)frozenset annotations as immutable (#25251)flake8-type-checking] Avoid strict behavior when future-annotations are enabled (TC001, TC002, TC003) (#25035)pylint] Avoid false positives in else clause (PLR1733) (#25177)flake8-comprehensions] Skip C417 for lambdas with positional-only parameters (#25272)flake8-simplify] Preserve f-string source verbatim in SIM101 fix (#25061)ruff_python_stdlib APIs (#25273)Released on 2026-05-28.
pyflakes] Report duplicate imports in typing.TYPE_CHECKING block (F811) (#22560)pyflakes] Treat function-scope bare annotations as locals per PEP 526 (F821) (#21540)TokenValue drops in the lexer (#25300)Vec size (#25354)ThinVec in AST to shrink Stmt (#25361)line-length example for --config option (#25389)flake8-comprehensions] Document RecursionError edge case in __len__ (C416) (#25286)mccabe] Improve example (C901) (#25287)pyupgrade] Clarify fix safety docs (UP007, UP045) (#25288)refurb] Document FURB192 exception change for empty sequences (#25317)ruff] Document false negative for user-defined types (RUF013) (#25289)codeAction/resolve requests that contain no or no valid URL (#25365)Released on 2026-06-04.
flake8-async] Implement yield-in-context-manager-in-async-generator (ASYNC119) (#24644)pylint] Narrow diagnostic range and exclude cases without exception handlers (PLW0717) (#25440)ruff] Treat yield before break from a terminal loop as terminal (RUF075) (#25447)eradicate] Avoid flagging ruff:ignore comments as code (ERA001) (#25537)eradicate] Fix ERA001/RUF100 conflict when noqa is on commented-out code (#25414)pyflakes] Avoid removing the format call when it would change behavior (F523) (#25320)pylint] Avoid syntax errors in invalid character replacements in f-strings before Python 3.12 (PLE2510, PLE2512, PLE2513, PLE2514, PLE2515) (#25544)pyupgrade] Avoid converting format calls with more kinds of side effects (UP032) (#25484)flake8-pytest-style] Avoid fixes for ambiguous argnames and argvalues combinations (PT006) (#24776)pydocstyle] Improve discoverability of rules enabled for each convention (#24973)ruff] Restore example code for Python versions before 3.15 (RUF017) (#25439)bin/active → bin/activate in tutorial (#25473)Released on 2026-06-11.
ignore comments within a disable/enable pair (#25845)ruff:ignore suppressions (#25673)flake8-async] Add trio.as_safe_channel to safe decorators (ASYNC119) (#25775)flake8-pytest-style] Also check pytest_asyncio fixtures (#25375)ruff] Ban pytest autouse fixtures (RUF076) (#25477)pyupgrade] Add from __future__ import annotations automatically (UP007, UP045) (#23259)ruff:enable or ruff:disable appears where ruff:ignore is expected (#25700)pyupgrade] Preserve leading empty literals to avoid syntax errors (UP032) (#25491)flake8-pytest-style] Clarify diagnostic message for single parameters (PT007) (#25592)numpy] Drop autofix for np.in1d (NPY201) (#25612)pylint] Exempt Python version comparisons (PLR2004) (#25743)Vecs with correct capacity for common cases (#25451)ruff rule (#25640)Released on 2026-06-18.
ruff:ignore comments (#25791)pydocstyle] Prevent property docstrings starting with verbs (D421) (#23775)flake8-pyi] Extend PYI033 to Python files (#26129)flake8-pyi] Rename PYI033 to legacy-type-comment (#26131)ThinVec for call keywords (#25999)flake8-tidy-imports] Add fix safety section (TID252) (#17491)__debug__ lambda parameters (#26022)_ as a match-pattern target (#25977)yield expressions after commas (#26024)Released on 2026-06-23.
pylint] Update fix suggestions for __floor__, __trunc__, __length_hint__, and __matmul__ variants (PLC2801) (#26239)flake8-pyi] Note that PYI051 is an opinionated stylistic rule (#26179)pyupgrade] Clarify UP029 as a Python 2 compatibility rule (#26243)Released on 2026-06-25.
noqa shebang handling in ruff:ignore comments (#26286)ruff] Remove pytest-fixture-autouse (RUF076) (#26240, #26371)ruff_python_parser README for crates.io (#26315)perflint] Clarify that PERF402 applies to any iterable (#26242)Released on 2026-07-09.
--add-ignore for adding ruff:ignore comments (#26346)flake8-comprehensions] Drop C409 tuple comprehension preview behavior (#25707)pyupgrade] Lint and fix use of deprecated abc decorators (UP051) (#26417)flake8-implicit-str-concat] Fix ISC003 autofix incorrectly stripping + from comments (#26554)flake8-executable] Mark EXE004 fix as unsafe (#26033)flake8-pyi] Mark PYI061 fixes as unsafe in Python files (#26533)pydocstyle] Skip overload-with-docstring in stub files (D418) (#26318)fits_element in formatter (#26429)--extend-exclude to ruff format (#26372)flake8-bandit] Fix misleading docstring for mako-templates (S702) (#26432)ruff] Fix non-triggering example for if-key-in-dict-del (RUF051) (#26433)Released on 2026-07-16.
pycodestyle] Add an autofix for E402 (#22212)refurb] Allow subclassing builtins in stub files (FURB189) (#26812)ruff] Add rule to replace noqa comments with ruff:ignore (RUF105) (#26423)ruff] Add rule to use human-readable names in ruff:ignore comments (RUF106) (#26682)ruff] Add rule to use human-readable names in configuration selectors (RUF201) (#26772)flake8-pyi] Fix false positive in __all__ (PYI053) (#26872)pylint] Ignore mutable type updates in redefined-loop-name (PLW2901) (#25733)flake8-datetimez] Clarify naming guidance for datetime.today (DTZ002) (#26658)pycodestyle] Document E731 fix safety (#26847)ruff] Clarify intentional async contexts for unused-async (RUF029) (#26641)