Skip to content

Commit b6b3706

Browse files
committed
Merge branch 'main' into hover-variance
2 parents d398380 + 3c229ae commit b6b3706

File tree

84 files changed

+4360
-1196
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+4360
-1196
lines changed

.github/workflows/ci.yaml

Lines changed: 27 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ env:
2222
CARGO_TERM_COLOR: always
2323
RUSTUP_MAX_RETRIES: 10
2424
PACKAGE_NAME: ruff
25-
PYTHON_VERSION: "3.13"
25+
PYTHON_VERSION: "3.14"
2626
NEXTEST_PROFILE: ci
2727

2828
jobs:
@@ -277,7 +277,8 @@ jobs:
277277
run: cargo test -p ty_python_semantic --test mdtest || true
278278
- name: "Run tests"
279279
run: cargo insta test --all-features --unreferenced reject --test-runner nextest
280-
280+
# Dogfood ty on py-fuzzer
281+
- run: uv run --project=./python/py-fuzzer cargo run -p ty check --project=./python/py-fuzzer
281282
# Check for broken links in the documentation.
282283
- run: cargo doc --all --no-deps
283284
env:
@@ -333,38 +334,14 @@ jobs:
333334
- name: "Run tests"
334335
run: cargo insta test --release --all-features --unreferenced reject --test-runner nextest
335336

336-
cargo-test-windows:
337-
name: "cargo test (windows)"
338-
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-windows-2022-16' || 'windows-latest' }}
339-
needs: determine_changes
340-
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
341-
timeout-minutes: 20
342-
steps:
343-
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
344-
with:
345-
persist-credentials: false
346-
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
347-
- name: "Install Rust toolchain"
348-
run: rustup show
349-
- name: "Install cargo nextest"
350-
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
351-
with:
352-
tool: cargo-nextest
353-
- name: "Install uv"
354-
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
355-
with:
356-
enable-cache: "true"
357-
- name: "Run tests"
358-
env:
359-
# Workaround for <https://github.com/nextest-rs/nextest/issues/1493>.
360-
RUSTUP_WINDOWS_PATH_ADD_BIN: 1
361-
run: |
362-
cargo nextest run --all-features --profile ci
363-
cargo test --all-features --doc
364-
365-
cargo-test-macos:
366-
name: "cargo test (macos)"
367-
runs-on: macos-latest
337+
cargo-test-other:
338+
strategy:
339+
matrix:
340+
platform:
341+
- ${{ github.repository == 'astral-sh/ruff' && 'depot-windows-2022-16' || 'windows-latest' }}
342+
- macos-latest
343+
name: "cargo test (${{ matrix.platform }})"
344+
runs-on: ${{ matrix.platform }}
368345
needs: determine_changes
369346
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
370347
timeout-minutes: 20
@@ -375,8 +352,6 @@ jobs:
375352
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
376353
- name: "Install Rust toolchain"
377354
run: rustup show
378-
- name: "Install mold"
379-
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
380355
- name: "Install cargo nextest"
381356
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
382357
with:
@@ -498,9 +473,10 @@ jobs:
498473
chmod +x "${DOWNLOAD_PATH}/ruff"
499474
500475
(
501-
uvx \
476+
uv run \
502477
--python="${PYTHON_VERSION}" \
503-
--from=./python/py-fuzzer \
478+
--project=./python/py-fuzzer \
479+
--locked \
504480
fuzz \
505481
--test-executable="${DOWNLOAD_PATH}/ruff" \
506482
--bin=ruff \
@@ -518,6 +494,7 @@ jobs:
518494
with:
519495
persist-credentials: false
520496
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
497+
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
521498
- name: "Install Rust toolchain"
522499
run: rustup component add rustfmt
523500
# Run all code generation scripts, and verify that the current output is
@@ -532,6 +509,11 @@ jobs:
532509
./scripts/add_plugin.py test --url https://pypi.org/project/-test/0.1.0/ --prefix TST
533510
./scripts/add_rule.py --name FirstRule --prefix TST --code 001 --linter test
534511
- run: cargo check
512+
# Lint/format/type-check py-fuzzer
513+
# (dogfooding with ty is done in a separate job)
514+
- run: uv run --directory=./python/py-fuzzer mypy
515+
- run: uv run --directory=./python/py-fuzzer ruff format --check
516+
- run: uv run --directory=./python/py-fuzzer ruff check
535517

536518
ecosystem:
537519
name: "ecosystem"
@@ -549,7 +531,8 @@ jobs:
549531
persist-credentials: false
550532
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
551533
with:
552-
python-version: ${{ env.PYTHON_VERSION }}
534+
# TODO: figure out why `ruff-ecosystem` crashes on Python 3.14
535+
python-version: "3.13"
553536

554537
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
555538
name: Download comparison Ruff binary
@@ -666,7 +649,7 @@ jobs:
666649
- determine_changes
667650
# Only runs on pull requests, since that is the only we way we can find the base version for comparison.
668651
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && github.event_name == 'pull_request' && (needs.determine_changes.outputs.ty == 'true' || needs.determine_changes.outputs.py-fuzzer == 'true') }}
669-
timeout-minutes: 20
652+
timeout-minutes: ${{ github.repository == 'astral-sh/ruff' && 5 || 20 }}
670653
steps:
671654
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
672655
with:
@@ -694,15 +677,16 @@ jobs:
694677
chmod +x "${PWD}/ty" "${NEW_TY}/ty"
695678
696679
(
697-
uvx \
680+
uv run \
698681
--python="${PYTHON_VERSION}" \
699-
--from=./python/py-fuzzer \
682+
--project=./python/py-fuzzer \
683+
--locked \
700684
fuzz \
701685
--test-executable="${NEW_TY}/ty" \
702686
--baseline-executable="${PWD}/ty" \
703687
--only-new-bugs \
704688
--bin=ty \
705-
0-500
689+
0-1000
706690
)
707691
708692
cargo-shear:

.github/workflows/daily_fuzz.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ jobs:
4848
run: |
4949
# shellcheck disable=SC2046
5050
(
51-
uvx \
52-
--python=3.12 \
53-
--from=./python/py-fuzzer \
51+
uv run \
52+
--python=3.14 \
53+
--project=./python/py-fuzzer \
54+
--locked \
5455
fuzz \
5556
--test-executable=target/debug/ruff \
5657
--bin=ruff \

CHANGELOG.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,63 @@
11
# Changelog
22

3+
## 0.14.1
4+
5+
Released on 2025-10-16.
6+
7+
### Preview features
8+
9+
- [formatter] Remove parentheses around multiple exception types on Python 3.14+ ([#20768](https://github.com/astral-sh/ruff/pull/20768))
10+
- \[`flake8-bugbear`\] Omit annotation in preview fix for `B006` ([#20877](https://github.com/astral-sh/ruff/pull/20877))
11+
- \[`flake8-logging-format`\] Avoid dropping implicitly concatenated pieces in the `G004` fix ([#20793](https://github.com/astral-sh/ruff/pull/20793))
12+
- \[`pydoclint`\] Implement `docstring-extraneous-parameter` (`DOC102`) ([#20376](https://github.com/astral-sh/ruff/pull/20376))
13+
- \[`pyupgrade`\] Extend `UP019` to detect `typing_extensions.Text` (`UP019`) ([#20825](https://github.com/astral-sh/ruff/pull/20825))
14+
- \[`pyupgrade`\] Fix false negative for `TypeVar` with default argument in `non-pep695-generic-class` (`UP046`) ([#20660](https://github.com/astral-sh/ruff/pull/20660))
15+
16+
### Bug fixes
17+
18+
- Fix false negatives in `Truthiness::from_expr` for lambdas, generators, and f-strings ([#20704](https://github.com/astral-sh/ruff/pull/20704))
19+
- Fix syntax error false positives for escapes and quotes in f-strings ([#20867](https://github.com/astral-sh/ruff/pull/20867))
20+
- Fix syntax error false positives on parenthesized context managers ([#20846](https://github.com/astral-sh/ruff/pull/20846))
21+
- \[`fastapi`\] Fix false positives for path parameters that FastAPI doesn't recognize (`FAST003`) ([#20687](https://github.com/astral-sh/ruff/pull/20687))
22+
- \[`flake8-pyi`\] Fix operator precedence by adding parentheses when needed (`PYI061`) ([#20508](https://github.com/astral-sh/ruff/pull/20508))
23+
- \[`ruff`\] Suppress diagnostic for f-string interpolations with debug text (`RUF010`) ([#20525](https://github.com/astral-sh/ruff/pull/20525))
24+
25+
### Rule changes
26+
27+
- \[`airflow`\] Add warning to `airflow.datasets.DatasetEvent` usage (`AIR301`) ([#20551](https://github.com/astral-sh/ruff/pull/20551))
28+
- \[`flake8-bugbear`\] Mark `B905` and `B912` fixes as unsafe ([#20695](https://github.com/astral-sh/ruff/pull/20695))
29+
- Use `DiagnosticTag` for more rules - changes display in editors ([#20758](https://github.com/astral-sh/ruff/pull/20758),[#20734](https://github.com/astral-sh/ruff/pull/20734))
30+
31+
### Documentation
32+
33+
- Update Python compatibility from 3.13 to 3.14 in README.md ([#20852](https://github.com/astral-sh/ruff/pull/20852))
34+
- Update `lint.flake8-type-checking.quoted-annotations` docs ([#20765](https://github.com/astral-sh/ruff/pull/20765))
35+
- Update setup instructions for Zed 0.208.0+ ([#20902](https://github.com/astral-sh/ruff/pull/20902))
36+
- \[`flake8-datetimez`\] Clarify docs for several rules ([#20778](https://github.com/astral-sh/ruff/pull/20778))
37+
- Fix typo in `RUF015` description ([#20873](https://github.com/astral-sh/ruff/pull/20873))
38+
39+
### Other changes
40+
41+
- Reduce binary size ([#20863](https://github.com/astral-sh/ruff/pull/20863))
42+
- Improved error recovery for unclosed strings (including f- and t-strings) ([#20848](https://github.com/astral-sh/ruff/pull/20848))
43+
44+
### Contributors
45+
46+
- [@ntBre](https://github.com/ntBre)
47+
- [@Paillat-dev](https://github.com/Paillat-dev)
48+
- [@terror](https://github.com/terror)
49+
- [@pieterh-oai](https://github.com/pieterh-oai)
50+
- [@MichaReiser](https://github.com/MichaReiser)
51+
- [@TaKO8Ki](https://github.com/TaKO8Ki)
52+
- [@ageorgou](https://github.com/ageorgou)
53+
- [@danparizher](https://github.com/danparizher)
54+
- [@mgaitan](https://github.com/mgaitan)
55+
- [@augustelalande](https://github.com/augustelalande)
56+
- [@dylwil3](https://github.com/dylwil3)
57+
- [@Lee-W](https://github.com/Lee-W)
58+
- [@injust](https://github.com/injust)
59+
- [@CarrotManMatt](https://github.com/CarrotManMatt)
60+
361
## 0.14.0
462

563
Released on 2025-10-07.

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
148148
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
149149

150150
# For a specific version.
151-
curl -LsSf https://astral.sh/ruff/0.14.0/install.sh | sh
152-
powershell -c "irm https://astral.sh/ruff/0.14.0/install.ps1 | iex"
151+
curl -LsSf https://astral.sh/ruff/0.14.1/install.sh | sh
152+
powershell -c "irm https://astral.sh/ruff/0.14.1/install.ps1 | iex"
153153
```
154154

155155
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -182,7 +182,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
182182
```yaml
183183
- repo: https://github.com/astral-sh/ruff-pre-commit
184184
# Ruff version.
185-
rev: v0.14.0
185+
rev: v0.14.1
186186
hooks:
187187
# Run the linter.
188188
- id: ruff-check

crates/ruff/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ruff"
3-
version = "0.14.0"
3+
version = "0.14.1"
44
publish = true
55
authors = { workspace = true }
66
edition = { workspace = true }

crates/ruff_linter/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ruff_linter"
3-
version = "0.14.0"
3+
version = "0.14.1"
44
publish = false
55
authors = { workspace = true }
66
edition = { workspace = true }

crates/ruff_linter/src/checkers/ast/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ impl SemanticSyntaxContext for Checker<'_> {
723723
| SemanticSyntaxErrorKind::IrrefutableCasePattern(_)
724724
| SemanticSyntaxErrorKind::SingleStarredAssignment
725725
| SemanticSyntaxErrorKind::WriteToDebug(_)
726+
| SemanticSyntaxErrorKind::DifferentMatchPatternBindings
726727
| SemanticSyntaxErrorKind::InvalidExpression(..)
727728
| SemanticSyntaxErrorKind::DuplicateMatchKey(_)
728729
| SemanticSyntaxErrorKind::DuplicateMatchClassAttribute(_)

crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/fstring.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,3 +748,7 @@
748748

749749
# Regression tests for https://github.com/astral-sh/ruff/issues/15536
750750
print(f"{ {}, 1, }")
751+
752+
753+
# The inner quotes should not be changed to double quotes before Python 3.12
754+
f"{f'''{'nested'} inner'''} outer"

crates/ruff_python_formatter/src/context.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ pub(crate) enum InterpolatedStringState {
144144
///
145145
/// The containing `FStringContext` is the surrounding f-string context.
146146
InsideInterpolatedElement(InterpolatedStringContext),
147+
/// The formatter is inside more than one nested f-string, such as in `nested` in:
148+
///
149+
/// ```py
150+
/// f"{f'''{'nested'} inner'''} outer"
151+
/// ```
152+
NestedInterpolatedElement(InterpolatedStringContext),
147153
/// The formatter is outside an f-string.
148154
#[default]
149155
Outside,
@@ -152,12 +158,18 @@ pub(crate) enum InterpolatedStringState {
152158
impl InterpolatedStringState {
153159
pub(crate) fn can_contain_line_breaks(self) -> Option<bool> {
154160
match self {
155-
InterpolatedStringState::InsideInterpolatedElement(context) => {
161+
InterpolatedStringState::InsideInterpolatedElement(context)
162+
| InterpolatedStringState::NestedInterpolatedElement(context) => {
156163
Some(context.is_multiline())
157164
}
158165
InterpolatedStringState::Outside => None,
159166
}
160167
}
168+
169+
/// Returns `true` if the interpolated string state is [`NestedInterpolatedElement`].
170+
pub(crate) fn is_nested(self) -> bool {
171+
matches!(self, Self::NestedInterpolatedElement(..))
172+
}
161173
}
162174

163175
/// The position of a top-level statement in the module.

0 commit comments

Comments
 (0)