Compare commits

..

1 Commits

Author SHA1 Message Date
Aria Desires
3e2da64f23 Make the default settings try harder to avoid crashes
This introduces a safe_mode to our settings type that tells our initialization code
to more aggressively believe Settings Failure Is Not An Error. This allows the full
settings initialization logic to be used as much as possible for the default database
while trying to keep any discarded results as narrow as possible for graceful degradation.

Finally, Python has SFINAE.
2025-10-23 10:44:04 -04:00
1060 changed files with 6342 additions and 14056 deletions

View File

@@ -438,7 +438,7 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- name: "Install cargo-binstall"
uses: cargo-bins/cargo-binstall@afcf9780305558bcc9e4bc94b7589ab2bb8b6106 # v1.15.9
uses: cargo-bins/cargo-binstall@a66119fbb1c952daba62640c2609111fe0803621 # v1.15.7
- name: "Install cargo-fuzz"
# Download the latest version from quick install and not the github releases because github releases only has MUSL targets.
run: cargo binstall cargo-fuzz --force --disable-strategies crate-meta-data --no-confirm
@@ -531,7 +531,8 @@ jobs:
persist-credentials: false
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
with:
python-version: ${{ env.PYTHON_VERSION }}
# TODO: figure out why `ruff-ecosystem` crashes on Python 3.14
python-version: "3.13"
activate-environment: true
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
@@ -698,7 +699,7 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: cargo-bins/cargo-binstall@afcf9780305558bcc9e4bc94b7589ab2bb8b6106 # v1.15.9
- uses: cargo-bins/cargo-binstall@a66119fbb1c952daba62640c2609111fe0803621 # v1.15.7
- run: cargo binstall --no-confirm cargo-shear
- run: cargo shear

View File

@@ -18,8 +18,6 @@ env:
CARGO_TERM_COLOR: always
RUSTUP_MAX_RETRIES: 10
permissions: {}
jobs:
publish:
runs-on: ubuntu-latest
@@ -34,7 +32,8 @@ jobs:
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version: 22
package-manager-cache: false
cache: "npm"
cache-dependency-path: playground/package-lock.json
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
- name: "Install Node dependencies"
run: npm ci

View File

@@ -38,7 +38,6 @@ jobs:
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version: 22
package-manager-cache: false
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
- name: "Install Node dependencies"
run: npm ci

View File

@@ -1,6 +1,7 @@
# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist
# This file was autogenerated by dist: https://github.com/astral-sh/cargo-dist
#
# Copyright 2022-2024, axodotdev
# Copyright 2025 Astral Software Inc.
# SPDX-License-Identifier: MIT or Apache-2.0
#
# CI that:
@@ -68,9 +69,9 @@ jobs:
# we specify bash to get pipefail; it guards against the `curl` command
# failing. otherwise `sh` won't catch that `curl` returned non-0
shell: bash
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.0/cargo-dist-installer.sh | sh"
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.5-prerelease.1/cargo-dist-installer.sh | sh"
- name: Cache dist
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
with:
name: cargo-dist-cache
path: ~/.cargo/bin/dist
@@ -86,7 +87,7 @@ jobs:
cat plan-dist-manifest.json
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
with:
name: artifacts-plan-dist-manifest
path: plan-dist-manifest.json
@@ -128,14 +129,14 @@ jobs:
persist-credentials: false
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
- name: Fetch local artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0
with:
pattern: artifacts-*
path: target/distrib/
@@ -153,7 +154,7 @@ jobs:
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
- name: "Upload artifacts"
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
with:
name: artifacts-build-global
path: |
@@ -179,14 +180,14 @@ jobs:
persist-credentials: false
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Fetch artifacts from scratch-storage
- name: Fetch artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0
with:
pattern: artifacts-*
path: target/distrib/
@@ -200,7 +201,7 @@ jobs:
cat dist-manifest.json
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
with:
# Overwrite the previous copy
name: artifacts-dist-manifest
@@ -256,7 +257,7 @@ jobs:
submodules: recursive
# Create a GitHub Release while uploading all files to it
- name: "Download GitHub Artifacts"
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0
with:
pattern: artifacts-*
path: artifacts

View File

@@ -34,13 +34,10 @@ jobs:
- name: Install the latest version of uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
with:
enable-cache: true # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "ruff"
lookup-only: false # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
- name: Install Rust toolchain
run: rustup show

View File

@@ -30,13 +30,10 @@ jobs:
- name: Install the latest version of uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
with:
enable-cache: true # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "ruff"
lookup-only: false # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
- name: Install Rust toolchain
run: rustup show

15
.github/zizmor.yml vendored
View File

@@ -1,5 +1,5 @@
# Configuration for the zizmor static analysis tool, run via pre-commit in CI
# https://docs.zizmor.sh/configuration/
# https://woodruffw.github.io/zizmor/configuration/
#
# TODO: can we remove the ignores here so that our workflows are more secure?
rules:
@@ -9,18 +9,13 @@ rules:
cache-poisoning:
ignore:
- build-docker.yml
- publish-playground.yml
- ty-ecosystem-analyzer.yaml
- ty-ecosystem-report.yaml
excessive-permissions:
# it's hard to test what the impact of removing these ignores would be
# without actually running the release workflow...
ignore:
- build-docker.yml
- publish-playground.yml
- publish-docs.yml
secrets-inherit:
# `cargo dist` makes extensive use of `secrets: inherit`,
# and we can't easily fix that until an upstream release changes that.
disable: true
template-injection:
ignore:
# like with `secrets-inherit`, `cargo dist` introduces some
# template injections. We've manually audited these usages for safety.
- release.yml

View File

@@ -101,8 +101,8 @@ repos:
# zizmor detects security vulnerabilities in GitHub Actions workflows.
# Additional configuration for the tool is found in `.github/zizmor.yml`
- repo: https://github.com/zizmorcore/zizmor-pre-commit
rev: v1.16.0
- repo: https://github.com/woodruffw/zizmor-pre-commit
rev: v1.11.0
hooks:
- id: zizmor

View File

@@ -1,53 +1,5 @@
# Changelog
## 0.14.2
Released on 2025-10-23.
### Preview features
- \[`flake8-gettext`\] Resolve qualified names and built-in bindings (`INT001`, `INT002`, `INT003`) ([#19045](https://github.com/astral-sh/ruff/pull/19045))
### Bug fixes
- Avoid reusing nested, interpolated quotes before Python 3.12 ([#20930](https://github.com/astral-sh/ruff/pull/20930))
- Catch syntax errors in nested interpolations before Python 3.12 ([#20949](https://github.com/astral-sh/ruff/pull/20949))
- \[`fastapi`\] Handle ellipsis defaults in `FAST002` autofix ([#20810](https://github.com/astral-sh/ruff/pull/20810))
- \[`flake8-simplify`\] Skip `SIM911` when unknown arguments are present ([#20697](https://github.com/astral-sh/ruff/pull/20697))
- \[`pyupgrade`\] Always parenthesize assignment expressions in fix for `f-string` (`UP032`) ([#21003](https://github.com/astral-sh/ruff/pull/21003))
- \[`pyupgrade`\] Fix `UP032` conversion for decimal ints with underscores ([#21022](https://github.com/astral-sh/ruff/pull/21022))
- \[`fastapi`\] Skip autofix for keyword and `__debug__` path params (`FAST003`) ([#20960](https://github.com/astral-sh/ruff/pull/20960))
### Rule changes
- \[`flake8-bugbear`\] Skip `B905` and `B912` for fewer than two iterables and no starred arguments ([#20998](https://github.com/astral-sh/ruff/pull/20998))
- \[`ruff`\] Use `DiagnosticTag` for more `pyflakes` and `pandas` rules ([#20801](https://github.com/astral-sh/ruff/pull/20801))
### CLI
- Improve JSON output from `ruff rule` ([#20168](https://github.com/astral-sh/ruff/pull/20168))
### Documentation
- Add source to testimonial ([#20971](https://github.com/astral-sh/ruff/pull/20971))
- Document when a rule was added ([#21035](https://github.com/astral-sh/ruff/pull/21035))
### Other changes
- [syntax-errors] Name is parameter and global ([#20426](https://github.com/astral-sh/ruff/pull/20426))
- [syntax-errors] Alternative `match` patterns bind different names ([#20682](https://github.com/astral-sh/ruff/pull/20682))
### Contributors
- [@hengky-kurniawan-1](https://github.com/hengky-kurniawan-1)
- [@ShalokShalom](https://github.com/ShalokShalom)
- [@robsdedude](https://github.com/robsdedude)
- [@LoicRiegel](https://github.com/LoicRiegel)
- [@TaKO8Ki](https://github.com/TaKO8Ki)
- [@dylwil3](https://github.com/dylwil3)
- [@11happy](https://github.com/11happy)
- [@ntBre](https://github.com/ntBre)
## 0.14.1
Released on 2025-10-16.

41
Cargo.lock generated
View File

@@ -295,9 +295,9 @@ checksum = "36f64beae40a84da1b4b26ff2761a5b895c12adc41dc25aaee1c4f2bbfe97a6e"
[[package]]
name = "bstr"
version = "1.12.1"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"regex-automata",
@@ -433,9 +433,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.50"
version = "4.5.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623"
checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f"
dependencies = [
"clap_builder",
"clap_derive",
@@ -443,9 +443,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.50"
version = "4.5.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0"
checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730"
dependencies = [
"anstream",
"anstyle",
@@ -1007,7 +1007,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.60.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -1224,9 +1224,9 @@ dependencies = [
[[package]]
name = "get-size-derive2"
version = "0.7.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46b134aa084df7c3a513a1035c52f623e4b3065dfaf3d905a4f28a2e79b5bb3f"
checksum = "e3814abc7da8ab18d2fd820f5b540b5e39b6af0a32de1bdd7c47576693074843"
dependencies = [
"attribute-derive",
"quote",
@@ -1235,9 +1235,9 @@ dependencies = [
[[package]]
name = "get-size2"
version = "0.7.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0d51c9f2e956a517619ad9e7eaebc7a573f9c49b38152e12eade750f89156f9"
checksum = "5dfe2cec5b5ce8fb94dcdb16a1708baa4d0609cc3ce305ca5d3f6f2ffb59baed"
dependencies = [
"compact_str",
"get-size-derive2",
@@ -1523,9 +1523,9 @@ dependencies = [
[[package]]
name = "ignore"
version = "0.4.24"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81776e6f9464432afcc28d03e52eb101c93b6f0566f52aef2427663e700f0403"
checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
dependencies = [
"crossbeam-deque",
"globset",
@@ -2835,7 +2835,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.14.2"
version = "0.14.1"
dependencies = [
"anyhow",
"argfile",
@@ -3092,7 +3092,7 @@ dependencies = [
[[package]]
name = "ruff_linter"
version = "0.14.2"
version = "0.14.1"
dependencies = [
"aho-corasick",
"anyhow",
@@ -3447,7 +3447,7 @@ dependencies = [
[[package]]
name = "ruff_wasm"
version = "0.14.2"
version = "0.14.1"
dependencies = [
"console_error_panic_hook",
"console_log",
@@ -3563,7 +3563,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "salsa"
version = "0.24.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=cdd0b85516a52c18b8a6d17a2279a96ed6c3e198#cdd0b85516a52c18b8a6d17a2279a96ed6c3e198"
source = "git+https://github.com/salsa-rs/salsa.git?rev=ef9f9329be6923acd050c8dddd172e3bc93e8051#ef9f9329be6923acd050c8dddd172e3bc93e8051"
dependencies = [
"boxcar",
"compact_str",
@@ -3587,12 +3587,12 @@ dependencies = [
[[package]]
name = "salsa-macro-rules"
version = "0.24.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=cdd0b85516a52c18b8a6d17a2279a96ed6c3e198#cdd0b85516a52c18b8a6d17a2279a96ed6c3e198"
source = "git+https://github.com/salsa-rs/salsa.git?rev=ef9f9329be6923acd050c8dddd172e3bc93e8051#ef9f9329be6923acd050c8dddd172e3bc93e8051"
[[package]]
name = "salsa-macros"
version = "0.24.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=cdd0b85516a52c18b8a6d17a2279a96ed6c3e198#cdd0b85516a52c18b8a6d17a2279a96ed6c3e198"
source = "git+https://github.com/salsa-rs/salsa.git?rev=ef9f9329be6923acd050c8dddd172e3bc93e8051#ef9f9329be6923acd050c8dddd172e3bc93e8051"
dependencies = [
"proc-macro2",
"quote",
@@ -4434,12 +4434,10 @@ dependencies = [
"glob",
"hashbrown 0.16.0",
"indexmap",
"indoc",
"insta",
"itertools 0.14.0",
"memchr",
"ordermap",
"pretty_assertions",
"quickcheck",
"quickcheck_macros",
"ruff_annotate_snippets",
@@ -4521,6 +4519,7 @@ name = "ty_test"
version = "0.0.0"
dependencies = [
"anyhow",
"bitflags 2.9.4",
"camino",
"colored 3.0.0",
"insta",

View File

@@ -146,7 +146,7 @@ regex-automata = { version = "0.4.9" }
rustc-hash = { version = "2.0.0" }
rustc-stable-hash = { version = "0.1.2" }
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "cdd0b85516a52c18b8a6d17a2279a96ed6c3e198", default-features = false, features = [
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "ef9f9329be6923acd050c8dddd172e3bc93e8051", default-features = false, features = [
"compact_str",
"macros",
"salsa_unstable",

View File

@@ -147,8 +147,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.14.2/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.14.2/install.ps1 | iex"
curl -LsSf https://astral.sh/ruff/0.14.1/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.14.1/install.ps1 | iex"
```
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -181,7 +181,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.14.2
rev: v0.14.1
hooks:
# Run the linter.
- id: ruff-check

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.14.2"
version = "0.14.1"
publish = true
authors = { workspace = true }
edition = { workspace = true }

View File

@@ -25,7 +25,6 @@ struct Explanation<'a> {
explanation: Option<&'a str>,
preview: bool,
status: RuleGroup,
source_location: SourceLocation,
}
impl<'a> Explanation<'a> {
@@ -44,10 +43,6 @@ impl<'a> Explanation<'a> {
explanation: rule.explanation(),
preview: rule.is_preview(),
status: rule.group(),
source_location: SourceLocation {
file: rule.file(),
line: rule.line(),
},
}
}
}
@@ -132,14 +127,3 @@ pub(crate) fn rules(format: HelpFormat) -> Result<()> {
}
Ok(())
}
/// The location of the rule's implementation in the Ruff source tree, relative to the repository
/// root.
///
/// For most rules this will point to the `#[derive(ViolationMetadata)]` line above the rule's
/// struct.
#[derive(Serialize)]
struct SourceLocation {
file: &'static str,
line: u32,
}

View File

@@ -9,7 +9,9 @@ use itertools::{Itertools, iterate};
use ruff_linter::linter::FixTable;
use serde::Serialize;
use ruff_db::diagnostic::{Diagnostic, DisplayDiagnosticConfig, SecondaryCode};
use ruff_db::diagnostic::{
Diagnostic, DiagnosticFormat, DisplayDiagnosticConfig, DisplayDiagnostics, SecondaryCode,
};
use ruff_linter::fs::relativize_path;
use ruff_linter::logging::LogLevel;
use ruff_linter::message::{EmitterContext, render_diagnostics};
@@ -388,18 +390,21 @@ impl Printer {
let context = EmitterContext::new(&diagnostics.notebook_indexes);
let format = if preview {
self.format
DiagnosticFormat::Full
} else {
OutputFormat::Concise
DiagnosticFormat::Concise
};
let config = DisplayDiagnosticConfig::default()
.preview(preview)
.hide_severity(true)
.color(!cfg!(test) && colored::control::SHOULD_COLORIZE.should_colorize())
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
.with_fix_applicability(self.unsafe_fixes.required_applicability())
.show_fix_diff(preview);
render_diagnostics(writer, format, config, &context, &diagnostics.inner)?;
.format(format)
.with_fix_applicability(self.unsafe_fixes.required_applicability());
write!(
writer,
"{}",
DisplayDiagnostics::new(&context, &config, &diagnostics.inner)
)?;
}
writer.flush()?;

View File

@@ -953,11 +953,7 @@ fn rule_f401() {
#[test]
fn rule_f401_output_json() {
insta::with_settings!({filters => vec![
(r#"("file": ")[^"]+(",)"#, "$1<FILE>$2"),
]}, {
assert_cmd_snapshot!(ruff_cmd().args(["rule", "F401", "--output-format", "json"]));
});
assert_cmd_snapshot!(ruff_cmd().args(["rule", "F401", "--output-format", "json"]));
}
#[test]

View File

@@ -25,14 +25,6 @@ exit_code: 0
"fix_availability": "Sometimes",
"explanation": "## What it does\nChecks for unused imports.\n\n## Why is this bad?\nUnused imports add a performance overhead at runtime, and risk creating\nimport cycles. They also increase the cognitive load of reading the code.\n\nIf an import statement is used to check for the availability or existence\nof a module, consider using `importlib.util.find_spec` instead.\n\nIf an import statement is used to re-export a symbol as part of a module's\npublic interface, consider using a \"redundant\" import alias, which\ninstructs Ruff (and other tools) to respect the re-export, and avoid\nmarking it as unused, as in:\n\n```python\nfrom module import member as member\n```\n\nAlternatively, you can use `__all__` to declare a symbol as part of the module's\ninterface, as in:\n\n```python\n# __init__.py\nimport some_module\n\n__all__ = [\"some_module\"]\n```\n\n## Preview\nWhen [preview] is enabled (and certain simplifying assumptions\nare met), we analyze all import statements for a given module\nwhen determining whether an import is used, rather than simply\nthe last of these statements. This can result in both different and\nmore import statements being marked as unused.\n\nFor example, if a module consists of\n\n```python\nimport a\nimport a.b\n```\n\nthen both statements are marked as unused under [preview], whereas\nonly the second is marked as unused under stable behavior.\n\nAs another example, if a module consists of\n\n```python\nimport a.b\nimport a\n\na.b.foo()\n```\n\nthen a diagnostic will only be emitted for the first line under [preview],\nwhereas a diagnostic would only be emitted for the second line under\nstable behavior.\n\nNote that this behavior is somewhat subjective and is designed\nto conform to the developer's intuition rather than Python's actual\nexecution. To wit, the statement `import a.b` automatically executes\n`import a`, so in some sense `import a` is _always_ redundant\nin the presence of `import a.b`.\n\n\n## Fix safety\n\nFixes to remove unused imports are safe, except in `__init__.py` files.\n\nApplying fixes to `__init__.py` files is currently in preview. The fix offered depends on the\ntype of the unused import. Ruff will suggest a safe fix to export first-party imports with\neither a redundant alias or, if already present in the file, an `__all__` entry. If multiple\n`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix\nto remove third-party and standard library imports -- the fix is unsafe because the module's\ninterface changes.\n\nSee [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc)\nfor more details on how Ruff\ndetermines whether an import is first or third-party.\n\n## Example\n\n```python\nimport numpy as np # unused import\n\n\ndef area(radius):\n return 3.14 * radius**2\n```\n\nUse instead:\n\n```python\ndef area(radius):\n return 3.14 * radius**2\n```\n\nTo check the availability of a module, use `importlib.util.find_spec`:\n\n```python\nfrom importlib.util import find_spec\n\nif find_spec(\"numpy\") is not None:\n print(\"numpy is installed\")\nelse:\n print(\"numpy is not installed\")\n```\n\n## Options\n- `lint.ignore-init-module-imports`\n- `lint.pyflakes.allowed-unused-imports`\n\n## References\n- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)\n- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)\n- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)\n\n[preview]: https://docs.astral.sh/ruff/preview/\n",
"preview": false,
"status": {
"Stable": {
"since": "v0.0.18"
}
},
"source_location": {
"file": "<FILE>",
"line": 145
}
"status": "Stable"
}
----- stderr -----

View File

@@ -667,7 +667,7 @@ fn attrs(criterion: &mut Criterion) {
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY313,
},
110,
100,
);
bench_project(&benchmark, criterion);
@@ -684,7 +684,7 @@ fn anyio(criterion: &mut Criterion) {
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY313,
},
150,
100,
);
bench_project(&benchmark, criterion);

View File

@@ -210,7 +210,7 @@ static TANJUN: Benchmark = Benchmark::new(
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
320,
100,
);
static STATIC_FRAME: Benchmark = Benchmark::new(
@@ -226,7 +226,7 @@ static STATIC_FRAME: Benchmark = Benchmark::new(
max_dep_date: "2025-08-09",
python_version: PythonVersion::PY311,
},
800,
630,
);
#[track_caller]

View File

@@ -200,12 +200,7 @@ impl System for OsSystem {
/// The walker ignores files according to [`ignore::WalkBuilder::standard_filters`]
/// when setting [`WalkDirectoryBuilder::standard_filters`] to true.
fn walk_directory(&self, path: &SystemPath) -> WalkDirectoryBuilder {
WalkDirectoryBuilder::new(
path,
OsDirectoryWalker {
cwd: self.current_directory().to_path_buf(),
},
)
WalkDirectoryBuilder::new(path, OsDirectoryWalker {})
}
fn glob(
@@ -459,9 +454,7 @@ struct ListedDirectory {
}
#[derive(Debug)]
struct OsDirectoryWalker {
cwd: SystemPathBuf,
}
struct OsDirectoryWalker;
impl DirectoryWalker for OsDirectoryWalker {
fn walk(
@@ -480,7 +473,6 @@ impl DirectoryWalker for OsDirectoryWalker {
};
let mut builder = ignore::WalkBuilder::new(first.as_std_path());
builder.current_dir(self.cwd.as_std_path());
builder.standard_filters(standard_filters);
builder.hidden(hidden);

View File

@@ -8,7 +8,6 @@ use std::path::PathBuf;
use anyhow::Result;
use itertools::Itertools;
use regex::{Captures, Regex};
use ruff_linter::codes::RuleGroup;
use strum::IntoEnumIterator;
use ruff_linter::FixAvailability;
@@ -32,47 +31,6 @@ pub(crate) fn main(args: &Args) -> Result<()> {
let _ = writeln!(&mut output, "# {} ({})", rule.name(), rule.noqa_code());
let status_text = match rule.group() {
RuleGroup::Stable { since } => {
format!(
r#"Added in <a href="https://github.com/astral-sh/ruff/releases/tag/{since}">{since}</a>"#
)
}
RuleGroup::Preview { since } => {
format!(
r#"Preview (since <a href="https://github.com/astral-sh/ruff/releases/tag/{since}">{since}</a>)"#
)
}
RuleGroup::Deprecated { since } => {
format!(
r#"Deprecated (since <a href="https://github.com/astral-sh/ruff/releases/tag/{since}">{since}</a>)"#
)
}
RuleGroup::Removed { since } => {
format!(
r#"Removed (since <a href="https://github.com/astral-sh/ruff/releases/tag/{since}">{since}</a>)"#
)
}
};
let _ = writeln!(
&mut output,
r#"<small>
{status_text} ·
<a href="https://github.com/astral-sh/ruff/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20(%27{encoded_name}%27%20OR%20{rule_code})" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/{file}#L{line}" target="_blank">View source</a>
</small>
"#,
encoded_name =
url::form_urlencoded::byte_serialize(rule.name().as_str().as_bytes())
.collect::<String>(),
rule_code = rule.noqa_code(),
file =
url::form_urlencoded::byte_serialize(rule.file().replace('\\', "/").as_bytes())
.collect::<String>(),
line = rule.line(),
);
let (linter, _) = Linter::parse_code(&rule.noqa_code().to_string()).unwrap();
if linter.url().is_some() {
let common_prefix: String = match linter.common_prefix() {

View File

@@ -32,24 +32,20 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>,
table_out.push('\n');
for rule in rules {
let status_token = match rule.group() {
RuleGroup::Removed { since } => {
RuleGroup::Removed => {
format!(
"<span {SYMBOL_STYLE} title='Rule was removed in {since}'>{REMOVED_SYMBOL}</span>"
"<span {SYMBOL_STYLE} title='Rule has been removed'>{REMOVED_SYMBOL}</span>"
)
}
RuleGroup::Deprecated { since } => {
RuleGroup::Deprecated => {
format!(
"<span {SYMBOL_STYLE} title='Rule has been deprecated since {since}'>{WARNING_SYMBOL}</span>"
"<span {SYMBOL_STYLE} title='Rule has been deprecated'>{WARNING_SYMBOL}</span>"
)
}
RuleGroup::Preview { since } => {
format!(
"<span {SYMBOL_STYLE} title='Rule has been in preview since {since}'>{PREVIEW_SYMBOL}</span>"
)
}
RuleGroup::Stable { since } => {
format!("<span {SYMBOL_STYLE} title='Rule has been stable since {since}'></span>")
RuleGroup::Preview => {
format!("<span {SYMBOL_STYLE} title='Rule is in preview'>{PREVIEW_SYMBOL}</span>")
}
RuleGroup::Stable => format!("<span {SYMBOL_STYLE}></span>"),
};
let fix_token = match rule.fixable() {

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_linter"
version = "0.14.2"
version = "0.14.1"
publish = false
authors = { workspace = true }
edition = { workspace = true }

View File

@@ -10,7 +10,6 @@ from airflow.datasets import (
)
from airflow.datasets.manager import DatasetManager
from airflow.lineage.hook import DatasetLineageInfo, HookLineageCollector
from airflow.models.dag import DAG
from airflow.providers.amazon.aws.auth_manager.aws_auth_manager import AwsAuthManager
from airflow.providers.apache.beam.hooks import BeamHook, NotAir302HookError
from airflow.providers.google.cloud.secrets.secret_manager import (
@@ -21,7 +20,6 @@ from airflow.providers_manager import ProvidersManager
from airflow.secrets.base_secrets import BaseSecretsBackend
from airflow.secrets.local_filesystem import LocalFilesystemBackend
# airflow.Dataset
dataset_from_root = DatasetFromRoot()
dataset_from_root.iter_datasets()
@@ -58,10 +56,6 @@ hlc.add_input_dataset()
hlc.add_output_dataset()
hlc.collected_datasets()
# airflow.models.dag.DAG
test_dag = DAG(dag_id="test_dag")
test_dag.create_dagrun()
# airflow.providers.amazon.auth_manager.aws_auth_manager
aam = AwsAuthManager()
aam.is_authorized_dataset()
@@ -102,15 +96,3 @@ base_secret_backend.get_connections()
# airflow.secrets.local_filesystem
lfb = LocalFilesystemBackend()
lfb.get_connections()
from airflow.models import DAG
# airflow.DAG
test_dag = DAG(dag_id="test_dag")
test_dag.create_dagrun()
from airflow import DAG
# airflow.DAG
test_dag = DAG(dag_id="test_dag")
test_dag.create_dagrun()

View File

@@ -91,20 +91,10 @@ get_unique_task_id()
task_decorator_factory()
from airflow.models import DagParam, Param, ParamsDict
from airflow.models import Param
# airflow.models
Param()
DagParam()
ParamsDict()
from airflow.models.param import DagParam, Param, ParamsDict
# airflow.models.param
Param()
DagParam()
ParamsDict()
from airflow.sensors.base import (

View File

@@ -46,9 +46,3 @@ class CorrectModel(models.Model):
max_length=255, null=True, blank=True, unique=True
)
urlfieldu = models.URLField(max_length=255, null=True, blank=True, unique=True)
class IncorrectModelWithSimpleAnnotations(models.Model):
charfield: models.CharField = models.CharField(max_length=255, null=True)
textfield: models.TextField = models.TextField(max_length=255, null=True)
slugfield: models.SlugField = models.SlugField(max_length=255, null=True)

View File

@@ -1,7 +0,0 @@
# Regression test for https://github.com/astral-sh/ruff/issues/21023
'' '
"" ""
'' '' '
"" "" "
f"" f"
f"" f"" f"

View File

@@ -359,29 +359,3 @@ class Generic5(list[PotentialTypeVar]):
def __new__(cls: type[Generic5]) -> Generic5: ...
def __enter__(self: Generic5) -> Generic5: ...
# Test cases based on issue #20781 - metaclasses that triggers IsMetaclass::Maybe
class MetaclassInWhichSelfCannotBeUsed5(type(Protocol)):
def __new__(
cls, name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any], **kwargs: Any
) -> MetaclassInWhichSelfCannotBeUsed5:
new_class = super().__new__(cls, name, bases, attrs, **kwargs)
return new_class
import django.db.models.base
class MetaclassInWhichSelfCannotBeUsed6(django.db.models.base.ModelBase):
def __new__(cls, name: str, bases: tuple[Any, ...], attrs: dict[str, Any], **kwargs: Any) -> MetaclassInWhichSelfCannotBeUsed6:
...
class MetaclassInWhichSelfCannotBeUsed7(django.db.models.base.ModelBase):
def __new__(cls, /, name: str, bases: tuple[object, ...], attrs: dict[str, object], **kwds: object) -> MetaclassInWhichSelfCannotBeUsed7:
...
class MetaclassInWhichSelfCannotBeUsed8(django.db.models.base.ModelBase):
def __new__(cls, name: builtins.str, bases: tuple, attributes: dict, /, **kw) -> MetaclassInWhichSelfCannotBeUsed8:
...

View File

@@ -252,28 +252,3 @@ from some_module import PotentialTypeVar
class Generic5(list[PotentialTypeVar]):
def __new__(cls: type[Generic5]) -> Generic5: ...
def __enter__(self: Generic5) -> Generic5: ...
# Test case based on issue #20781 - metaclass that triggers IsMetaclass::Maybe
class MetaclassInWhichSelfCannotBeUsed5(type(Protocol)):
def __new__(
cls, name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any], **kwargs: Any
) -> MetaclassInWhichSelfCannotBeUsed5: ...
import django.db.models.base
class MetaclassInWhichSelfCannotBeUsed6(django.db.models.base.ModelBase):
def __new__(cls, name: str, bases: tuple[Any, ...], attrs: dict[str, Any], **kwargs: Any) -> MetaclassInWhichSelfCannotBeUsed6:
...
class MetaclassInWhichSelfCannotBeUsed7(django.db.models.base.ModelBase):
def __new__(cls, /, name: str, bases: tuple[object, ...], attrs: dict[str, object], **kwds: object) -> MetaclassInWhichSelfCannotBeUsed7:
...
class MetaclassInWhichSelfCannotBeUsed8(django.db.models.base.ModelBase):
def __new__(cls, name: builtins.str, bases: tuple, attributes: dict, /, **kw) -> MetaclassInWhichSelfCannotBeUsed8:
...

View File

@@ -34,7 +34,3 @@ def foo():
# https://github.com/astral-sh/ruff/issues/18776
flag_stars = {}
for country, stars in(zip)(flag_stars.keys(), flag_stars.values()):...
# Regression test for https://github.com/astral-sh/ruff/issues/18778
d = {}
for country, stars in zip(d.keys(*x), d.values("hello")):...

View File

@@ -14,14 +14,3 @@ def f():
import os
print(os)
# regression test for https://github.com/astral-sh/ruff/issues/21121
from dataclasses import KW_ONLY, dataclass
@dataclass
class DataClass:
a: int
_: KW_ONLY # should be an exception to TC003, even with future-annotations
b: int

View File

@@ -370,22 +370,3 @@ class Foo:
The flag converter instance with all flags parsed.
"""
return
# OK
def baz(x: int) -> int:
"""
Show a `Warnings` DOC102 false positive.
Parameters
----------
x : int
Warnings
--------
This function demonstrates a DOC102 false positive
Returns
-------
int
"""
return x

View File

@@ -81,55 +81,3 @@ def calculate_speed(distance: float, time: float) -> float:
except TypeError:
print("Not a number? Shame on you!")
raise
# This should NOT trigger DOC502 because OSError is explicitly re-raised
def f():
"""Do nothing.
Raises:
OSError: If the OS errors.
"""
try:
pass
except OSError as e:
raise e
# This should NOT trigger DOC502 because OSError is explicitly re-raised with from None
def g():
"""Do nothing.
Raises:
OSError: If the OS errors.
"""
try:
pass
except OSError as e:
raise e from None
# This should NOT trigger DOC502 because ValueError is explicitly re-raised from tuple exception
def h():
"""Do nothing.
Raises:
ValueError: If something goes wrong.
"""
try:
pass
except (ValueError, TypeError) as e:
raise e
# This should NOT trigger DOC502 because TypeError is explicitly re-raised from tuple exception
def i():
"""Do nothing.
Raises:
TypeError: If something goes wrong.
"""
try:
pass
except (ValueError, TypeError) as e:
raise e

View File

@@ -1,21 +0,0 @@
class C:
f = lambda self: __class__
print(C().f().__name__)
# Test: nested lambda
class D:
g = lambda self: (lambda: __class__)
print(D().g()().__name__)
# Test: lambda outside class (should still fail)
h = lambda: __class__
# Test: lambda referencing module-level variable (should not be flagged as F821)
import uuid
class E:
uuid = lambda: str(uuid.uuid4())

View File

@@ -1,131 +0,0 @@
"""Test cases for PLR1708 stop-iteration-return."""
# Valid cases - should not trigger the rule
def normal_function():
raise StopIteration # Not a generator, should not trigger
def normal_function_with_value():
raise StopIteration("value") # Not a generator, should not trigger
def generator_with_return():
yield 1
yield 2
return "finished" # This is the correct way
def generator_with_yield_from():
yield from [1, 2, 3]
def generator_without_stop_iteration():
yield 1
yield 2
# No explicit termination
def generator_with_other_exception():
yield 1
raise ValueError("something else") # Different exception
# Invalid cases - should trigger the rule
def generator_with_stop_iteration():
yield 1
yield 2
raise StopIteration # Should trigger
def generator_with_stop_iteration_value():
yield 1
yield 2
raise StopIteration("finished") # Should trigger
def generator_with_stop_iteration_expr():
yield 1
yield 2
raise StopIteration(1 + 2) # Should trigger
def async_generator_with_stop_iteration():
yield 1
yield 2
raise StopIteration("async") # Should trigger
def nested_generator():
def inner_gen():
yield 1
raise StopIteration("inner") # Should trigger
yield from inner_gen()
def generator_in_class():
class MyClass:
def generator_method(self):
yield 1
raise StopIteration("method") # Should trigger
return MyClass
# Complex cases
def complex_generator():
try:
yield 1
yield 2
raise StopIteration("complex") # Should trigger
except ValueError:
yield 3
finally:
pass
def generator_with_conditional_stop_iteration(condition):
yield 1
if condition:
raise StopIteration("conditional") # Should trigger
yield 2
# Edge cases
def generator_with_bare_stop_iteration():
yield 1
raise StopIteration # Should trigger (no arguments)
def generator_with_stop_iteration_in_loop():
for i in range(5):
yield i
if i == 3:
raise StopIteration("loop") # Should trigger
# Should not trigger - different exceptions
def generator_with_runtime_error():
yield 1
raise RuntimeError("not StopIteration") # Should not trigger
def generator_with_custom_exception():
yield 1
raise CustomException("custom") # Should not trigger
class CustomException(Exception):
pass
# Generator comprehensions should not be affected
list_comp = [x for x in range(10)] # Should not trigger
# Lambda in generator context
def generator_with_lambda():
yield 1
func = lambda x: x # Just a regular lambda
yield 2

View File

@@ -26,8 +26,3 @@
"{.real}".format({1, 2})
"{.real}".format({1: 2, 3: 4})
"{}".format((i for i in range(2)))
# https://github.com/astral-sh/ruff/issues/21017
"{.real}".format(1_2)
"{0.real}".format(1_2)
"{a.real}".format(a=1_2)

View File

@@ -1,17 +0,0 @@
"""
Regression test for an ecosystem hit on
https://github.com/astral-sh/ruff/pull/21125.
We should mark all of the components of special dataclass annotations as
runtime-required, not just the first layer.
"""
from dataclasses import dataclass
from typing import ClassVar, Optional
@dataclass(frozen=True)
class EmptyCell:
_singleton: ClassVar[Optional["EmptyCell"]] = None
# the behavior of _singleton above should match a non-ClassVar
_doubleton: "EmptyCell"

View File

@@ -1,13 +0,0 @@
"""This is placed in a separate fixture as `TypeVar` needs to be imported
from `typing_extensions` to support default arguments in Python version < 3.13.
We verify that UP046 doesn't apply in this case.
"""
from typing import Generic
from typing_extensions import TypeVar
T = TypeVar("T", default=str)
class DefaultTypeVar(Generic[T]):
var: T

View File

@@ -1,12 +0,0 @@
"""This is placed in a separate fixture as `TypeVar` needs to be imported
from `typing_extensions` to support default arguments in Python version < 3.13.
We verify that UP047 doesn't apply in this case.
"""
from typing_extensions import TypeVar
T = TypeVar("T", default=int)
def default_var(var: T) -> T:
return var

View File

@@ -69,19 +69,3 @@ Decimal(float("\N{space}\N{hyPHen-MINus}nan"))
Decimal(float("\x20\N{character tabulation}\N{hyphen-minus}nan"))
Decimal(float(" -" "nan"))
Decimal(float("-nAn"))
# Test cases for digit separators (safe fixes)
# https://github.com/astral-sh/ruff/issues/20572
Decimal("15_000_000") # Safe fix: normalizes separators, becomes Decimal(15_000_000)
Decimal("1_234_567") # Safe fix: normalizes separators, becomes Decimal(1_234_567)
Decimal("-5_000") # Safe fix: normalizes separators, becomes Decimal(-5_000)
Decimal("+9_999") # Safe fix: normalizes separators, becomes Decimal(+9_999)
# Test cases for non-thousands separators
Decimal("12_34_56_78") # Safe fix: preserves non-thousands separators
Decimal("1234_5678") # Safe fix: preserves non-thousands separators
# Separators _and_ leading zeros
Decimal("0001_2345")
Decimal("000_1_2345")
Decimal("000_000")

View File

@@ -43,29 +43,3 @@ logging.warning("Value: %r", repr(42))
logging.error("Error: %r", repr([1, 2, 3]))
logging.info("Debug info: %s", repr("test\nstring"))
logging.warning("Value: %s", repr(42))
# %s + ascii()
logging.info("ASCII: %s", ascii("Hello\nWorld"))
logging.warning("ASCII: %s", ascii("test"))
# %s + oct()
logging.info("Octal: %s", oct(42))
logging.warning("Octal: %s", oct(255))
# %s + hex()
logging.info("Hex: %s", hex(42))
logging.warning("Hex: %s", hex(255))
# Test with imported functions
from logging import info, log
info("ASCII: %s", ascii("Hello\nWorld"))
log(logging.INFO, "ASCII: %s", ascii("test"))
info("Octal: %s", oct(42))
log(logging.INFO, "Octal: %s", oct(255))
info("Hex: %s", hex(42))
log(logging.INFO, "Hex: %s", hex(255))

View File

@@ -1,12 +0,0 @@
async def f(): return [[x async for x in foo(n)] for n in range(3)]
async def test(): return [[x async for x in elements(n)] async for n in range(3)]
async def f(): [x for x in foo()] and [x async for x in foo()]
async def f():
def g(): ...
[x async for x in foo()]
[x async for x in y]

View File

@@ -1,3 +0,0 @@
match x:
case Point(x=1, x=2):
pass

View File

@@ -1,3 +0,0 @@
match x:
case {'key': 1, 'key': 2}:
pass

View File

@@ -1 +0,0 @@
class C[T, T]: pass

View File

@@ -1,29 +0,0 @@
def f(a):
global a
def g(a):
if True:
global a
def h(a):
def inner():
global a
def i(a):
try:
global a
except Exception:
pass
def f(a):
a = 1
global a
def f(a):
a = 1
a = 2
global a
def f(a):
class Inner:
global a # ok

View File

@@ -1,8 +0,0 @@
type X[T: (yield 1)] = int
type Y = (yield 1)
def f[T](x: int) -> (y := 3): return x
class C[T]((yield from [object])):
pass

View File

@@ -1,8 +0,0 @@
def func():
return *x
for *x in range(10):
pass
def func():
yield *x

View File

@@ -1,11 +0,0 @@
match value:
case _:
pass
case 1:
pass
match value:
case irrefutable:
pass
case 1:
pass

View File

@@ -1,5 +0,0 @@
match x:
case [a, a]:
pass
case _:
pass

View File

@@ -1 +0,0 @@
[x:= 2 for x in range(2)]

View File

@@ -1,7 +0,0 @@
__debug__ = False
def process(__debug__):
pass
class Generic[__debug__]:
pass

View File

@@ -951,9 +951,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.is_rule_enabled(Rule::MisplacedBareRaise) {
pylint::rules::misplaced_bare_raise(checker, raise);
}
if checker.is_rule_enabled(Rule::StopIterationReturn) {
pylint::rules::stop_iteration_return(checker, raise);
}
}
Stmt::AugAssign(aug_assign @ ast::StmtAugAssign { target, .. }) => {
if checker.is_rule_enabled(Rule::GlobalStatement) {

View File

@@ -1400,14 +1400,6 @@ impl<'a> Visitor<'a> for Checker<'a> {
AnnotationContext::RuntimeRequired => {
self.visit_runtime_required_annotation(annotation);
}
AnnotationContext::RuntimeEvaluated
if flake8_type_checking::helpers::is_dataclass_meta_annotation(
annotation,
self.semantic(),
) =>
{
self.visit_runtime_required_annotation(annotation);
}
AnnotationContext::RuntimeEvaluated => {
self.visit_runtime_evaluated_annotation(annotation);
}
@@ -2124,7 +2116,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
| Expr::DictComp(_)
| Expr::SetComp(_) => {
self.analyze.scopes.push(self.semantic.scope_id);
self.semantic.pop_scope(); // Lambda/Generator/Comprehension scope
self.semantic.pop_scope();
}
_ => {}
}
@@ -3049,35 +3041,7 @@ impl<'a> Checker<'a> {
if let Some(parameters) = parameters {
self.visit_parameters(parameters);
}
// Here we add the implicit scope surrounding a lambda which allows code in the
// lambda to access `__class__` at runtime when the lambda is defined within a class.
// See the `ScopeKind::DunderClassCell` docs for more information.
let added_dunder_class_scope = if self
.semantic
.current_scopes()
.any(|scope| scope.kind.is_class())
{
self.semantic.push_scope(ScopeKind::DunderClassCell);
let binding_id = self.semantic.push_binding(
TextRange::default(),
BindingKind::DunderClassCell,
BindingFlags::empty(),
);
self.semantic
.current_scope_mut()
.add("__class__", binding_id);
true
} else {
false
};
self.visit_expr(body);
// Pop the DunderClassCell scope if it was added
if added_dunder_class_scope {
self.semantic.pop_scope();
}
}
}
self.semantic.restore(snapshot);

File diff suppressed because it is too large Load Diff

View File

@@ -11,8 +11,6 @@ pub(crate) static GOOGLE_SECTIONS: &[SectionKind] = &[
SectionKind::References,
SectionKind::Returns,
SectionKind::SeeAlso,
SectionKind::Warnings,
SectionKind::Warns,
SectionKind::Yields,
// Google-only
SectionKind::Args,
@@ -34,5 +32,7 @@ pub(crate) static GOOGLE_SECTIONS: &[SectionKind] = &[
SectionKind::Tip,
SectionKind::Todo,
SectionKind::Warning,
SectionKind::Warnings,
SectionKind::Warns,
SectionKind::Yield,
];

View File

@@ -11,14 +11,11 @@ pub(crate) static NUMPY_SECTIONS: &[SectionKind] = &[
SectionKind::References,
SectionKind::Returns,
SectionKind::SeeAlso,
SectionKind::Warnings,
SectionKind::Warns,
SectionKind::Yields,
// NumPy-only
SectionKind::ExtendedSummary,
SectionKind::OtherParams,
SectionKind::OtherParameters,
SectionKind::Parameters,
SectionKind::Receives,
SectionKind::ShortSummary,
];

View File

@@ -36,7 +36,6 @@ pub(crate) enum SectionKind {
OtherParameters,
Parameters,
Raises,
Receives,
References,
Return,
Returns,
@@ -77,7 +76,6 @@ impl SectionKind {
"other parameters" => Some(Self::OtherParameters),
"parameters" => Some(Self::Parameters),
"raises" => Some(Self::Raises),
"receives" => Some(Self::Receives),
"references" => Some(Self::References),
"return" => Some(Self::Return),
"returns" => Some(Self::Returns),
@@ -119,7 +117,6 @@ impl SectionKind {
Self::OtherParameters => "Other Parameters",
Self::Parameters => "Parameters",
Self::Raises => "Raises",
Self::Receives => "Receives",
Self::References => "References",
Self::Return => "Return",
Self::Returns => "Returns",

View File

@@ -14,7 +14,7 @@ use ruff_text_size::TextSize;
/// The length of a line of text that is considered too long.
///
/// The allowed range of values is 1..=320
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct LineLength(
#[cfg_attr(feature = "schemars", schemars(range(min = 1, max = 320)))] NonZeroU16,
@@ -46,21 +46,6 @@ impl fmt::Display for LineLength {
}
}
impl<'de> serde::Deserialize<'de> for LineLength {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = u16::deserialize(deserializer)?;
Self::try_from(value).map_err(|_| {
serde::de::Error::custom(format!(
"line-length must be between 1 and {} (got {value})",
Self::MAX,
))
})
}
}
impl CacheKey for LineLength {
fn cache_key(&self, state: &mut CacheKeyHasher) {
state.write_u16(self.0.get());

View File

@@ -919,6 +919,17 @@ mod tests {
Ok(())
}
/// Wrapper around `test_contents_syntax_errors` for testing a snippet of code instead of a
/// file.
fn test_snippet_syntax_errors(contents: &str, settings: &LinterSettings) -> Vec<Diagnostic> {
let contents = dedent(contents);
test_contents_syntax_errors(
&SourceKind::Python(contents.to_string()),
Path::new("<filename>"),
settings,
)
}
/// A custom test runner that prints syntax errors in addition to other diagnostics. Adapted
/// from `flakes` in pyflakes/mod.rs.
fn test_contents_syntax_errors(
@@ -961,38 +972,245 @@ mod tests {
}
#[test_case(
Path::new("async_comprehension_outside_async_function.py"),
PythonVersion::PY311
"async_in_sync_error_on_310",
"async def f(): return [[x async for x in foo(n)] for n in range(3)]",
PythonVersion::PY310,
"AsyncComprehensionOutsideAsyncFunction"
)]
#[test_case(
Path::new("async_comprehension_outside_async_function.py"),
PythonVersion::PY310
"async_in_sync_okay_on_311",
"async def f(): return [[x async for x in foo(n)] for n in range(3)]",
PythonVersion::PY311,
"AsyncComprehensionOutsideAsyncFunction"
)]
#[test_case(Path::new("rebound_comprehension.py"), PythonVersion::PY310)]
#[test_case(Path::new("duplicate_type_parameter.py"), PythonVersion::PY312)]
#[test_case(Path::new("multiple_case_assignment.py"), PythonVersion::PY310)]
#[test_case(Path::new("duplicate_match_key.py"), PythonVersion::PY310)]
#[test_case(Path::new("duplicate_match_class_attribute.py"), PythonVersion::PY310)]
#[test_case(Path::new("invalid_star_expression.py"), PythonVersion::PY310)]
#[test_case(Path::new("irrefutable_case_pattern.py"), PythonVersion::PY310)]
#[test_case(Path::new("single_starred_assignment.py"), PythonVersion::PY310)]
#[test_case(Path::new("write_to_debug.py"), PythonVersion::PY312)]
#[test_case(Path::new("write_to_debug.py"), PythonVersion::PY310)]
#[test_case(Path::new("invalid_expression.py"), PythonVersion::PY312)]
#[test_case(Path::new("global_parameter.py"), PythonVersion::PY310)]
fn test_semantic_errors(path: &Path, python_version: PythonVersion) -> Result<()> {
let snapshot = format!(
"semantic_syntax_error_{}_{}",
path.to_string_lossy(),
python_version
);
let path = Path::new("resources/test/fixtures/semantic_errors").join(path);
let contents = std::fs::read_to_string(&path)?;
let source_kind = SourceKind::Python(contents);
#[test_case(
"async_in_sync_okay_on_310",
"async def test(): return [[x async for x in elements(n)] async for n in range(3)]",
PythonVersion::PY310,
"AsyncComprehensionOutsideAsyncFunction"
)]
#[test_case(
"deferred_function_body",
"
async def f(): [x for x in foo()] and [x async for x in foo()]
async def f():
def g(): ...
[x async for x in foo()]
",
PythonVersion::PY310,
"AsyncComprehensionOutsideAsyncFunction"
)]
#[test_case(
"async_in_sync_false_positive",
"[x async for x in y]",
PythonVersion::PY310,
"AsyncComprehensionOutsideAsyncFunction"
)]
#[test_case(
"rebound_comprehension",
"[x:= 2 for x in range(2)]",
PythonVersion::PY310,
"ReboundComprehensionVariable"
)]
#[test_case(
"duplicate_type_param",
"class C[T, T]: pass",
PythonVersion::PY312,
"DuplicateTypeParameter"
)]
#[test_case(
"multiple_case_assignment",
"
match x:
case [a, a]:
pass
case _:
pass
",
PythonVersion::PY310,
"MultipleCaseAssignment"
)]
#[test_case(
"duplicate_match_key",
"
match x:
case {'key': 1, 'key': 2}:
pass
",
PythonVersion::PY310,
"DuplicateMatchKey"
)]
#[test_case(
"global_parameter",
"
def f(a):
global a
let diagnostics = test_contents_syntax_errors(
&source_kind,
&path,
def g(a):
if True:
global a
def h(a):
def inner():
global a
def i(a):
try:
global a
except Exception:
pass
def f(a):
a = 1
global a
def f(a):
a = 1
a = 2
global a
def f(a):
class Inner:
global a # ok
",
PythonVersion::PY310,
"GlobalParameter"
)]
#[test_case(
"duplicate_match_class_attribute",
"
match x:
case Point(x=1, x=2):
pass
",
PythonVersion::PY310,
"DuplicateMatchClassAttribute"
)]
#[test_case(
"invalid_star_expression",
"
def func():
return *x
",
PythonVersion::PY310,
"InvalidStarExpression"
)]
#[test_case(
"invalid_star_expression_for",
"
for *x in range(10):
pass
",
PythonVersion::PY310,
"InvalidStarExpression"
)]
#[test_case(
"invalid_star_expression_yield",
"
def func():
yield *x
",
PythonVersion::PY310,
"InvalidStarExpression"
)]
#[test_case(
"irrefutable_case_pattern_wildcard",
"
match value:
case _:
pass
case 1:
pass
",
PythonVersion::PY310,
"IrrefutableCasePattern"
)]
#[test_case(
"irrefutable_case_pattern_capture",
"
match value:
case irrefutable:
pass
case 1:
pass
",
PythonVersion::PY310,
"IrrefutableCasePattern"
)]
#[test_case(
"single_starred_assignment",
"*a = [1, 2, 3, 4]",
PythonVersion::PY310,
"SingleStarredAssignment"
)]
#[test_case(
"write_to_debug",
"
__debug__ = False
",
PythonVersion::PY310,
"WriteToDebug"
)]
#[test_case(
"write_to_debug_in_function_param",
"
def process(__debug__):
pass
",
PythonVersion::PY310,
"WriteToDebug"
)]
#[test_case(
"write_to_debug_class_type_param",
"
class Generic[__debug__]:
pass
",
PythonVersion::PY312,
"WriteToDebug"
)]
#[test_case(
"invalid_expression_yield_in_type_param",
"
type X[T: (yield 1)] = int
",
PythonVersion::PY312,
"InvalidExpression"
)]
#[test_case(
"invalid_expression_yield_in_type_alias",
"
type Y = (yield 1)
",
PythonVersion::PY312,
"InvalidExpression"
)]
#[test_case(
"invalid_expression_walrus_in_return_annotation",
"
def f[T](x: int) -> (y := 3): return x
",
PythonVersion::PY312,
"InvalidExpression"
)]
#[test_case(
"invalid_expression_yield_from_in_base_class",
"
class C[T]((yield from [object])):
pass
",
PythonVersion::PY312,
"InvalidExpression"
)]
fn test_semantic_errors(
name: &str,
contents: &str,
python_version: PythonVersion,
error_type: &str,
) {
let snapshot = format!("semantic_syntax_error_{error_type}_{name}_{python_version}");
let diagnostics = test_snippet_syntax_errors(
contents,
&LinterSettings {
rules: settings::rule_table::RuleTable::empty(),
unresolved_target_version: python_version.into(),
@@ -1000,11 +1218,7 @@ mod tests {
..Default::default()
},
);
insta::with_settings!({filters => vec![(r"\\", "/")]}, {
assert_diagnostics!(format!("{snapshot}"), diagnostics);
});
Ok(())
assert_diagnostics!(snapshot, diagnostics);
}
#[test_case(PythonVersion::PY310)]

View File

@@ -150,7 +150,7 @@ mod tests {
for rule in Rule::iter() {
let (code, group) = (rule.noqa_code(), rule.group());
if matches!(group, RuleGroup::Removed { .. }) {
if matches!(group, RuleGroup::Removed) {
continue;
}

View File

@@ -209,15 +209,15 @@ impl RuleSelector {
self.all_rules().filter(move |rule| {
match rule.group() {
// Always include stable rules
RuleGroup::Stable { .. } => true,
RuleGroup::Stable => true,
// Enabling preview includes all preview rules unless explicit selection is turned on
RuleGroup::Preview { .. } => {
RuleGroup::Preview => {
preview_enabled && (self.is_exact() || !preview_require_explicit)
}
// Deprecated rules are excluded by default unless explicitly selected
RuleGroup::Deprecated { .. } => !preview_enabled && self.is_exact(),
RuleGroup::Deprecated => !preview_enabled && self.is_exact(),
// Removed rules are included if explicitly selected but will error downstream
RuleGroup::Removed { .. } => self.is_exact(),
RuleGroup::Removed => self.is_exact(),
}
})
}

View File

@@ -41,7 +41,6 @@ use crate::checkers::ast::Checker;
/// dag = DAG(dag_id="my_dag", schedule=timedelta(days=1))
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.13.0")]
pub(crate) struct AirflowDagNoScheduleArgument;
impl Violation for AirflowDagNoScheduleArgument {

View File

@@ -35,7 +35,6 @@ use crate::{FixAvailability, Violation};
/// fab_auth_manager_app = FabAuthManager().get_fastapi_app()
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.13.0")]
pub(crate) struct Airflow3MovedToProvider<'a> {
deprecated: QualifiedName<'a>,
replacement: ProviderReplacement,

View File

@@ -41,7 +41,6 @@ use ruff_text_size::TextRange;
/// yesterday = today - timedelta(days=1)
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.13.0")]
pub(crate) struct Airflow3Removal {
deprecated: String,
replacement: Replacement,
@@ -492,12 +491,6 @@ fn check_method(checker: &Checker, call_expr: &ExprCall) {
"collected_datasets" => Replacement::AttrName("collected_assets"),
_ => return,
},
["airflow", "models", "dag", "DAG"] | ["airflow", "models", "DAG"] | ["airflow", "DAG"] => {
match attr.as_str() {
"create_dagrun" => Replacement::None,
_ => return,
}
}
["airflow", "providers_manager", "ProvidersManager"] => match attr.as_str() {
"initialize_providers_dataset_uri_resources" => {
Replacement::AttrName("initialize_providers_asset_uri_resources")

View File

@@ -51,7 +51,6 @@ use ruff_text_size::TextRange;
/// )
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.13.0")]
pub(crate) struct Airflow3SuggestedToMoveToProvider<'a> {
deprecated: QualifiedName<'a>,
replacement: ProviderReplacement,

View File

@@ -37,7 +37,6 @@ use ruff_text_size::TextRange;
/// Asset(uri="test://test/")
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.13.0")]
pub(crate) struct Airflow3SuggestedUpdate {
deprecated: String,
replacement: Replacement,
@@ -262,14 +261,9 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
name: (*rest).to_string(),
}
}
[
"airflow",
"models",
..,
rest @ ("Param" | "ParamsDict" | "DagParam"),
] => Replacement::SourceModuleMoved {
["airflow", "models", "Param"] => Replacement::Rename {
module: "airflow.sdk.definitions.param",
name: (*rest).to_string(),
name: "Param",
},
// airflow.models.baseoperator
@@ -288,12 +282,10 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
},
// airflow.model..DAG
["airflow", "models", "dag", "DAG"] | ["airflow", "models", "DAG"] | ["airflow", "DAG"] => {
Replacement::SourceModuleMoved {
module: "airflow.sdk",
name: "DAG".to_string(),
}
}
["airflow", "models", .., "DAG"] => Replacement::SourceModuleMoved {
module: "airflow.sdk",
name: "DAG".to_string(),
},
// airflow.sensors.base
[

View File

@@ -32,7 +32,6 @@ use crate::checkers::ast::Checker;
/// my_task = PythonOperator(task_id="my_task")
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.271")]
pub(crate) struct AirflowVariableNameTaskIdMismatch {
task_id: String,
}

View File

@@ -1,25 +1,6 @@
---
source: crates/ruff_linter/src/rules/airflow/mod.rs
---
AIR311 [*] `airflow.DAG` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
--> AIR311_args.py:13:1
|
13 | DAG(dag_id="class_sla_callback", sla_miss_callback=sla_callback)
| ^^^
|
help: Use `DAG` from `airflow.sdk` instead.
2 |
3 | from datetime import timedelta
4 |
- from airflow import DAG, dag
5 + from airflow import dag
6 | from airflow.operators.datetime import BranchDateTimeOperator
7 + from airflow.sdk import DAG
8 |
9 |
10 | def sla_callback(*arg, **kwargs):
note: This is an unsafe fix and may change runtime behavior
AIR311 `sla_miss_callback` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
--> AIR311_args.py:13:34
|

View File

@@ -737,185 +737,79 @@ AIR311 [*] `airflow.models.Param` is removed in Airflow 3.0; It still works in A
96 | # airflow.models
97 | Param()
| ^^^^^
98 | DagParam()
99 | ParamsDict()
|
help: Use `Param` from `airflow.sdk.definitions.param` instead.
91 | task_decorator_factory()
92 |
93 |
- from airflow.models import DagParam, Param, ParamsDict
94 + from airflow.models import DagParam, ParamsDict
95 + from airflow.sdk.definitions.param import Param
96 |
97 | # airflow.models
98 | Param()
note: This is an unsafe fix and may change runtime behavior
AIR311 [*] `airflow.models.DagParam` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
--> AIR311_names.py:98:1
|
- from airflow.models import Param
94 + from airflow.sdk.definitions.param import Param
95 |
96 | # airflow.models
97 | Param()
98 | DagParam()
| ^^^^^^^^
99 | ParamsDict()
|
help: Use `DagParam` from `airflow.sdk.definitions.param` instead.
91 | task_decorator_factory()
92 |
93 |
- from airflow.models import DagParam, Param, ParamsDict
94 + from airflow.models import Param, ParamsDict
95 + from airflow.sdk.definitions.param import DagParam
96 |
97 | # airflow.models
98 | Param()
note: This is an unsafe fix and may change runtime behavior
AIR311 [*] `airflow.models.ParamsDict` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
--> AIR311_names.py:99:1
|
97 | Param()
98 | DagParam()
99 | ParamsDict()
| ^^^^^^^^^^
|
help: Use `ParamsDict` from `airflow.sdk.definitions.param` instead.
91 | task_decorator_factory()
92 |
93 |
- from airflow.models import DagParam, Param, ParamsDict
94 + from airflow.models import DagParam, Param
95 + from airflow.sdk.definitions.param import ParamsDict
96 |
97 | # airflow.models
98 | Param()
note: This is an unsafe fix and may change runtime behavior
AIR311 [*] `airflow.models.param.Param` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
--> AIR311_names.py:105:1
|
104 | # airflow.models.param
105 | Param()
| ^^^^^
106 | DagParam()
107 | ParamsDict()
|
help: Use `Param` from `airflow.sdk.definitions.param` instead.
99 | ParamsDict()
100 |
101 |
- from airflow.models.param import DagParam, Param, ParamsDict
102 + from airflow.models.param import DagParam, ParamsDict
103 + from airflow.sdk.definitions.param import Param
104 |
105 | # airflow.models.param
106 | Param()
note: This is an unsafe fix and may change runtime behavior
AIR311 [*] `airflow.models.param.DagParam` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
--> AIR311_names.py:106:1
|
104 | # airflow.models.param
105 | Param()
106 | DagParam()
| ^^^^^^^^
107 | ParamsDict()
|
help: Use `DagParam` from `airflow.sdk.definitions.param` instead.
99 | ParamsDict()
100 |
101 |
- from airflow.models.param import DagParam, Param, ParamsDict
102 + from airflow.models.param import Param, ParamsDict
103 + from airflow.sdk.definitions.param import DagParam
104 |
105 | # airflow.models.param
106 | Param()
note: This is an unsafe fix and may change runtime behavior
AIR311 [*] `airflow.models.param.ParamsDict` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
--> AIR311_names.py:107:1
|
105 | Param()
106 | DagParam()
107 | ParamsDict()
| ^^^^^^^^^^
|
help: Use `ParamsDict` from `airflow.sdk.definitions.param` instead.
99 | ParamsDict()
100 |
101 |
- from airflow.models.param import DagParam, Param, ParamsDict
102 + from airflow.models.param import DagParam, Param
103 + from airflow.sdk.definitions.param import ParamsDict
104 |
105 | # airflow.models.param
106 | Param()
note: This is an unsafe fix and may change runtime behavior
AIR311 [*] `airflow.sensors.base.BaseSensorOperator` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
--> AIR311_names.py:117:1
--> AIR311_names.py:107:1
|
116 | # airflow.sensors.base
117 | BaseSensorOperator()
106 | # airflow.sensors.base
107 | BaseSensorOperator()
| ^^^^^^^^^^^^^^^^^^
118 | PokeReturnValue()
119 | poke_mode_only()
108 | PokeReturnValue()
109 | poke_mode_only()
|
help: Use `BaseSensorOperator` from `airflow.sdk` instead.
108 |
109 |
110 | from airflow.sensors.base import (
98 |
99 |
100 | from airflow.sensors.base import (
- BaseSensorOperator,
111 | PokeReturnValue,
112 | poke_mode_only,
113 | )
114 + from airflow.sdk import BaseSensorOperator
115 |
116 | # airflow.sensors.base
117 | BaseSensorOperator()
101 | PokeReturnValue,
102 | poke_mode_only,
103 | )
104 + from airflow.sdk import BaseSensorOperator
105 |
106 | # airflow.sensors.base
107 | BaseSensorOperator()
note: This is an unsafe fix and may change runtime behavior
AIR311 [*] `airflow.sensors.base.PokeReturnValue` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
--> AIR311_names.py:118:1
--> AIR311_names.py:108:1
|
116 | # airflow.sensors.base
117 | BaseSensorOperator()
118 | PokeReturnValue()
106 | # airflow.sensors.base
107 | BaseSensorOperator()
108 | PokeReturnValue()
| ^^^^^^^^^^^^^^^
119 | poke_mode_only()
109 | poke_mode_only()
|
help: Use `PokeReturnValue` from `airflow.sdk` instead.
109 |
110 | from airflow.sensors.base import (
111 | BaseSensorOperator,
99 |
100 | from airflow.sensors.base import (
101 | BaseSensorOperator,
- PokeReturnValue,
112 | poke_mode_only,
113 | )
114 + from airflow.sdk import PokeReturnValue
115 |
116 | # airflow.sensors.base
117 | BaseSensorOperator()
102 | poke_mode_only,
103 | )
104 + from airflow.sdk import PokeReturnValue
105 |
106 | # airflow.sensors.base
107 | BaseSensorOperator()
note: This is an unsafe fix and may change runtime behavior
AIR311 [*] `airflow.sensors.base.poke_mode_only` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
--> AIR311_names.py:119:1
--> AIR311_names.py:109:1
|
117 | BaseSensorOperator()
118 | PokeReturnValue()
119 | poke_mode_only()
107 | BaseSensorOperator()
108 | PokeReturnValue()
109 | poke_mode_only()
| ^^^^^^^^^^^^^^
|
help: Use `poke_mode_only` from `airflow.sdk` instead.
110 | from airflow.sensors.base import (
111 | BaseSensorOperator,
112 | PokeReturnValue,
100 | from airflow.sensors.base import (
101 | BaseSensorOperator,
102 | PokeReturnValue,
- poke_mode_only,
113 | )
114 + from airflow.sdk import poke_mode_only
115 |
116 | # airflow.sensors.base
117 | BaseSensorOperator()
103 | )
104 + from airflow.sdk import poke_mode_only
105 |
106 | # airflow.sensors.base
107 | BaseSensorOperator()
note: This is an unsafe fix and may change runtime behavior

View File

@@ -30,7 +30,6 @@ use crate::rules::eradicate::detection::comment_contains_code;
///
/// [#4845]: https://github.com/astral-sh/ruff/issues/4845
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.145")]
pub(crate) struct CommentedOutCode;
impl Violation for CommentedOutCode {

View File

@@ -79,7 +79,6 @@ use ruff_python_ast::PythonVersion;
/// [typing-annotated]: https://docs.python.org/3/library/typing.html#typing.Annotated
/// [typing-extensions]: https://typing-extensions.readthedocs.io/en/stable/
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.8.0")]
pub(crate) struct FastApiNonAnnotatedDependency {
py_version: PythonVersion,
}

View File

@@ -59,7 +59,6 @@ use crate::{AlwaysFixableViolation, Fix};
/// return item
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.8.0")]
pub(crate) struct FastApiRedundantResponseModel;
impl AlwaysFixableViolation for FastApiRedundantResponseModel {

View File

@@ -64,7 +64,6 @@ use crate::{FixAvailability, Violation};
/// This rule's fix is marked as unsafe, as modifying a function signature can
/// change the behavior of the code.
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.10.0")]
pub(crate) struct FastApiUnusedPathParameter {
arg_name: String,
function_name: String,

View File

@@ -41,7 +41,6 @@ use crate::rules::flake8_2020::helpers::is_sys;
/// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version)
/// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.113")]
pub(crate) struct SysVersionCmpStr3;
impl Violation for SysVersionCmpStr3 {
@@ -92,7 +91,6 @@ impl Violation for SysVersionCmpStr3 {
/// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version)
/// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.113")]
pub(crate) struct SysVersionInfo0Eq3 {
eq: bool,
}
@@ -139,7 +137,6 @@ impl Violation for SysVersionInfo0Eq3 {
/// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version)
/// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.113")]
pub(crate) struct SysVersionInfo1CmpInt;
impl Violation for SysVersionInfo1CmpInt {
@@ -182,7 +179,6 @@ impl Violation for SysVersionInfo1CmpInt {
/// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version)
/// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.113")]
pub(crate) struct SysVersionInfoMinorCmpInt;
impl Violation for SysVersionInfoMinorCmpInt {
@@ -226,7 +222,6 @@ impl Violation for SysVersionInfoMinorCmpInt {
/// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version)
/// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.113")]
pub(crate) struct SysVersionCmpStr10;
impl Violation for SysVersionCmpStr10 {

View File

@@ -36,7 +36,6 @@ use crate::checkers::ast::Checker;
/// - [Six documentation: `six.PY2`](https://six.readthedocs.io/#six.PY2)
/// - [Six documentation: `six.PY3`](https://six.readthedocs.io/#six.PY3)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.113")]
pub(crate) struct SixPY3;
impl Violation for SixPY3 {

View File

@@ -38,7 +38,6 @@ use crate::rules::flake8_2020::helpers::is_sys;
/// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version)
/// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.113")]
pub(crate) struct SysVersionSlice3;
impl Violation for SysVersionSlice3 {
@@ -79,7 +78,6 @@ impl Violation for SysVersionSlice3 {
/// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version)
/// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.113")]
pub(crate) struct SysVersion2;
impl Violation for SysVersion2 {
@@ -120,7 +118,6 @@ impl Violation for SysVersion2 {
/// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version)
/// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.113")]
pub(crate) struct SysVersion0;
impl Violation for SysVersion0 {
@@ -161,7 +158,6 @@ impl Violation for SysVersion0 {
/// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version)
/// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.113")]
pub(crate) struct SysVersionSlice1;
impl Violation for SysVersionSlice1 {

View File

@@ -38,7 +38,6 @@ use crate::{Edit, Fix, FixAvailability, Violation};
/// ## Options
/// - `lint.flake8-annotations.suppress-dummy-args`
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.105")]
pub(crate) struct MissingTypeFunctionArgument {
name: String,
}
@@ -74,7 +73,6 @@ impl Violation for MissingTypeFunctionArgument {
/// ## Options
/// - `lint.flake8-annotations.suppress-dummy-args`
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.105")]
pub(crate) struct MissingTypeArgs {
name: String,
}
@@ -110,7 +108,6 @@ impl Violation for MissingTypeArgs {
/// ## Options
/// - `lint.flake8-annotations.suppress-dummy-args`
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.105")]
pub(crate) struct MissingTypeKwargs {
name: String,
}
@@ -152,7 +149,6 @@ impl Violation for MissingTypeKwargs {
/// ```
#[derive(ViolationMetadata)]
#[deprecated(note = "ANN101 has been removed")]
#[violation_metadata(removed_since = "0.8.0")]
pub(crate) struct MissingTypeSelf;
#[expect(deprecated)]
@@ -197,7 +193,6 @@ impl Violation for MissingTypeSelf {
/// ```
#[derive(ViolationMetadata)]
#[deprecated(note = "ANN102 has been removed")]
#[violation_metadata(removed_since = "0.8.0")]
pub(crate) struct MissingTypeCls;
#[expect(deprecated)]
@@ -241,7 +236,6 @@ impl Violation for MissingTypeCls {
///
/// - `lint.typing-extensions`
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.105")]
pub(crate) struct MissingReturnTypeUndocumentedPublicFunction {
name: String,
annotation: Option<String>,
@@ -295,7 +289,6 @@ impl Violation for MissingReturnTypeUndocumentedPublicFunction {
///
/// - `lint.typing-extensions`
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.105")]
pub(crate) struct MissingReturnTypePrivateFunction {
name: String,
annotation: Option<String>,
@@ -352,7 +345,6 @@ impl Violation for MissingReturnTypePrivateFunction {
/// self.x = x
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.105")]
pub(crate) struct MissingReturnTypeSpecialMethod {
name: String,
annotation: Option<String>,
@@ -400,7 +392,6 @@ impl Violation for MissingReturnTypeSpecialMethod {
/// return 1
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.105")]
pub(crate) struct MissingReturnTypeStaticMethod {
name: String,
annotation: Option<String>,
@@ -448,7 +439,6 @@ impl Violation for MissingReturnTypeStaticMethod {
/// return 1
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.105")]
pub(crate) struct MissingReturnTypeClassMethod {
name: String,
annotation: Option<String>,
@@ -518,7 +508,6 @@ impl Violation for MissingReturnTypeClassMethod {
/// - [Python documentation: `typing.Any`](https://docs.python.org/3/library/typing.html#typing.Any)
/// - [Mypy documentation: The Any type](https://mypy.readthedocs.io/en/stable/kinds_of_types.html#the-any-type)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.108")]
pub(crate) struct AnyType {
name: String,
}

View File

@@ -42,7 +42,6 @@ use crate::rules::flake8_async::helpers::AsyncModule;
/// - [`anyio` events](https://anyio.readthedocs.io/en/latest/api.html#anyio.Event)
/// - [`trio` events](https://trio.readthedocs.io/en/latest/reference-core.html#trio.Event)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.5.0")]
pub(crate) struct AsyncBusyWait {
module: AsyncModule,
}

View File

@@ -65,7 +65,6 @@ use ruff_python_ast::PythonVersion;
///
/// ["structured concurrency"]: https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/#timeouts-and-cancellation
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.5.0")]
pub(crate) struct AsyncFunctionWithTimeout {
module: AsyncModule,
}

View File

@@ -49,7 +49,6 @@ use crate::{AlwaysFixableViolation, Edit, Fix};
/// )
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.5.0")]
pub(crate) struct AsyncZeroSleep {
module: AsyncModule,
}

View File

@@ -38,7 +38,6 @@ use crate::checkers::ast::Checker;
/// ...
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.5.0")]
pub(crate) struct BlockingHttpCallInAsyncFunction;
impl Violation for BlockingHttpCallInAsyncFunction {

View File

@@ -37,7 +37,6 @@ use crate::checkers::ast::Checker;
/// response = await client.get(...)
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(preview_since = "0.12.11")]
pub(crate) struct BlockingHttpCallHttpxInAsyncFunction {
name: String,
call: String,

View File

@@ -33,7 +33,6 @@ use crate::checkers::ast::Checker;
/// username = await loop.run_in_executor(None, input, "Username:")
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(preview_since = "0.12.12")]
pub(crate) struct BlockingInputInAsyncFunction;
impl Violation for BlockingInputInAsyncFunction {

View File

@@ -34,7 +34,6 @@ use crate::checkers::ast::Checker;
/// contents = await f.read()
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.5.0")]
pub(crate) struct BlockingOpenCallInAsyncFunction;
impl Violation for BlockingOpenCallInAsyncFunction {

View File

@@ -47,7 +47,6 @@ use ruff_text_size::Ranged;
/// new_path = os.path.join("/tmp/src/", path)
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(preview_since = "0.13.2")]
pub(crate) struct BlockingPathMethodInAsyncFunction {
path_library: String,
}

View File

@@ -37,7 +37,6 @@ use crate::checkers::ast::Checker;
/// asyncio.create_subprocess_shell(cmd)
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.5.0")]
pub(crate) struct CreateSubprocessInAsyncFunction;
impl Violation for CreateSubprocessInAsyncFunction {
@@ -77,7 +76,6 @@ impl Violation for CreateSubprocessInAsyncFunction {
/// asyncio.create_subprocess_shell(cmd)
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.5.0")]
pub(crate) struct RunProcessInAsyncFunction;
impl Violation for RunProcessInAsyncFunction {
@@ -122,7 +120,6 @@ impl Violation for RunProcessInAsyncFunction {
/// await asyncio.loop.run_in_executor(None, wait_for_process)
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.5.0")]
pub(crate) struct WaitForProcessInAsyncFunction;
impl Violation for WaitForProcessInAsyncFunction {

View File

@@ -35,7 +35,6 @@ use crate::checkers::ast::Checker;
/// await asyncio.sleep(1)
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.5.0")]
pub(crate) struct BlockingSleepInAsyncFunction;
impl Violation for BlockingSleepInAsyncFunction {

View File

@@ -45,7 +45,6 @@ use crate::rules::flake8_async::helpers::MethodName;
/// - [`anyio` timeouts](https://anyio.readthedocs.io/en/stable/cancellation.html)
/// - [`trio` timeouts](https://trio.readthedocs.io/en/stable/reference-core.html#cancellation-and-timeouts)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.269")]
pub(crate) struct CancelScopeNoCheckpoint {
method_name: MethodName,
}

View File

@@ -39,7 +39,6 @@ use crate::{Edit, Fix, FixAvailability, Violation};
///
/// This fix is marked as unsafe as it changes program behavior.
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.13.0")]
pub(crate) struct LongSleepNotForever {
module: AsyncModule,
}

View File

@@ -38,7 +38,6 @@ use crate::{Edit, Fix, FixAvailability, Violation};
/// This rule's fix is marked as unsafe, as adding an `await` to a function
/// call changes its semantics and runtime behavior.
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.5.0")]
pub(crate) struct TrioSyncCall {
method_name: MethodName,
}

View File

@@ -33,7 +33,6 @@ use crate::checkers::ast::Checker;
/// raise ValueError("Expected positive value.")
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.116")]
pub(crate) struct Assert;
impl Violation for Assert {

View File

@@ -35,7 +35,6 @@ use crate::checkers::ast::Checker;
/// - [Python documentation: `stat`](https://docs.python.org/3/library/stat.html)
/// - [Common Weakness Enumeration: CWE-732](https://cwe.mitre.org/data/definitions/732.html)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.211")]
pub(crate) struct BadFilePermissions {
reason: Reason,
}

View File

@@ -34,7 +34,6 @@ use crate::checkers::ast::Checker;
/// - [Django documentation: SQL injection protection](https://docs.djangoproject.com/en/dev/topics/security/#sql-injection-protection)
/// - [Common Weakness Enumeration: CWE-89](https://cwe.mitre.org/data/definitions/89.html)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "0.5.0")]
pub(crate) struct DjangoExtra;
impl Violation for DjangoExtra {

View File

@@ -25,7 +25,6 @@ use crate::checkers::ast::Checker;
/// - [Django documentation: SQL injection protection](https://docs.djangoproject.com/en/dev/topics/security/#sql-injection-protection)
/// - [Common Weakness Enumeration: CWE-89](https://cwe.mitre.org/data/definitions/89.html)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.2.0")]
pub(crate) struct DjangoRawSql;
impl Violation for DjangoRawSql {

View File

@@ -22,7 +22,6 @@ use crate::checkers::ast::Checker;
/// - [Python documentation: `exec`](https://docs.python.org/3/library/functions.html#exec)
/// - [Common Weakness Enumeration: CWE-78](https://cwe.mitre.org/data/definitions/78.html)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.116")]
pub(crate) struct ExecBuiltin;
impl Violation for ExecBuiltin {

View File

@@ -39,7 +39,6 @@ use crate::checkers::ast::Checker;
/// ## References
/// - [Flask documentation: Debug Mode](https://flask.palletsprojects.com/en/latest/quickstart/#debug-mode)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.2.0")]
pub(crate) struct FlaskDebugTrue;
impl Violation for FlaskDebugTrue {

Some files were not shown because too many files have changed in this diff Show More