diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md
index 93adf49c75825..055796559fab5 100644
--- a/BREAKING_CHANGES.md
+++ b/BREAKING_CHANGES.md
@@ -1,5 +1,9 @@
# Breaking Changes
+## 0.9.0
+
+Ruff now formats your code according to the 2025 style guide. As a result, your code might now get formatted differently. See the [changelog](./CHANGELOG.md#090) for a detailed list of changes.
+
## 0.8.0
- **Default to Python 3.9**
diff --git a/CHANGELOG.md b/CHANGELOG.md
index efb7c950d463d..db0d827e82071 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,93 @@
# Changelog
+## 0.9.0
+
+Check out the [blog post](https://astral.sh/blog/ruff-v0.9.0) for a migration guide and overview of the changes!
+
+### Breaking changes
+
+Ruff now formats your code according to the 2025 style guide. As a result, your code might now get formatted differently. See the formatter section for a detailed list of changes.
+
+This release doesn’t remove or remap any existing stable rules.
+
+### Stabilization
+
+The following rules have been stabilized and are no longer in preview:
+
+- [`stdlib-module-shadowing`](https://docs.astral.sh/ruff/rules/stdlib-module-shadowing/) (`A005`).
+ This rule has also been renamed: previously, it was called `builtin-module-shadowing`.
+- [`builtin-lambda-argument-shadowing`](https://docs.astral.sh/ruff/rules/builtin-lambda-argument-shadowing/) (`A006`)
+- [`slice-to-remove-prefix-or-suffix`](https://docs.astral.sh/ruff/rules/slice-to-remove-prefix-or-suffix/) (`FURB188`)
+- [`boolean-chained-comparison`](https://docs.astral.sh/ruff/rules/boolean-chained-comparison/) (`PLR1716`)
+- [`decimal-from-float-literal`](https://docs.astral.sh/ruff/rules/decimal-from-float-literal/) (`RUF032`)
+- [`post-init-default`](https://docs.astral.sh/ruff/rules/post-init-default/) (`RUF033`)
+- [`useless-if-else`](https://docs.astral.sh/ruff/rules/useless-if-else/) (`RUF034`)
+
+The following behaviors have been stabilized:
+
+- [`pytest-parametrize-names-wrong-type`](https://docs.astral.sh/ruff/rules/pytest-parametrize-names-wrong-type/) (`PT006`): Detect [`pytest.parametrize`](https://docs.pytest.org/en/7.1.x/how-to/parametrize.html#parametrize) calls outside decorators and calls with keyword arguments.
+- [`module-import-not-at-top-of-file`](https://docs.astral.sh/ruff/rules/module-import-not-at-top-of-file/) (`E402`): Ignore [`pytest.importorskip`](https://docs.pytest.org/en/7.1.x/reference/reference.html#pytest-importorskip) calls between import statements.
+- [`mutable-dataclass-default`](https://docs.astral.sh/ruff/rules/mutable-dataclass-default/) (`RUF008`) and [`function-call-in-dataclass-default-argument`](https://docs.astral.sh/ruff/rules/function-call-in-dataclass-default-argument/) (`RUF009`): Add support for [`attrs`](https://www.attrs.org/en/stable/).
+- [`bad-version-info-comparison`](https://docs.astral.sh/ruff/rules/bad-version-info-comparison/) (`PYI006`): Extend the rule to check non-stub files.
+
+The following fixes or improvements to fixes have been stabilized:
+
+- [`redundant-numeric-union`](https://docs.astral.sh/ruff/rules/redundant-numeric-union/) (`PYI041`)
+- [`duplicate-union-members`](https://docs.astral.sh/ruff/rules/duplicate-union-member/) (`PYI016`)
+
+### Formatter
+
+This release introduces the new 2025 stable style ([#13371](https://github.com/astral-sh/ruff/issues/13371)), stabilizing the following changes:
+
+- Format expressions in f-string elements ([#7594](https://github.com/astral-sh/ruff/issues/7594))
+- Alternate quotes for strings inside f-strings ([#13860](https://github.com/astral-sh/ruff/pull/13860))
+- Preserve the casing of hex codes in f-string debug expressions ([#14766](https://github.com/astral-sh/ruff/issues/14766))
+- Choose the quote style for each string literal in an implicitly concatenated f-string rather than for the entire string ([#13539](https://github.com/astral-sh/ruff/pull/13539))
+- Automatically join an implicitly concatenated string into a single string literal if it fits on a single line ([#9457](https://github.com/astral-sh/ruff/issues/9457))
+- Remove the [`ISC001`](https://docs.astral.sh/ruff/rules/single-line-implicit-string-concatenation/) incompatibility warning ([#15123](https://github.com/astral-sh/ruff/pull/15123))
+- Prefer parenthesizing the `assert` message over breaking the assertion expression ([#9457](https://github.com/astral-sh/ruff/issues/9457))
+- Automatically parenthesize over-long `if` guards in `match` `case` clauses ([#13513](https://github.com/astral-sh/ruff/pull/13513))
+- More consistent formatting for `match` `case` patterns ([#6933](https://github.com/astral-sh/ruff/issues/6933))
+- Avoid unnecessary parentheses around return type annotations ([#13381](https://github.com/astral-sh/ruff/pull/13381))
+- Keep the opening parentheses on the same line as the `if` keyword for comprehensions where the condition has a leading comment ([#12282](https://github.com/astral-sh/ruff/pull/12282))
+- More consistent formatting for `with` statements with a single context manager for Python 3.8 or older ([#10276](https://github.com/astral-sh/ruff/pull/10276))
+- Correctly calculate the line-width for code blocks in docstrings when using `max-doc-code-line-length = "dynamic"` ([#13523](https://github.com/astral-sh/ruff/pull/13523))
+
+### Preview features
+
+- \[`flake8-bugbear`\] Implement `class-as-data-structure` (`B903`) ([#9601](https://github.com/astral-sh/ruff/pull/9601))
+- \[`flake8-type-checking`\] Apply `quoted-type-alias` more eagerly in `TYPE_CHECKING` blocks and ignore it in stubs (`TC008`) ([#15180](https://github.com/astral-sh/ruff/pull/15180))
+- \[`pylint`\] Ignore `eq-without-hash` in stub files (`PLW1641`) ([#15310](https://github.com/astral-sh/ruff/pull/15310))
+- \[`pyupgrade`\] Split `UP007` into two individual rules: `UP007` for `Union` and `UP045` for `Optional` (`UP007`, `UP045`) ([#15313](https://github.com/astral-sh/ruff/pull/15313))
+- \[`ruff`\] New rule that detects classes that are both an enum and a `dataclass` (`RUF049`) ([#15299](https://github.com/astral-sh/ruff/pull/15299))
+- \[`ruff`\] Recode `RUF025` to `RUF037` (`RUF037`) ([#15258](https://github.com/astral-sh/ruff/pull/15258))
+
+### Rule changes
+
+- \[`flake8-builtins`\] Ignore [`stdlib-module-shadowing`](https://docs.astral.sh/ruff/rules/stdlib-module-shadowing/) in stub files(`A005`) ([#15350](https://github.com/astral-sh/ruff/pull/15350))
+- \[`flake8-return`\] Add support for functions returning `typing.Never` (`RET503`) ([#15298](https://github.com/astral-sh/ruff/pull/15298))
+
+### Server
+
+- Improve the observability by removing the need for the ["trace" value](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#traceValue) to turn on or off logging. The server logging is solely controlled using the [`logLevel` server setting](https://docs.astral.sh/ruff/editors/settings/#loglevel)
+ which defaults to `info`. This addresses the issue where users were notified about an error and told to consult the log, but it didn’t contain any messages. ([#15232](https://github.com/astral-sh/ruff/pull/15232))
+- Ignore diagnostics from other sources for code action requests ([#15373](https://github.com/astral-sh/ruff/pull/15373))
+
+### CLI
+
+- Improve the error message for `--config key=value` when the `key` is for a table and it’s a simple `value`
+
+### Bug fixes
+
+- \[`eradicate`\] Ignore metadata blocks directly followed by normal blocks (`ERA001`) ([#15330](https://github.com/astral-sh/ruff/pull/15330))
+- \[`flake8-django`\] Recognize other magic methods (`DJ012`) ([#15365](https://github.com/astral-sh/ruff/pull/15365))
+- \[`pycodestyle`\] Avoid false positives related to type aliases (`E252`) ([#15356](https://github.com/astral-sh/ruff/pull/15356))
+- \[`pydocstyle`\] Avoid treating newline-separated sections as sub-sections (`D405`) ([#15311](https://github.com/astral-sh/ruff/pull/15311))
+- \[`pyflakes`\] Remove call when removing final argument from `format` (`F523`) ([#15309](https://github.com/astral-sh/ruff/pull/15309))
+- \[`refurb`\] Mark fix as unsafe when the right-hand side is a string (`FURB171`) ([#15273](https://github.com/astral-sh/ruff/pull/15273))
+- \[`ruff`\] Treat `)` as a regex metacharacter (`RUF043`, `RUF055`) ([#15318](https://github.com/astral-sh/ruff/pull/15318))
+- \[`ruff`\] Parenthesize the `int`-call argument when removing the `int` call would change semantics (`RUF046`) ([#15277](https://github.com/astral-sh/ruff/pull/15277))
+
## 0.8.6
### Preview features
diff --git a/Cargo.lock b/Cargo.lock
index 5aa27c5a3c419..710480ff89117 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2497,7 +2497,7 @@ dependencies = [
[[package]]
name = "ruff"
-version = "0.8.6"
+version = "0.9.0"
dependencies = [
"anyhow",
"argfile",
@@ -2716,7 +2716,7 @@ dependencies = [
[[package]]
name = "ruff_linter"
-version = "0.8.6"
+version = "0.9.0"
dependencies = [
"aho-corasick",
"annotate-snippets 0.9.2",
@@ -3033,7 +3033,7 @@ dependencies = [
[[package]]
name = "ruff_wasm"
-version = "0.8.6"
+version = "0.9.0"
dependencies = [
"console_error_panic_hook",
"console_log",
diff --git a/README.md b/README.md
index 2e157c7ece01e..b75f6e9dae34d 100644
--- a/README.md
+++ b/README.md
@@ -149,8 +149,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
# For a specific version.
-curl -LsSf https://astral.sh/ruff/0.8.6/install.sh | sh
-powershell -c "irm https://astral.sh/ruff/0.8.6/install.ps1 | iex"
+curl -LsSf https://astral.sh/ruff/0.9.0/install.sh | sh
+powershell -c "irm https://astral.sh/ruff/0.9.0/install.ps1 | iex"
```
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -183,7 +183,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
- rev: v0.8.6
+ rev: v0.9.0
hooks:
# Run the linter.
- id: ruff
diff --git a/crates/ruff/Cargo.toml b/crates/ruff/Cargo.toml
index 25677513084e3..5f437b34a7f4f 100644
--- a/crates/ruff/Cargo.toml
+++ b/crates/ruff/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ruff"
-version = "0.8.6"
+version = "0.9.0"
publish = true
authors = { workspace = true }
edition = { workspace = true }
diff --git a/crates/ruff_linter/Cargo.toml b/crates/ruff_linter/Cargo.toml
index 98d9a2c78999f..9a7236ed32257 100644
--- a/crates/ruff_linter/Cargo.toml
+++ b/crates/ruff_linter/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ruff_linter"
-version = "0.8.6"
+version = "0.9.0"
publish = false
authors = { workspace = true }
edition = { workspace = true }
diff --git a/crates/ruff_wasm/Cargo.toml b/crates/ruff_wasm/Cargo.toml
index c60a9168c63dd..8f2d4547b022a 100644
--- a/crates/ruff_wasm/Cargo.toml
+++ b/crates/ruff_wasm/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ruff_wasm"
-version = "0.8.6"
+version = "0.9.0"
publish = false
authors = { workspace = true }
edition = { workspace = true }
diff --git a/docs/formatter.md b/docs/formatter.md
index 01eb4fb396ff8..c82124105b4d8 100644
--- a/docs/formatter.md
+++ b/docs/formatter.md
@@ -373,7 +373,7 @@ Meanwhile, `ruff format --check` exits with the following status codes:
- `2` if Ruff terminates abnormally due to invalid configuration, invalid CLI options, or an
internal error.
-## Style Guide
+## Style Guide
The formatter is designed to be a drop-in replacement for [Black](https://github.com/psf/black).
This section documents the areas where the Ruff formatter goes beyond Black in terms of code style.
diff --git a/docs/integrations.md b/docs/integrations.md
index d3267f66d1498..44e132f9d6361 100644
--- a/docs/integrations.md
+++ b/docs/integrations.md
@@ -80,7 +80,7 @@ You can add the following configuration to `.gitlab-ci.yml` to run a `ruff forma
stage: build
interruptible: true
image:
- name: ghcr.io/astral-sh/ruff:0.8.6-alpine
+ name: ghcr.io/astral-sh/ruff:0.9.0-alpine
before_script:
- cd $CI_PROJECT_DIR
- ruff --version
@@ -106,7 +106,7 @@ Ruff can be used as a [pre-commit](https://pre-commit.com) hook via [`ruff-pre-c
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
- rev: v0.8.6
+ rev: v0.9.0
hooks:
# Run the linter.
- id: ruff
@@ -119,7 +119,7 @@ To enable lint fixes, add the `--fix` argument to the lint hook:
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
- rev: v0.8.6
+ rev: v0.9.0
hooks:
# Run the linter.
- id: ruff
@@ -133,7 +133,7 @@ To run the hooks over Jupyter Notebooks too, add `jupyter` to the list of allowe
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
- rev: v0.8.6
+ rev: v0.9.0
hooks:
# Run the linter.
- id: ruff
diff --git a/docs/tutorial.md b/docs/tutorial.md
index d004eb102a697..bac3c39999f66 100644
--- a/docs/tutorial.md
+++ b/docs/tutorial.md
@@ -364,7 +364,7 @@ This tutorial has focused on Ruff's command-line interface, but Ruff can also be
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
- rev: v0.8.6
+ rev: v0.9.0
hooks:
# Run the linter.
- id: ruff
diff --git a/pyproject.toml b/pyproject.toml
index 6fa39c66a0187..1293c59eebf7d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "maturin"
[project]
name = "ruff"
-version = "0.8.6"
+version = "0.9.0"
description = "An extremely fast Python linter and code formatter, written in Rust."
authors = [{ name = "Astral Software Inc.", email = "hey@astral.sh" }]
readme = "README.md"
diff --git a/scripts/benchmarks/pyproject.toml b/scripts/benchmarks/pyproject.toml
index 3f54524cb7390..8f1531d3af1ef 100644
--- a/scripts/benchmarks/pyproject.toml
+++ b/scripts/benchmarks/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "scripts"
-version = "0.8.6"
+version = "0.9.0"
description = ""
authors = ["Charles Marsh "]