Released on 2026-03-26.
- [
ruff] New ruleunnecessary-if(RUF050) (#24114) - [
ruff] New ruleuseless-finally(RUF072) (#24165) - [
ruff] New rulef-string-percent-format(RUF073): warn when using%operator on an f-string (#24162) - [
pyflakes] Recognizefrozendictas a builtin for Python 3.15+ (#24100)
- [
flake8-async] Use fully-qualifiedanyio.lowlevelimport in autofix (ASYNC115) (#24166) - [
flake8-bandit] Check tuple arguments for partial paths inS607(#24080) - [
pyflakes] Skipundefined-name(F821) for conditionally deleted variables (#24088) E501/W505/formatter: Exclude nested pragma comments from line width calculation (#24071)- Fix
%foo?parsing in IPython assignment expressions (#24152) analyze graph: resolve string imports that reference attributes, not just modules (#24058)
- [
eradicate] ignorety: ignorecomments inERA001(#24192) - [
flake8-bandit] Treatsys.executableas trusted input inS603(#24106) - [
flake8-self] RecognizeSelfannotation andselfassignment inSLF001(#24144) - [
pyflakes]F507: Fix false negative for non-tuple RHS in%-formatting (#24142) - [
refurb] Parenthesize generator arguments inFURB142fixer (#24200)
- Speed up diagnostic rendering (#24146)
- Warn when Markdown files are skipped due to preview being disabled (#24150)
- Clarify
extend-ignoreandextend-selectsettings documentation (#24064) - Mention AI policy in PR template (#24198)
- Use trusted publishing for NPM packages (#24171)
- @bitloi
- @Sim-hu
- @mvanhorn
- @chinar-amrutkar
- @markjm
- @RenzoMXD
- @vivekkhimani
- @seroperson
- @moktamd
- @charliermarsh
- @ntBre
- @zanieb
- @dylwil3
- @MichaReiser
Released on 2026-03-19.
- [
pycodestyle] Recognizepyrefly:as a pragma comment (E501) (#24019)
- Don't return code actions for non-Python documents (#23905)
- Add company AI policy to contributing guide (#24021)
- Document editor features for Markdown code formatting (#23924)
- [
pylint] Improve phrasing (PLC0208) (#24033)
- Use PEP 639 license information (#19661)
Released on 2026-03-12.
- Add support for
lazyimport parsing (#23755) - Add support for star-unpacking of comprehensions (PEP 798) (#23788)
- Reject semantic syntax errors for lazy imports (#23757)
- Drop a few rules from the preview default set (#23879)
- [
airflow] FlagVariable.get()calls outside of task execution context (AIR003) (#23584) - [
airflow] Flag runtime-varying values in DAG/task constructor arguments (AIR304) (#23631) - [
flake8-bugbear] Implementdelattr-with-constant(B043) (#23737) - [
flake8-tidy-imports] AddTID254to enforce lazy imports (#23777) - [
flake8-tidy-imports] Allow users to ban lazy imports withTID254(#23847) - [
isort] Retainlazykeyword when sorting imports (#23762) - [
pyupgrade] Addfrom __future__ import annotationsautomatically (UP006) (#23260) - [
refurb] Supportnewlineparameter inFURB101for Python 3.13+ (#23754) - [
ruff] Addos-path-commonprefix(RUF071) (#23814) - [
ruff] Add unsafe fix for os-path-commonprefix (RUF071) (#23852) - [
ruff] LimitRUF036to typing contexts; make it unsafe for non-typing-only (#23765) - [
ruff] Use starred unpacking forRUF017in Python 3.15+ (#23789)
- Fix
--add-noqacreating unwanted leading whitespace (#23773) - Fix
--add-noqabreaking shebangs (#23577) - [formatter] Fix lambda body formatting for multiline calls and subscripts (#23866)
- [formatter] Preserve required annotation parentheses in annotated assignments (#23865)
- [formatter] Preserve type-expression parentheses in the formatter (#23867)
- [
flake8-annotations] Fix stack overflow inANN401on quoted annotations with escape sequences (#23912) - [
pep8-naming] Check naming conventions inmatchpattern bindings (N806,N815,N816) (#23899) - [
perflint] Fix comment duplication in fixes (PERF401,PERF403) (#23729) - [
pyupgrade] Properly triggersuperchange in nested class (UP008) (#22677) - [
ruff] Avoid syntax errors inRUF036fixes (#23764)
- [
flake8-bandit] FlagS501withrequests.request(#23873) - [
flake8-executable] Fix WSL detection in non-Docker containers (#22879) - [
flake8-print] Ignorepprintcalls withstream=(#23787)
- Update docs for Markdown code block formatting (#23871)
- [
flake8-bugbear] Fix misleading description forB904(#23731)
- @zsol
- @carljm
- @ntBre
- @Bortlesboat
- @sososonia-cyber
- @chirizxc
- @leandrobbraga
- @11happy
- @Acelogic
- @anishgirianish
- @amyreese
- @xvchris
- @charliermarsh
- @getehen
- @Dev-iL
Released on 2026-03-05.
- Discover Markdown files by default in preview mode (#23434)
- [
perflint] ExtendPERF102to comprehensions and generators (#23473) - [
refurb] FixFURB101andFURB103false positives when I/O variable is used later (#23542) - [
ruff] Add fix fornone-not-at-end-of-union(RUF036) (#22829) - [
ruff] Fix false positive forre.splitwith 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 redundantNoneelements inUP045(#23459)
- Document extension mapping for Markdown code formatting (#23574)
- Update default Python version examples (#23605)
- Publish releases to Astral mirror (#23616)
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.
- Fix panic on access to definitions after analyzing definitions (#23588)
- [
pyflakes] Suppress false positive inF821for names used beforedelin stub files (#23550)
Released on 2026-02-26.
-
Drop explicit support for
.qmdfile extension (#23572)This can now be enabled instead by setting the
extensionoption:# ruff.toml extension = { qmd = "markdown" } # pyproject.toml [tool.ruff] extension = { qmd = "markdown" }
-
Include configured extensions in file discovery (#23400)
-
[
flake8-bandit] Allow suspicious imports inTYPE_CHECKINGblocks (S401-S415) (#23441) -
[
flake8-bugbear] AllowB901in 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] Implementswap-with-temporary-variable(PLR1712) (#22205) -
[
ruff] Addunnecessary-assign-before-yield(RUF070) (#23300) -
[
ruff] Support file-level noqa inRUF102(#23535) -
[
ruff] Suppress diagnostic for invalid f-strings before Python 3.12 (RUF027) (#23480) -
[
flake8-bandit] Don't flagBaseLoader/CBaseLoaderas unsafe (S506) (#23510)
- Avoid infinite loop between
I002andPYI025(#23352) - [
pyflakes] Fix false positive for@overloadfromlint.typing-modules(F811) (#23357) - [
pyupgrade] Fix false positive forTypeVardefault before Python 3.12 (UP046) (#23540) - [
pyupgrade] Fix handling of\Nin raw strings (UP032) (#22149)
-
Render sub-diagnostics in the GitHub output format (#23455)
-
[
flake8-bugbear] Tag certainB007diagnostics as unnecessary (#23453) -
[
ruff] Ignore unknown rule codes inRUF100(#23531)These are now flagged by
RUF102instead.
- Fix missing settings links for several linters (#23519)
- Update isort action comments heading (#23515)
- [
pydocstyle] Fix double comma in description ofD404(#23440)
- Update the Python module (notably
find_ruff_bin) for parity with uv (#23406)
- @zanieb
- @o1x3
- @assadyousuf
- @kar-ganap
- @denyszhak
- @amyreese
- @carljm
- @anishgirianish
- @Bnyro
- @danparizher
- @ntBre
- @gcomneno
- @jaap3
- @stakeswky
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] Fixin_async_contextlogic (#23426) - [
ruff] Fix forRUF102should delete entire comment (#23380) - [
ruff] Suppress diagnostic for strings with backslashes in interpolations before Python 3.12 (RUF027) (#21069) - [
flake8-bugbear] FixB023false positive for immediately-invoked lambdas (#23294) - [parser] Fix false syntax error for match-like annotated assignments (#23297)
- [parser] Fix indentation tracking after line continuations (#23417)
- [
flake8-executable] Allow global flags in uv shebangs (EXE003) (#22582) - [
pyupgrade] Fix handling oftyping.{io,re}(UP035) (#23131) - [
ruff] DetectPLC0207on chainedstr.split()calls (#23275)
- Remove invalid inline
noqawarning (#23270)
- Add extension mapping to configuration file options (#23384)
- Add
Q004to the list of conflicting rules (#23340) - [
ruff] Expandlint.externaldocs and add sub-diagnostic (RUF100,RUF102) (#23268)
- @dylwil3
- @Jkhall81
- @danparizher
- @dhruvmanila
- @harupy
- @ngnpope
- @amyreese
- @kar-ganap
- @robsdedude
- @shaanmajid
- @ntBre
- @toslunar
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 namedti_keyshould be flagged forBaseOperatorLink.get_link(AIR303) (#22828) - [
flake8-gettext] Fix false negatives for plural argument ofngettext(INT001,INT002,INT003) (#21078) - [
pyflakes] Fix infinite loop in preview fix forunused-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 inreimplemented-operator(FURB118) (#23079) - [
ruff] Detect mutable defaults infieldcalls (RUF008) (#23046) - [
ruff] Ignore stdcmath.inf(RUF069) (#23120) - [
ruff] New rulefloat-equality-comparison(RUF069) (#20585) - Don't format unlabeled Markdown code blocks (#23106)
- Markdown formatting support in LSP (#23063)
- Support Quarto Markdown language markers (#22947)
- Support formatting
pyconMarkdown code blocks (#23112) - Use extension mapping to select Markdown code block language (#22934)
- Avoid false positive for undefined variables in
FAST001(#23224) - Avoid introducing syntax errors for
FAST003autofix (#23227) - Avoid suggesting
InitVarfor__post_init__that references PEP 695 type parameters (#23226) - Deduplicate type variables in generic functions (#23225)
- Fix exception handler parenthesis removal for Python 3.14+ (#23126)
- Fix f-string middle panic when parsing t-strings (#23232)
- Wrap
RUF020target for multiline fixes (#23210) - Wrap
UP007target for multiline fixes (#23208) - Fix missing diagnostics for last range suppression in file (#23242)
- [
pyupgrade] Fix syntax error on string with newline escape and comment (UP037) (#22968)
- Use
ruffinstead ofRuffas the program name in GitHub output format (#23240) - [
PT006] Fix syntax error when unpacking nested tuples inparametrizefixes (#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] Addmultiprocessing.Valueto excluded functions forFBT003(#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 forRUF012(#23202)
- Suppress diagnostic output for
format --check --silent(#17736)
- Add tabbed shell completion documentation (#23169)
- Explain how to enable Markdown formatting for pre-commit hook (#23077)
- Fixed import in
runtime-evaluated-decoratorsexample (#23187) - Update ruff server contributing guide (#23060)
- Exclude WASM artifacts from GitHub releases (#23221)
- @mkniewallner
- @bxff
- @dylwil3
- @Avasam
- @amyreese
- @charliermarsh
- @Alex-ley-scrub
- @Kalmaegi
- @danparizher
- @AiyionPrime
- @eureka928
- @11happy
- @Jkhall81
- @chirizxc
- @leandrobbraga
- @tvatter
- @anishgirianish
- @shaanmajid
- @ntBre
- @sjyangkevin
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
N803for 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:alpineDocker image is now based on Alpine 3.23 (up from 3.21). -
The
ruff:debianandruff:debian-slimDocker 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:
- The
--output-formatflag is now respected when running Ruff in--watchmode, and thefulloutput 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 considerstyping.Optionalwhen searching for duplicate union members.split-static-string(SIM905) now offers an autofix when themaxsplitargument is provided, even without asepargument.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:
- Lambda parameters are now kept on the same line and lambda bodies will be parenthesized to let them break across multiple lines (#21385)
- Parentheses around tuples of exceptions in
exceptclauses will now be removed on Python 3.14 and later (#20768) - A single empty line is now permitted at the beginning of function bodies (#21110)
- Parentheses are avoided for long
ascaptures inmatchstatements (#21176) - Extra spaces between escaped quotes and ending triple quotes can now be omitted (#17216)
- Blank lines are now enforced before classes with decorators in stub files (#18888)
-
Apply formatting to Markdown code blocks (#22470, #22990, #22996)
See the documentation for more details.
- Fix suppression indentation matching (#22903)
- Customize where the
fix_titlesub-diagnostic appears (#23044) - [
FastAPI] Add sub-diagnostic explaining why a fix was unavailable (FAST002) (#22565) - [
flake8-annotations] Don't suggestNoReturnfor functions raisingNotImplementedError(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 addabc.ABCif already present (FURB180) (#22234) - [
refurb] Make fix unsafe if it deletes comments (FURB110) (#22768) - [
ruff] Add sub-diagnostics with permissions (RUF064) (#22972)
- Identify notebooks by LSP
didOpeninstead of.ipynbfile extension (#22810)
- Add
--colorCLI option to force colored output (#22806)
- Document
-stdin convention in CLI help text (#22817) - [
refurb] Change example tore.searchwith^anchor (FURB167) (#22984) - Fix link to Sphinx code block directives (#23041)
- [
pydocstyle] Clarify which quote styles are allowed (D300) (#22825) - [
flake8-bugbear] Improve docs forno-explicit-stacklevel(B028) (#22538)
- Update MSRV to 1.91 (#22874)
- @danparizher
- @chirizxc
- @amyreese
- @Jkhall81
- @cwkang1998
- @manzt
- @11happy
- @hugovk
- @caiquejjx
- @ntBre
- @akawd
- @konstin
See changelogs/0.9.x
See changelogs/0.8.x
See changelogs/0.7.x
See changelogs/0.6.x
See changelogs/0.5.x
See changelogs/0.4.x
See changelogs/0.3.x
See changelogs/0.2.x
See changelogs/0.1.x