Skip to content

Commit c0f4ee5

Browse files
committed
Merge branch 'main' into charlie/metaclass
* main: Update conformance suite commit hash (#23746) conformance.py: Collapse the summary paragraph when nothing changed (#23745) [ty] Make inferred specializations line up with source types more better (#23715) Bump 0.15.5 (#23743) [ty] Render all changed diagnostics in conformance.py (#23613) [ty] Split deferred checks out of `types/infer/builder.rs` (#23740) Discover markdown files by default in preview mode (#23434) [ty] Use `HasOptionalDefinition` for `except` handlers (#23739) [ty] Fix precedence of `all` selector in TOML configurations (#23723) [ty] Make `all` selector case sensitive (#23713) [ty] Add a diagnostic if a `TypeVar` is used to specialize a `ParamSpec`, or vice versa (#23738) [ty] Override home directory in ty tests (#23724) [ty] More type-variable default validation (#23639) [ty] Validate bare ParamSpec usage in type annotations, and support stringified ParamSpecs as the first argument to `Callable` (#23625) [ty] Add `all` selector to ty.json's `schema` (#23721) [ty] Add quotes to related issues links (#23720) [ty] Fix panic on incomplete except handlers (#23708) Update conformance suite commit hash (#23719)
2 parents 9d7bb89 + f9324a5 commit c0f4ee5

File tree

72 files changed

+5521
-2756
lines changed

Some content is hidden

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

72 files changed

+5521
-2756
lines changed

.github/workflows/typing_conformance.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ env:
3434
CARGO_TERM_COLOR: always
3535
RUSTUP_MAX_RETRIES: 10
3636
RUST_BACKTRACE: 1
37-
CONFORMANCE_SUITE_COMMIT: 5b5f2f89bd19462f4707400f0437ab5a48d88bb3
37+
CONFORMANCE_SUITE_COMMIT: 7f8f6bbcaac9be6cde08c886f32aab64a3d597d2
3838
PYTHON_VERSION: 3.12
3939

4040
jobs:

CHANGELOG.md

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

3+
## 0.15.5
4+
5+
Released on 2026-03-05.
6+
7+
### Preview features
8+
9+
- Discover Markdown files by default in preview mode ([#23434](https://github.com/astral-sh/ruff/pull/23434))
10+
- \[`perflint`\] Extend `PERF102` to comprehensions and generators ([#23473](https://github.com/astral-sh/ruff/pull/23473))
11+
- \[`refurb`\] Fix `FURB101` and `FURB103` false positives when I/O variable is used later ([#23542](https://github.com/astral-sh/ruff/pull/23542))
12+
- \[`ruff`\] Add fix for `none-not-at-end-of-union` (`RUF036`) ([#22829](https://github.com/astral-sh/ruff/pull/22829))
13+
- \[`ruff`\] Fix false positive for `re.split` with empty string pattern (`RUF055`) ([#23634](https://github.com/astral-sh/ruff/pull/23634))
14+
15+
### Bug fixes
16+
17+
- \[`fastapi`\] Handle callable class dependencies with `__call__` method (`FAST003`) ([#23553](https://github.com/astral-sh/ruff/pull/23553))
18+
- \[`pydocstyle`\] Fix numpy section ordering (`D420`) ([#23685](https://github.com/astral-sh/ruff/pull/23685))
19+
- \[`pyflakes`\] Fix false positive for names shadowing re-exports (`F811`) ([#23356](https://github.com/astral-sh/ruff/pull/23356))
20+
- \[`pyupgrade`\] Avoid inserting redundant `None` elements in `UP045` ([#23459](https://github.com/astral-sh/ruff/pull/23459))
21+
22+
### Documentation
23+
24+
- Document extension mapping for Markdown code formatting ([#23574](https://github.com/astral-sh/ruff/pull/23574))
25+
- Update default Python version examples ([#23605](https://github.com/astral-sh/ruff/pull/23605))
26+
27+
### Other changes
28+
29+
- Publish releases to Astral mirror ([#23616](https://github.com/astral-sh/ruff/pull/23616))
30+
31+
### Contributors
32+
33+
- [@amyreese](https://github.com/amyreese)
34+
- [@stakeswky](https://github.com/stakeswky)
35+
- [@chirizxc](https://github.com/chirizxc)
36+
- [@anishgirianish](https://github.com/anishgirianish)
37+
- [@bxff](https://github.com/bxff)
38+
- [@zsol](https://github.com/zsol)
39+
- [@charliermarsh](https://github.com/charliermarsh)
40+
- [@ntBre](https://github.com/ntBre)
41+
- [@kar-ganap](https://github.com/kar-ganap)
42+
343
## 0.15.4
444

545
Released on 2026-02-26.

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
@@ -152,8 +152,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
152152
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
153153

154154
# For a specific version.
155-
curl -LsSf https://astral.sh/ruff/0.15.4/install.sh | sh
156-
powershell -c "irm https://astral.sh/ruff/0.15.4/install.ps1 | iex"
155+
curl -LsSf https://astral.sh/ruff/0.15.5/install.sh | sh
156+
powershell -c "irm https://astral.sh/ruff/0.15.5/install.ps1 | iex"
157157
```
158158

159159
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -186,7 +186,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
186186
```yaml
187187
- repo: https://github.com/astral-sh/ruff-pre-commit
188188
# Ruff version.
189-
rev: v0.15.4
189+
rev: v0.15.5
190190
hooks:
191191
# Run the linter.
192192
- 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.15.4"
3+
version = "0.15.5"
44
publish = true
55
authors = { workspace = true }
66
edition = { workspace = true }

crates/ruff/src/commands/add_noqa.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use ruff_linter::source_kind::SourceKind;
1111
use ruff_linter::warn_user_once;
1212
use ruff_python_ast::{PySourceType, SourceType};
1313
use ruff_workspace::resolver::{
14-
PyprojectConfig, ResolvedFile, match_exclusion, python_files_in_path,
14+
PyprojectConfig, ResolvedFile, match_exclusion, project_files_in_path,
1515
};
1616

1717
use crate::args::ConfigArguments;
@@ -25,10 +25,22 @@ pub(crate) fn add_noqa(
2525
) -> Result<usize> {
2626
// Collect all the files to check.
2727
let start = Instant::now();
28-
let (paths, resolver) = python_files_in_path(files, pyproject_config, config_arguments)?;
28+
let (mut paths, resolver) = project_files_in_path(files, pyproject_config, config_arguments)?;
2929
let duration = start.elapsed();
3030
debug!("Identified files to lint in: {duration:?}");
3131

32+
// Filter out paths for file types not supported for linting
33+
paths.retain(|path| {
34+
if let Ok(ResolvedFile::Root(path) | ResolvedFile::Nested(path)) = path {
35+
matches!(
36+
SourceType::from(path),
37+
SourceType::Python(PySourceType::Python | PySourceType::Stub)
38+
)
39+
} else {
40+
true
41+
}
42+
});
43+
3244
if paths.is_empty() {
3345
warn_user_once!("No Python files found under the given path(s)");
3446
return Ok(0);
@@ -48,11 +60,7 @@ pub(crate) fn add_noqa(
4860
.par_iter()
4961
.flatten()
5062
.filter_map(|resolved_file| {
51-
let SourceType::Python(source_type @ (PySourceType::Python | PySourceType::Stub)) =
52-
SourceType::from(resolved_file.path())
53-
else {
54-
return None;
55-
};
63+
let source_type = SourceType::from(resolved_file.path());
5664
let path = resolved_file.path();
5765
let package = resolved_file
5866
.path()
@@ -69,7 +77,7 @@ pub(crate) fn add_noqa(
6977
{
7078
return None;
7179
}
72-
let source_kind = match SourceKind::from_path(path, SourceType::Python(source_type)) {
80+
let source_kind = match SourceKind::from_path(path, source_type) {
7381
Ok(Some(source_kind)) => source_kind,
7482
Ok(None) => return None,
7583
Err(e) => {
@@ -81,7 +89,7 @@ pub(crate) fn add_noqa(
8189
path,
8290
package,
8391
&source_kind,
84-
source_type,
92+
source_type.expect_python(),
8593
&settings.linter,
8694
reason,
8795
) {

crates/ruff/src/commands/analyze_graph.rs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use ruff_linter::package::PackageRoot;
1111
use ruff_linter::source_kind::SourceKind;
1212
use ruff_linter::{warn_user, warn_user_once};
1313
use ruff_python_ast::SourceType;
14-
use ruff_workspace::resolver::{ResolvedFile, match_exclusion, python_files_in_path};
14+
use ruff_workspace::resolver::{ResolvedFile, match_exclusion, project_files_in_path};
1515
use rustc_hash::{FxBuildHasher, FxHashMap};
1616
use std::io::Write;
1717
use std::path::{Path, PathBuf};
@@ -35,7 +35,16 @@ pub(crate) fn analyze_graph(
3535

3636
// Find all Python files.
3737
let files = resolve_default_files(args.files, false);
38-
let (paths, resolver) = python_files_in_path(&files, &pyproject_config, config_arguments)?;
38+
let (mut paths, resolver) = project_files_in_path(&files, &pyproject_config, config_arguments)?;
39+
40+
// Filter to only Python files
41+
paths.retain(|path| {
42+
if let Ok(ResolvedFile::Root(path) | ResolvedFile::Nested(path)) = path {
43+
matches!(SourceType::from(path), SourceType::Python(_))
44+
} else {
45+
true
46+
}
47+
});
3948

4049
if paths.is_empty() {
4150
warn_user_once!("No Python files found under the given path(s)");
@@ -124,6 +133,7 @@ pub(crate) fn analyze_graph(
124133
let string_imports = settings.analyze.string_imports;
125134
let include_dependencies = settings.analyze.include_dependencies.get(path).cloned();
126135
let type_checking_imports = settings.analyze.type_checking_imports;
136+
let source_type = settings.analyze.extension.get_source_type(path);
127137

128138
// Skip excluded files.
129139
if (settings.file_resolver.force_exclude || !resolved_file.is_root())
@@ -136,19 +146,6 @@ pub(crate) fn analyze_graph(
136146
continue;
137147
}
138148

139-
// Ignore non-Python files.
140-
let source_type = match settings.analyze.extension.get_source_type(path) {
141-
SourceType::Python(source_type) => source_type,
142-
SourceType::Toml(_) => {
143-
debug!("Ignoring TOML file: {}", path.display());
144-
continue;
145-
}
146-
SourceType::Markdown => {
147-
debug!("Ignoring Markdown file: {}", path.display());
148-
continue;
149-
}
150-
};
151-
152149
// Convert to system paths.
153150
let Ok(package) = package.map(SystemPathBuf::from_path_buf).transpose() else {
154151
warn!("Failed to convert package to system path");
@@ -165,10 +162,7 @@ pub(crate) fn analyze_graph(
165162
let result = inner_result.clone();
166163
scope.spawn(move |_| {
167164
// Extract source code (handles both .py and .ipynb files)
168-
let source_kind = match SourceKind::from_path(
169-
path.as_std_path(),
170-
SourceType::Python(source_type),
171-
) {
165+
let source_kind = match SourceKind::from_path(path.as_std_path(), source_type) {
172166
Ok(Some(source_kind)) => source_kind,
173167
Ok(None) => {
174168
debug!("Skipping non-Python notebook: {path}");
@@ -186,7 +180,7 @@ pub(crate) fn analyze_graph(
186180
let mut imports = ModuleImports::detect(
187181
&db,
188182
source_code,
189-
source_type,
183+
source_type.expect_python(),
190184
&path,
191185
package.as_deref(),
192186
string_imports,

crates/ruff/src/commands/check.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use log::{debug, warn};
1010
#[cfg(not(target_family = "wasm"))]
1111
use rayon::prelude::*;
1212
use ruff_linter::message::create_panic_diagnostic;
13+
use ruff_python_ast::{SourceType, TomlSourceType};
1314
use rustc_hash::FxHashMap;
1415

1516
use ruff_db::diagnostic::Diagnostic;
@@ -22,7 +23,7 @@ use ruff_linter::{IOError, Violation, fs, warn_user_once};
2223
use ruff_source_file::SourceFileBuilder;
2324
use ruff_text_size::TextRange;
2425
use ruff_workspace::resolver::{
25-
PyprojectConfig, ResolvedFile, match_exclusion, python_files_in_path,
26+
PyprojectConfig, ResolvedFile, match_exclusion, project_files_in_path,
2627
};
2728

2829
use crate::args::ConfigArguments;
@@ -41,9 +42,21 @@ pub(crate) fn check(
4142
) -> Result<Diagnostics> {
4243
// Collect all the Python files to check.
4344
let start = Instant::now();
44-
let (paths, resolver) = python_files_in_path(files, pyproject_config, config_arguments)?;
45+
let (mut paths, resolver) = project_files_in_path(files, pyproject_config, config_arguments)?;
4546
debug!("Identified files to lint in: {:?}", start.elapsed());
4647

48+
// Filter out paths for file types not supported for linting
49+
paths.retain(|path| {
50+
if let Ok(ResolvedFile::Root(path) | ResolvedFile::Nested(path)) = path {
51+
matches!(
52+
SourceType::from(path),
53+
SourceType::Python(_) | SourceType::Toml(TomlSourceType::Pyproject)
54+
)
55+
} else {
56+
true
57+
}
58+
});
59+
4760
if paths.is_empty() {
4861
warn_user_once!("No Python files found under the given path(s)");
4962
return Ok(Diagnostics::default());

crates/ruff/src/commands/check_stdin.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ruff_db::diagnostic::Diagnostic;
55
use ruff_linter::package::PackageRoot;
66
use ruff_linter::packaging;
77
use ruff_linter::settings::flags;
8-
use ruff_workspace::resolver::{PyprojectConfig, Resolver, match_exclusion, python_file_at_path};
8+
use ruff_workspace::resolver::{PyprojectConfig, Resolver, match_exclusion, project_file_at_path};
99

1010
use crate::args::ConfigArguments;
1111
use crate::diagnostics::{Diagnostics, lint_stdin};
@@ -23,7 +23,7 @@ pub(crate) fn check_stdin(
2323

2424
if resolver.force_exclude() {
2525
if let Some(filename) = filename {
26-
if !python_file_at_path(filename, &mut resolver, overrides)? {
26+
if !project_file_at_path(filename, &mut resolver, overrides)? {
2727
if fix_mode.is_apply() {
2828
parrot_stdin()?;
2929
}

crates/ruff/src/commands/format.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use ruff_source_file::{LineIndex, LineRanges, OneIndexed, SourceFileBuilder};
3838
use ruff_text_size::{TextLen, TextRange, TextSize};
3939
use ruff_workspace::FormatterSettings;
4040
use ruff_workspace::resolver::{
41-
PyprojectConfig, ResolvedFile, Resolver, match_exclusion, python_files_in_path,
41+
PyprojectConfig, ResolvedFile, Resolver, match_exclusion, project_files_in_path,
4242
};
4343

4444
use crate::args::{ConfigArguments, FormatArguments, FormatRange};
@@ -75,7 +75,7 @@ pub(crate) fn format(
7575
) -> Result<ExitStatus> {
7676
let mode = FormatMode::from_cli(&cli);
7777
let files = resolve_default_files(cli.files, false);
78-
let (paths, resolver) = python_files_in_path(&files, pyproject_config, config_arguments)?;
78+
let (paths, resolver) = project_files_in_path(&files, pyproject_config, config_arguments)?;
7979

8080
let output_format = pyproject_config.settings.output_format;
8181
let preview = pyproject_config.settings.formatter.preview;

0 commit comments

Comments
 (0)