diff --git a/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above-2.snap b/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above-2.snap index f35c93029c..da3eb66afc 100644 --- a/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above-2.snap +++ b/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above-2.snap @@ -9,7 +9,6 @@ info: - concise - "--show-settings" - test.py -snapshot_kind: text --- success: true exit_code: 0 @@ -265,7 +264,7 @@ linter.ruff.parenthesize_tuple_in_subscript = false # Formatter Settings formatter.exclude = [] -formatter.unresolved_target_version = 3.9 +formatter.unresolved_target_version = 3.10 formatter.per_file_target_version = {} formatter.preview = disabled formatter.line_width = 88 @@ -280,7 +279,7 @@ formatter.docstring_code_line_width = dynamic # Analyze Settings analyze.exclude = [] analyze.preview = disabled -analyze.target_version = 3.9 +analyze.target_version = 3.10 analyze.string_imports = disabled analyze.extension = ExtensionMapping({}) analyze.include_dependencies = {} diff --git a/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above.snap b/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above.snap index 980eaf47ad..e9ca7bd400 100644 --- a/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above.snap +++ b/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above.snap @@ -9,7 +9,6 @@ info: - concise - "--show-settings" - foo/test.py -snapshot_kind: text --- success: true exit_code: 0 @@ -265,7 +264,7 @@ linter.ruff.parenthesize_tuple_in_subscript = false # Formatter Settings formatter.exclude = [] -formatter.unresolved_target_version = 3.9 +formatter.unresolved_target_version = 3.10 formatter.per_file_target_version = {} formatter.preview = disabled formatter.line_width = 88 @@ -280,7 +279,7 @@ formatter.docstring_code_line_width = dynamic # Analyze Settings analyze.exclude = [] analyze.preview = disabled -analyze.target_version = 3.9 +analyze.target_version = 3.10 analyze.string_imports = disabled analyze.extension = ExtensionMapping({}) analyze.include_dependencies = {} diff --git a/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_18.py b/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_18.py index 8576a1d403..eaae5e5ee3 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_18.py +++ b/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_18.py @@ -14,6 +14,6 @@ class Bar: ] -# OK: Allow named expressions in annotations. +# This is no longer allowed on Python 3.14+ x: (y := 1) print(y) diff --git a/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_26.py b/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_26.py index f87819ef80..3eaa0d8a4e 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_26.py +++ b/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_26.py @@ -13,16 +13,16 @@ CStr2: TypeAlias = Union["C", str] # always okay # References to a class from inside the class: class C: - other: C = ... # valid in a `.pyi` stub file, not in a `.py` runtime file + other: C = ... # valid in a `.pyi` stub file, and in a `.py` runtime file with deferred annotations other2: "C" = ... # always okay - def from_str(self, s: str) -> C: ... # valid in a `.pyi` stub file, not in a `.py` runtime file + def from_str(self, s: str) -> C: ... # valid in a `.pyi` stub file, and in a `.py` runtime file with deferred annotations def from_str2(self, s: str) -> "C": ... # always okay # Circular references: class A: - foo: B # valid in a `.pyi` stub file, not in a `.py` runtime file + foo: B # valid in a `.pyi` stub file, and in a `.py` runtime file with deferred annotations foo2: "B" # always okay - bar: dict[str, B] # valid in a `.pyi` stub file, not in a `.py` runtime file + bar: dict[str, B] # valid in a `.pyi` stub file, and in a `.py` runtime file with deferred annotations bar2: dict[str, "A"] # always okay class B: diff --git a/crates/ruff_linter/src/linter.rs b/crates/ruff_linter/src/linter.rs index 339a4f4aa5..56e180bfab 100644 --- a/crates/ruff_linter/src/linter.rs +++ b/crates/ruff_linter/src/linter.rs @@ -27,14 +27,13 @@ use crate::fix::{FixResult, fix_file}; use crate::message::create_syntax_error_diagnostic; use crate::noqa::add_noqa; use crate::package::PackageRoot; -use crate::preview::is_py314_support_enabled; use crate::registry::Rule; #[cfg(any(feature = "test-rules", test))] use crate::rules::ruff::rules::test_rules::{self, TEST_RULES, TestRule}; use crate::settings::types::UnsafeFixes; use crate::settings::{LinterSettings, TargetVersion, flags}; use crate::source_kind::SourceKind; -use crate::{Locator, directives, fs, warn_user_once}; +use crate::{Locator, directives, fs}; pub(crate) mod float; @@ -442,14 +441,6 @@ pub fn lint_only( ) -> LinterResult { let target_version = settings.resolve_target_version(path); - if matches!(target_version, TargetVersion(Some(PythonVersion::PY314))) - && !is_py314_support_enabled(settings) - { - warn_user_once!( - "Support for Python 3.14 is in preview and may undergo breaking changes. Enable `preview` to remove this warning." - ); - } - let parsed = source.into_parsed(source_kind, source_type, target_version.parser_version()); // Map row and column locations to byte slices (lazily). @@ -551,14 +542,6 @@ pub fn lint_fix<'a>( let target_version = settings.resolve_target_version(path); - if matches!(target_version, TargetVersion(Some(PythonVersion::PY314))) - && !is_py314_support_enabled(settings) - { - warn_user_once!( - "Support for Python 3.14 is in preview and may undergo breaking changes. Enable `preview` to remove this warning." - ); - } - // Continuously fix until the source code stabilizes. loop { // Parse once. diff --git a/crates/ruff_linter/src/preview.rs b/crates/ruff_linter/src/preview.rs index a5647d5ea0..37a577e8df 100644 --- a/crates/ruff_linter/src/preview.rs +++ b/crates/ruff_linter/src/preview.rs @@ -7,10 +7,6 @@ use crate::settings::LinterSettings; -pub(crate) const fn is_py314_support_enabled(settings: &LinterSettings) -> bool { - settings.preview.is_enabled() -} - // Rule-specific behavior // https://github.com/astral-sh/ruff/pull/15541 diff --git a/crates/ruff_linter/src/rules/fastapi/mod.rs b/crates/ruff_linter/src/rules/fastapi/mod.rs index f6e48dd4ad..17b3cb663c 100644 --- a/crates/ruff_linter/src/rules/fastapi/mod.rs +++ b/crates/ruff_linter/src/rules/fastapi/mod.rs @@ -8,9 +8,12 @@ mod tests { use anyhow::Result; use test_case::test_case; + use ruff_python_ast::PythonVersion; + use crate::registry::Rule; + use crate::settings::LinterSettings; use crate::test::test_path; - use crate::{assert_diagnostics, settings}; + use crate::{assert_diagnostics, assert_diagnostics_diff}; #[test_case(Rule::FastApiRedundantResponseModel, Path::new("FAST001.py"))] #[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_0.py"))] @@ -20,12 +23,35 @@ mod tests { let snapshot = format!("{}_{}", rule_code.name(), path.to_string_lossy()); let diagnostics = test_path( Path::new("fastapi").join(path).as_path(), - &settings::LinterSettings::for_rule(rule_code), + &LinterSettings::for_rule(rule_code), )?; assert_diagnostics!(snapshot, diagnostics); Ok(()) } + #[test_case(Rule::FastApiRedundantResponseModel, Path::new("FAST001.py"))] + #[test_case(Rule::FastApiUnusedPathParameter, Path::new("FAST003.py"))] + fn deferred_annotations_diff(rule_code: Rule, path: &Path) -> Result<()> { + let snapshot = format!( + "deferred_annotations_diff_{}_{}", + rule_code.name(), + path.to_string_lossy() + ); + assert_diagnostics_diff!( + snapshot, + Path::new("fastapi").join(path).as_path(), + &LinterSettings { + unresolved_target_version: PythonVersion::PY313.into(), + ..LinterSettings::for_rule(rule_code) + }, + &LinterSettings { + unresolved_target_version: PythonVersion::PY314.into(), + ..LinterSettings::for_rule(rule_code) + }, + ); + Ok(()) + } + // FAST002 autofixes use `typing_extensions` on Python 3.8, // since `typing.Annotated` was added in Python 3.9 #[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_0.py"))] @@ -34,9 +60,9 @@ mod tests { let snapshot = format!("{}_{}_py38", rule_code.name(), path.to_string_lossy()); let diagnostics = test_path( Path::new("fastapi").join(path).as_path(), - &settings::LinterSettings { - unresolved_target_version: ruff_python_ast::PythonVersion::PY38.into(), - ..settings::LinterSettings::for_rule(rule_code) + &LinterSettings { + unresolved_target_version: PythonVersion::PY38.into(), + ..LinterSettings::for_rule(rule_code) }, )?; assert_diagnostics!(snapshot, diagnostics); diff --git a/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-redundant-response-model_FAST001.py.snap b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-redundant-response-model_FAST001.py.snap new file mode 100644 index 0000000000..fc36a819d1 --- /dev/null +++ b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-redundant-response-model_FAST001.py.snap @@ -0,0 +1,213 @@ +--- +source: crates/ruff_linter/src/rules/fastapi/mod.rs +--- +--- Linter settings --- +-linter.unresolved_target_version = 3.13 ++linter.unresolved_target_version = 3.14 + +--- Summary --- +Removed: 10 +Added: 0 + +--- Removed --- +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:17:22 + | +17 | @app.post("/items/", response_model=Item) + | ^^^^^^^^^^^^^^^^^^^ +18 | async def create_item(item: Item) -> Item: +19 | return item + | +help: Remove argument +14 | # Errors +15 | +16 | + - @app.post("/items/", response_model=Item) +17 + @app.post("/items/") +18 | async def create_item(item: Item) -> Item: +19 | return item +20 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:22:22 + | +22 | @app.post("/items/", response_model=list[Item]) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +23 | async def create_item(item: Item) -> list[Item]: +24 | return item + | +help: Remove argument +19 | return item +20 | +21 | + - @app.post("/items/", response_model=list[Item]) +22 + @app.post("/items/") +23 | async def create_item(item: Item) -> list[Item]: +24 | return item +25 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:27:22 + | +27 | @app.post("/items/", response_model=List[Item]) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +28 | async def create_item(item: Item) -> List[Item]: +29 | return item + | +help: Remove argument +24 | return item +25 | +26 | + - @app.post("/items/", response_model=List[Item]) +27 + @app.post("/items/") +28 | async def create_item(item: Item) -> List[Item]: +29 | return item +30 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:32:22 + | +32 | @app.post("/items/", response_model=Dict[str, Item]) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +33 | async def create_item(item: Item) -> Dict[str, Item]: +34 | return item + | +help: Remove argument +29 | return item +30 | +31 | + - @app.post("/items/", response_model=Dict[str, Item]) +32 + @app.post("/items/") +33 | async def create_item(item: Item) -> Dict[str, Item]: +34 | return item +35 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:37:22 + | +37 | @app.post("/items/", response_model=str) + | ^^^^^^^^^^^^^^^^^^ +38 | async def create_item(item: Item) -> str: +39 | return item + | +help: Remove argument +34 | return item +35 | +36 | + - @app.post("/items/", response_model=str) +37 + @app.post("/items/") +38 | async def create_item(item: Item) -> str: +39 | return item +40 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:42:21 + | +42 | @app.get("/items/", response_model=Item) + | ^^^^^^^^^^^^^^^^^^^ +43 | async def create_item(item: Item) -> Item: +44 | return item + | +help: Remove argument +39 | return item +40 | +41 | + - @app.get("/items/", response_model=Item) +42 + @app.get("/items/") +43 | async def create_item(item: Item) -> Item: +44 | return item +45 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:47:21 + | +47 | @app.get("/items/", response_model=Item) + | ^^^^^^^^^^^^^^^^^^^ +48 | @app.post("/items/", response_model=Item) +49 | async def create_item(item: Item) -> Item: + | +help: Remove argument +44 | return item +45 | +46 | + - @app.get("/items/", response_model=Item) +47 + @app.get("/items/") +48 | @app.post("/items/", response_model=Item) +49 | async def create_item(item: Item) -> Item: +50 | return item +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:48:22 + | +47 | @app.get("/items/", response_model=Item) +48 | @app.post("/items/", response_model=Item) + | ^^^^^^^^^^^^^^^^^^^ +49 | async def create_item(item: Item) -> Item: +50 | return item + | +help: Remove argument +45 | +46 | +47 | @app.get("/items/", response_model=Item) + - @app.post("/items/", response_model=Item) +48 + @app.post("/items/") +49 | async def create_item(item: Item) -> Item: +50 | return item +51 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:53:24 + | +53 | @router.get("/items/", response_model=Item) + | ^^^^^^^^^^^^^^^^^^^ +54 | async def create_item(item: Item) -> Item: +55 | return item + | +help: Remove argument +50 | return item +51 | +52 | + - @router.get("/items/", response_model=Item) +53 + @router.get("/items/") +54 | async def create_item(item: Item) -> Item: +55 | return item +56 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:118:23 + | +116 | def setup_app(app_arg: FastAPI, non_app: str) -> None: +117 | # Error +118 | @app_arg.get("/", response_model=str) + | ^^^^^^^^^^^^^^^^^^ +119 | async def get_root() -> str: +120 | return "Hello World!" + | +help: Remove argument +115 | +116 | def setup_app(app_arg: FastAPI, non_app: str) -> None: +117 | # Error + - @app_arg.get("/", response_model=str) +118 + @app_arg.get("/") +119 | async def get_root() -> str: +120 | return "Hello World!" +121 | +note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-unused-path-parameter_FAST003.py.snap b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-unused-path-parameter_FAST003.py.snap new file mode 100644 index 0000000000..2feae227db --- /dev/null +++ b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-unused-path-parameter_FAST003.py.snap @@ -0,0 +1,74 @@ +--- +source: crates/ruff_linter/src/rules/fastapi/mod.rs +--- +--- Linter settings --- +-linter.unresolved_target_version = 3.13 ++linter.unresolved_target_version = 3.14 + +--- Summary --- +Removed: 3 +Added: 0 + +--- Removed --- +FAST003 [*] Parameter `thing_id` appears in route path, but not in `single` signature + --> FAST003.py:158:19 + | +157 | ### Errors +158 | @app.get("/things/{thing_id}") + | ^^^^^^^^^^ +159 | async def single(other: Annotated[str, Depends(something_else)]): ... +160 | @app.get("/things/{thing_id}") + | +help: Add `thing_id` to function signature +156 | +157 | ### Errors +158 | @app.get("/things/{thing_id}") + - async def single(other: Annotated[str, Depends(something_else)]): ... +159 + async def single(other: Annotated[str, Depends(something_else)], thing_id): ... +160 | @app.get("/things/{thing_id}") +161 | async def default(other: str = Depends(something_else)): ... +162 | +note: This is an unsafe fix and may change runtime behavior + + +FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_full` signature + --> FAST003.py:197:12 + | +196 | # Errors +197 | @app.get("/{id}") + | ^^^^ +198 | async def get_id_pydantic_full( +199 | params: Annotated[PydanticParams, Depends(PydanticParams)], + | +help: Add `id` to function signature +196 | # Errors +197 | @app.get("/{id}") +198 | async def get_id_pydantic_full( + - params: Annotated[PydanticParams, Depends(PydanticParams)], +199 + params: Annotated[PydanticParams, Depends(PydanticParams)], id, +200 | ): ... +201 | @app.get("/{id}") +202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ... +note: This is an unsafe fix and may change runtime behavior + + +FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_short` signature + --> FAST003.py:201:12 + | +199 | params: Annotated[PydanticParams, Depends(PydanticParams)], +200 | ): ... +201 | @app.get("/{id}") + | ^^^^ +202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ... +203 | @app.get("/{id}") + | +help: Add `id` to function signature +199 | params: Annotated[PydanticParams, Depends(PydanticParams)], +200 | ): ... +201 | @app.get("/{id}") + - async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ... +202 + async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()], id): ... +203 | @app.get("/{id}") +204 | async def get_id_init_not_annotated(params = Depends(InitParams)): ... +205 | +note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-redundant-response-model_FAST001.py.snap b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-redundant-response-model_FAST001.py.snap index f1c5682ef4..e3843a16cd 100644 --- a/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-redundant-response-model_FAST001.py.snap +++ b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-redundant-response-model_FAST001.py.snap @@ -1,195 +1,4 @@ --- source: crates/ruff_linter/src/rules/fastapi/mod.rs --- -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:17:22 - | -17 | @app.post("/items/", response_model=Item) - | ^^^^^^^^^^^^^^^^^^^ -18 | async def create_item(item: Item) -> Item: -19 | return item - | -help: Remove argument -14 | # Errors -15 | -16 | - - @app.post("/items/", response_model=Item) -17 + @app.post("/items/") -18 | async def create_item(item: Item) -> Item: -19 | return item -20 | -note: This is an unsafe fix and may change runtime behavior -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:22:22 - | -22 | @app.post("/items/", response_model=list[Item]) - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -23 | async def create_item(item: Item) -> list[Item]: -24 | return item - | -help: Remove argument -19 | return item -20 | -21 | - - @app.post("/items/", response_model=list[Item]) -22 + @app.post("/items/") -23 | async def create_item(item: Item) -> list[Item]: -24 | return item -25 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:27:22 - | -27 | @app.post("/items/", response_model=List[Item]) - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -28 | async def create_item(item: Item) -> List[Item]: -29 | return item - | -help: Remove argument -24 | return item -25 | -26 | - - @app.post("/items/", response_model=List[Item]) -27 + @app.post("/items/") -28 | async def create_item(item: Item) -> List[Item]: -29 | return item -30 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:32:22 - | -32 | @app.post("/items/", response_model=Dict[str, Item]) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -33 | async def create_item(item: Item) -> Dict[str, Item]: -34 | return item - | -help: Remove argument -29 | return item -30 | -31 | - - @app.post("/items/", response_model=Dict[str, Item]) -32 + @app.post("/items/") -33 | async def create_item(item: Item) -> Dict[str, Item]: -34 | return item -35 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:37:22 - | -37 | @app.post("/items/", response_model=str) - | ^^^^^^^^^^^^^^^^^^ -38 | async def create_item(item: Item) -> str: -39 | return item - | -help: Remove argument -34 | return item -35 | -36 | - - @app.post("/items/", response_model=str) -37 + @app.post("/items/") -38 | async def create_item(item: Item) -> str: -39 | return item -40 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:42:21 - | -42 | @app.get("/items/", response_model=Item) - | ^^^^^^^^^^^^^^^^^^^ -43 | async def create_item(item: Item) -> Item: -44 | return item - | -help: Remove argument -39 | return item -40 | -41 | - - @app.get("/items/", response_model=Item) -42 + @app.get("/items/") -43 | async def create_item(item: Item) -> Item: -44 | return item -45 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:47:21 - | -47 | @app.get("/items/", response_model=Item) - | ^^^^^^^^^^^^^^^^^^^ -48 | @app.post("/items/", response_model=Item) -49 | async def create_item(item: Item) -> Item: - | -help: Remove argument -44 | return item -45 | -46 | - - @app.get("/items/", response_model=Item) -47 + @app.get("/items/") -48 | @app.post("/items/", response_model=Item) -49 | async def create_item(item: Item) -> Item: -50 | return item -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:48:22 - | -47 | @app.get("/items/", response_model=Item) -48 | @app.post("/items/", response_model=Item) - | ^^^^^^^^^^^^^^^^^^^ -49 | async def create_item(item: Item) -> Item: -50 | return item - | -help: Remove argument -45 | -46 | -47 | @app.get("/items/", response_model=Item) - - @app.post("/items/", response_model=Item) -48 + @app.post("/items/") -49 | async def create_item(item: Item) -> Item: -50 | return item -51 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:53:24 - | -53 | @router.get("/items/", response_model=Item) - | ^^^^^^^^^^^^^^^^^^^ -54 | async def create_item(item: Item) -> Item: -55 | return item - | -help: Remove argument -50 | return item -51 | -52 | - - @router.get("/items/", response_model=Item) -53 + @router.get("/items/") -54 | async def create_item(item: Item) -> Item: -55 | return item -56 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:118:23 - | -116 | def setup_app(app_arg: FastAPI, non_app: str) -> None: -117 | # Error -118 | @app_arg.get("/", response_model=str) - | ^^^^^^^^^^^^^^^^^^ -119 | async def get_root() -> str: -120 | return "Hello World!" - | -help: Remove argument -115 | -116 | def setup_app(app_arg: FastAPI, non_app: str) -> None: -117 | # Error - - @app_arg.get("/", response_model=str) -118 + @app_arg.get("/") -119 | async def get_root() -> str: -120 | return "Hello World!" -121 | -note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-unused-path-parameter_FAST003.py.snap b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-unused-path-parameter_FAST003.py.snap index a3ea2bf7ce..fb6bde76f8 100644 --- a/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-unused-path-parameter_FAST003.py.snap +++ b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-unused-path-parameter_FAST003.py.snap @@ -324,26 +324,6 @@ help: Add `name` to function signature 91 | note: This is an unsafe fix and may change runtime behavior -FAST003 [*] Parameter `thing_id` appears in route path, but not in `single` signature - --> FAST003.py:158:19 - | -157 | ### Errors -158 | @app.get("/things/{thing_id}") - | ^^^^^^^^^^ -159 | async def single(other: Annotated[str, Depends(something_else)]): ... -160 | @app.get("/things/{thing_id}") - | -help: Add `thing_id` to function signature -156 | -157 | ### Errors -158 | @app.get("/things/{thing_id}") - - async def single(other: Annotated[str, Depends(something_else)]): ... -159 + async def single(other: Annotated[str, Depends(something_else)], thing_id): ... -160 | @app.get("/things/{thing_id}") -161 | async def default(other: str = Depends(something_else)): ... -162 | -note: This is an unsafe fix and may change runtime behavior - FAST003 [*] Parameter `thing_id` appears in route path, but not in `default` signature --> FAST003.py:160:19 | @@ -364,47 +344,6 @@ help: Add `thing_id` to function signature 164 | ### No errors note: This is an unsafe fix and may change runtime behavior -FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_full` signature - --> FAST003.py:197:12 - | -196 | # Errors -197 | @app.get("/{id}") - | ^^^^ -198 | async def get_id_pydantic_full( -199 | params: Annotated[PydanticParams, Depends(PydanticParams)], - | -help: Add `id` to function signature -196 | # Errors -197 | @app.get("/{id}") -198 | async def get_id_pydantic_full( - - params: Annotated[PydanticParams, Depends(PydanticParams)], -199 + params: Annotated[PydanticParams, Depends(PydanticParams)], id, -200 | ): ... -201 | @app.get("/{id}") -202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ... -note: This is an unsafe fix and may change runtime behavior - -FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_short` signature - --> FAST003.py:201:12 - | -199 | params: Annotated[PydanticParams, Depends(PydanticParams)], -200 | ): ... -201 | @app.get("/{id}") - | ^^^^ -202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ... -203 | @app.get("/{id}") - | -help: Add `id` to function signature -199 | params: Annotated[PydanticParams, Depends(PydanticParams)], -200 | ): ... -201 | @app.get("/{id}") - - async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ... -202 + async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()], id): ... -203 | @app.get("/{id}") -204 | async def get_id_init_not_annotated(params = Depends(InitParams)): ... -205 | -note: This is an unsafe fix and may change runtime behavior - FAST003 [*] Parameter `id` appears in route path, but not in `get_id_init_not_annotated` signature --> FAST003.py:203:12 | diff --git a/crates/ruff_linter/src/rules/flake8_builtins/mod.rs b/crates/ruff_linter/src/rules/flake8_builtins/mod.rs index 315ab704e7..0ab2764765 100644 --- a/crates/ruff_linter/src/rules/flake8_builtins/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_builtins/mod.rs @@ -10,12 +10,12 @@ mod tests { use anyhow::Result; use test_case::test_case; - use crate::assert_diagnostics; use crate::registry::Rule; use crate::rules::flake8_builtins; use crate::settings::LinterSettings; use crate::settings::types::PreviewMode; use crate::test::{test_path, test_resource_path}; + use crate::{assert_diagnostics, assert_diagnostics_diff}; use ruff_python_ast::PythonVersion; #[test_case(Rule::BuiltinVariableShadowing, Path::new("A001.py"))] @@ -64,6 +64,28 @@ mod tests { Ok(()) } + #[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))] + fn deferred_annotations_diff(rule_code: Rule, path: &Path) -> Result<()> { + let snapshot = format!( + "deferred_annotations_diff_{}_{}", + rule_code.name(), + path.to_string_lossy() + ); + assert_diagnostics_diff!( + snapshot, + Path::new("flake8_builtins").join(path).as_path(), + &LinterSettings { + unresolved_target_version: PythonVersion::PY313.into(), + ..LinterSettings::for_rule(rule_code) + }, + &LinterSettings { + unresolved_target_version: PythonVersion::PY314.into(), + ..LinterSettings::for_rule(rule_code) + }, + ); + Ok(()) + } + #[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( diff --git a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py.snap b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py.snap index 1444cc7d6a..df35fcb66a 100644 --- a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py.snap +++ b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py.snap @@ -1,22 +1,4 @@ --- source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs --- -A003 Python builtin is shadowed by method `str` from line 14 - --> A003.py:17:31 - | -15 | pass -16 | -17 | def method_usage(self) -> str: - | ^^^ -18 | pass - | -A003 Python builtin is shadowed by class attribute `id` from line 3 - --> A003.py:20:34 - | -18 | pass -19 | -20 | def attribute_usage(self) -> id: - | ^^ -21 | pass - | diff --git a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py_builtins_ignorelist.snap b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py_builtins_ignorelist.snap index ad43e1b0fc..df35fcb66a 100644 --- a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py_builtins_ignorelist.snap +++ b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py_builtins_ignorelist.snap @@ -1,12 +1,4 @@ --- source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs --- -A003 Python builtin is shadowed by method `str` from line 14 - --> A003.py:17:31 - | -15 | pass -16 | -17 | def method_usage(self) -> str: - | ^^^ -18 | pass - | + diff --git a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__deferred_annotations_diff_builtin-attribute-shadowing_A003.py.snap b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__deferred_annotations_diff_builtin-attribute-shadowing_A003.py.snap new file mode 100644 index 0000000000..ed283ed65a --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__deferred_annotations_diff_builtin-attribute-shadowing_A003.py.snap @@ -0,0 +1,32 @@ +--- +source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs +--- +--- Linter settings --- +-linter.unresolved_target_version = 3.13 ++linter.unresolved_target_version = 3.14 + +--- Summary --- +Removed: 2 +Added: 0 + +--- Removed --- +A003 Python builtin is shadowed by method `str` from line 14 + --> A003.py:17:31 + | +15 | pass +16 | +17 | def method_usage(self) -> str: + | ^^^ +18 | pass + | + + +A003 Python builtin is shadowed by class attribute `id` from line 3 + --> A003.py:20:34 + | +18 | pass +19 | +20 | def attribute_usage(self) -> id: + | ^^ +21 | pass + | diff --git a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__preview__A003_A003.py.snap b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__preview__A003_A003.py.snap index 6fd8c6f961..537a64433c 100644 --- a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__preview__A003_A003.py.snap +++ b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__preview__A003_A003.py.snap @@ -1,26 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs --- -A003 Python builtin is shadowed by method `str` from line 14 - --> A003.py:17:31 - | -15 | pass -16 | -17 | def method_usage(self) -> str: - | ^^^ -18 | pass - | - -A003 Python builtin is shadowed by class attribute `id` from line 3 - --> A003.py:20:34 - | -18 | pass -19 | -20 | def attribute_usage(self) -> id: - | ^^ -21 | pass - | - A003 Python builtin is shadowed by method `property` from line 26 --> A003.py:31:7 | diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.py.snap index 15a50a1e1b..8192743812 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.py.snap @@ -1,6 +1,196 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`. + --> PYI055.py:4:4 + | +2 | from typing import Union +3 | +4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +5 | t: type[int] | type[str] | type[float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] + | +help: Combine multiple `type` members +1 | import builtins +2 | from typing import Union +3 | + - s: builtins.type[int] | builtins.type[str] | builtins.type[complex] +4 + s: type[int | str | complex] +5 | t: type[int] | type[str] | type[float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] +7 | v: Union[type[float], type[complex]] + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | float]`. + --> PYI055.py:5:4 + | +4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex] +5 | t: type[int] | type[str] | type[float] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6 | u: builtins.type[int] | type[str] | builtins.type[complex] +7 | v: Union[type[float], type[complex]] + | +help: Combine multiple `type` members +2 | from typing import Union +3 | +4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex] + - t: type[int] | type[str] | type[float] +5 + t: type[int | str | float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] +7 | v: Union[type[float], type[complex]] +8 | w: Union[type[float | int], type[complex]] + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`. + --> PYI055.py:6:4 + | +4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex] +5 | t: type[int] | type[str] | type[float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +7 | v: Union[type[float], type[complex]] +8 | w: Union[type[float | int], type[complex]] + | +help: Combine multiple `type` members +3 | +4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex] +5 | t: type[int] | type[str] | type[float] + - u: builtins.type[int] | type[str] | builtins.type[complex] +6 + u: type[int | str | complex] +7 | v: Union[type[float], type[complex]] +8 | w: Union[type[float | int], type[complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, complex]]`. + --> PYI055.py:7:4 + | +5 | t: type[int] | type[str] | type[float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] +7 | v: Union[type[float], type[complex]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +8 | w: Union[type[float | int], type[complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] + | +help: Combine multiple `type` members +4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex] +5 | t: type[int] | type[str] | type[float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] + - v: Union[type[float], type[complex]] +7 + v: type[Union[float, complex]] +8 | w: Union[type[float | int], type[complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float | int, complex]]`. + --> PYI055.py:8:4 + | + 6 | u: builtins.type[int] | type[str] | builtins.type[complex] + 7 | v: Union[type[float], type[complex]] + 8 | w: Union[type[float | int], type[complex]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] + | +help: Combine multiple `type` members +5 | t: type[int] | type[str] | type[float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] +7 | v: Union[type[float], type[complex]] + - w: Union[type[float | int], type[complex]] +8 + w: type[Union[float | int, complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[Union[float, int], complex]]`. + --> PYI055.py:9:4 + | + 7 | v: Union[type[float], type[complex]] + 8 | w: Union[type[float | int], type[complex]] + 9 | x: Union[Union[type[Union[float, int]], type[complex]]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +10 | y: Union[Union[Union[type[float | int], type[complex]]]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] + | +help: Combine multiple `type` members +6 | u: builtins.type[int] | type[str] | builtins.type[complex] +7 | v: Union[type[float], type[complex]] +8 | w: Union[type[float | int], type[complex]] + - x: Union[Union[type[Union[float, int]], type[complex]]] +9 + x: type[Union[Union[float, int], complex]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] +12 | + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float | int, complex]]`. + --> PYI055.py:10:4 + | + 8 | w: Union[type[float | int], type[complex]] + 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] + | +help: Combine multiple `type` members +7 | v: Union[type[float], type[complex]] +8 | w: Union[type[float | int], type[complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] + - y: Union[Union[Union[type[float | int], type[complex]]]] +10 + y: type[Union[float | int, complex]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] +12 | +13 | + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[complex, Union[float, int]]]`. + --> PYI055.py:11:4 + | + 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: Combine multiple `type` members +8 | w: Union[type[float | int], type[complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] + - z: Union[type[complex], Union[Union[type[Union[float, int]]]]] +11 + z: type[Union[complex, Union[float, int]]] +12 | +13 | +14 | def func(arg: type[int] | str | type[float]) -> None: + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | float]`. + --> PYI055.py:14:15 + | +14 | def func(arg: type[int] | str | type[float]) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +15 | ... + | +help: Combine multiple `type` members +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] +12 | +13 | + - def func(arg: type[int] | str | type[float]) -> None: +14 + def func(arg: type[int | float] | str) -> None: +15 | ... +16 | +17 | + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty]`. + --> PYI055.py:29:7 + | +28 | # OK +29 | item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: Combine multiple `type` members +26 | +27 | +28 | # OK + - item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker +29 + item: type[requests_mock.Mocker | httpretty] = requests_mock.Mocker +30 | +31 | +32 | def func(): + PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty | str]`. --> PYI055.py:34:8 | diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001-TC002-TC003_TC001-3_future.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001-TC002-TC003_TC001-3_future.py.snap index ca004802ad..2d3e377e1f 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001-TC002-TC003_TC001-3_future.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001-TC002-TC003_TC001-3_future.py.snap @@ -11,18 +11,17 @@ TC003 [*] Move standard library import `collections.Counter` into a type-checkin | help: Move into type-checking block - from collections import Counter -1 + from __future__ import annotations -2 | -3 | from elsewhere import third_party -4 | -5 | from . import first_party -6 + from typing import TYPE_CHECKING -7 + -8 + if TYPE_CHECKING: -9 + from collections import Counter +1 | +2 | from elsewhere import third_party +3 | +4 | from . import first_party +5 + from typing import TYPE_CHECKING +6 + +7 + if TYPE_CHECKING: +8 + from collections import Counter +9 | 10 | -11 | -12 | def f(x: first_party.foo): ... +11 | def f(x: first_party.foo): ... note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `elsewhere.third_party` into a type-checking block @@ -36,19 +35,18 @@ TC002 [*] Move third-party import `elsewhere.third_party` into a type-checking b 5 | from . import first_party | help: Move into type-checking block -1 + from __future__ import annotations -2 | from collections import Counter -3 | +1 | from collections import Counter +2 | - from elsewhere import third_party -4 | -5 | from . import first_party -6 + from typing import TYPE_CHECKING -7 + -8 + if TYPE_CHECKING: -9 + from elsewhere import third_party +3 | +4 | from . import first_party +5 + from typing import TYPE_CHECKING +6 + +7 + if TYPE_CHECKING: +8 + from elsewhere import third_party +9 | 10 | -11 | -12 | def f(x: first_party.foo): ... +11 | def f(x: first_party.foo): ... note: This is an unsafe fix and may change runtime behavior TC001 [*] Move application import `.first_party` into a type-checking block @@ -60,17 +58,15 @@ TC001 [*] Move application import `.first_party` into a type-checking block | ^^^^^^^^^^^ | help: Move into type-checking block -1 + from __future__ import annotations -2 | from collections import Counter -3 | -4 | from elsewhere import third_party -5 | +2 | +3 | from elsewhere import third_party +4 | - from . import first_party -6 + from typing import TYPE_CHECKING -7 + -8 + if TYPE_CHECKING: -9 + from . import first_party +5 + from typing import TYPE_CHECKING +6 + +7 + if TYPE_CHECKING: +8 + from . import first_party +9 | 10 | -11 | -12 | def f(x: first_party.foo): ... +11 | def f(x: first_party.foo): ... note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001_TC001_future.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001_TC001_future.py.snap index 6d579e57c2..12b5aa40f2 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001_TC001_future.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001_TC001_future.py.snap @@ -12,15 +12,14 @@ TC001 [*] Move application import `.first_party` into a type-checking block | help: Move into type-checking block - def f(): -1 + from __future__ import annotations -2 + from typing import TYPE_CHECKING -3 + -4 + if TYPE_CHECKING: -5 | from . import first_party -6 + def f(): -7 | -8 | def f(x: first_party.foo): ... -9 | +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 | from . import first_party +5 + def f(): +6 | +7 | def f(x: first_party.foo): ... +8 | note: This is an unsafe fix and may change runtime behavior TC001 [*] Move application import `.foo` into a type-checking block @@ -33,24 +32,19 @@ TC001 [*] Move application import `.foo` into a type-checking block 59 | def f(x: Union[foo.Ty, int]): ... | help: Move into type-checking block -1 + from __future__ import annotations -2 | def f(): -3 | from . import first_party -4 | --------------------------------------------------------------------------------- +50 | 51 | -52 | -53 | # unions +52 | # unions - from typing import Union -54 + from typing import Union, TYPE_CHECKING -55 | -56 + if TYPE_CHECKING: -57 + from . import foo -58 + -59 | -60 | def n(): +53 + from typing import Union, TYPE_CHECKING +54 + +55 + if TYPE_CHECKING: +56 + from . import foo +57 | +58 | +59 | def n(): - from . import foo -61 | -62 | def f(x: Union[foo.Ty, int]): ... -63 | def g(x: foo.Ty | int): ... +60 | +61 | def f(x: Union[foo.Ty, int]): ... +62 | def g(x: foo.Ty | int): ... note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC002_TC002.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC002_TC002.py.snap index 2d3f306807..d84c1fff21 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC002_TC002.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC002_TC002.py.snap @@ -220,32 +220,3 @@ help: Move into type-checking block 52 | x = dict["pd.DataFrame", "pd.DataFrame"] 53 | note: This is an unsafe fix and may change runtime behavior - -TC002 [*] Move third-party import `module.Member` into a type-checking block - --> TC002.py:172:24 - | -170 | global Member -171 | -172 | from module import Member - | ^^^^^^ -173 | -174 | x: Member = 1 - | -help: Move into type-checking block -1 | """Tests to determine accurate detection of typing-only imports.""" -2 + from typing import TYPE_CHECKING -3 + -4 + if TYPE_CHECKING: -5 + from module import Member -6 | -7 | -8 | def f(): --------------------------------------------------------------------------------- -173 | def f(): -174 | global Member -175 | - - from module import Member -176 | -177 | x: Member = 1 -178 | -note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_runtime-import-in-type-checking-block_quote.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_runtime-import-in-type-checking-block_quote.py.snap index 1e6dc27a29..22bae5d08e 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_runtime-import-in-type-checking-block_quote.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_runtime-import-in-type-checking-block_quote.py.snap @@ -1,26 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs --- -TC004 [*] Quote references to `pandas.DataFrame`. Import is in a type-checking block. - --> quote.py:57:28 - | -56 | if TYPE_CHECKING: -57 | from pandas import DataFrame - | ^^^^^^^^^ -58 | -59 | def func(value: DataFrame): - | -help: Quote references -56 | if TYPE_CHECKING: -57 | from pandas import DataFrame -58 | - - def func(value: DataFrame): -59 + def func(value: "DataFrame"): -60 | ... -61 | -62 | -note: This is an unsafe fix and may change runtime behavior - TC004 [*] Move import `pandas.DataFrame` out of type-checking block. Import is used for more than type hinting. --> quote.py:110:28 | diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap index beccb0c7d9..8efb644122 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap @@ -11,18 +11,15 @@ TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block 4 | def baz() -> DataFrame: | help: Move into type-checking block - - def f(): -1 + from typing import TYPE_CHECKING -2 + -3 + if TYPE_CHECKING: -4 | from pandas import DataFrame -5 + def f(): -6 | - - def baz() -> DataFrame: -7 + def baz() -> "DataFrame": -8 | ... -9 | -10 | + - def f(): +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 | from pandas import DataFrame +5 + def f(): +6 | +7 | def baz() -> DataFrame: +8 | ... note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block @@ -48,11 +45,8 @@ help: Move into type-checking block 12 | def f(): - from pandas import DataFrame 13 | - - def baz() -> DataFrame[int]: -14 + def baz() -> "DataFrame[int]": +14 | def baz() -> DataFrame[int]: 15 | ... -16 | -17 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas` into a type-checking block @@ -78,11 +72,8 @@ help: Move into type-checking block 19 | def f(): - import pandas as pd 20 | - - def baz() -> pd.DataFrame: -21 + def baz() -> "pd.DataFrame": +21 | def baz() -> pd.DataFrame: 22 | ... -23 | -24 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas` into a type-checking block @@ -108,11 +99,8 @@ help: Move into type-checking block 26 | def f(): - import pandas as pd 27 | - - def baz() -> pd.DataFrame.Extra: -28 + def baz() -> "pd.DataFrame.Extra": +28 | def baz() -> pd.DataFrame.Extra: 29 | ... -30 | -31 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas` into a type-checking block @@ -138,11 +126,8 @@ help: Move into type-checking block 33 | def f(): - import pandas as pd 34 | - - def baz() -> pd.DataFrame | int: -35 + def baz() -> "pd.DataFrame | int": +35 | def baz() -> pd.DataFrame | int: 36 | ... -37 | -38 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block @@ -168,11 +153,8 @@ help: Move into type-checking block 41 | def f(): - from pandas import DataFrame 42 | - - def baz() -> DataFrame(): -43 + def baz() -> "DataFrame()": +43 | def baz() -> DataFrame(): 44 | ... -45 | -46 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block @@ -199,11 +181,8 @@ help: Move into type-checking block 50 | - from pandas import DataFrame 51 | - - def baz() -> DataFrame[Literal["int"]]: -52 + def baz() -> "DataFrame[Literal['int']]": +52 | def baz() -> DataFrame[Literal["int"]]: 53 | ... -54 | -55 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block @@ -229,11 +208,8 @@ help: Move into type-checking block 67 | def f(): - from pandas import DataFrame, Series 68 | - - def baz() -> DataFrame | Series: -69 + def baz() -> "DataFrame | Series": +69 | def baz() -> DataFrame | Series: 70 | ... -71 | -72 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.Series` into a type-checking block @@ -259,11 +235,8 @@ help: Move into type-checking block 67 | def f(): - from pandas import DataFrame, Series 68 | - - def baz() -> DataFrame | Series: -69 + def baz() -> "DataFrame | Series": +69 | def baz() -> DataFrame | Series: 70 | ... -71 | -72 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block @@ -290,23 +263,7 @@ help: Move into type-checking block - from pandas import DataFrame, Series 75 | 76 | def baz() -> ( - - DataFrame | - - Series -77 + "DataFrame | Series" -78 | ): -79 | ... -80 | -81 | class C: - - x: DataFrame[ - - int - - ] = 1 -82 + x: "DataFrame[int]" = 1 -83 | - - def func() -> DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]: -84 + def func() -> "DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]": -85 | ... -86 | -87 | +77 | DataFrame | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.Series` into a type-checking block @@ -333,23 +290,7 @@ help: Move into type-checking block - from pandas import DataFrame, Series 75 | 76 | def baz() -> ( - - DataFrame | - - Series -77 + "DataFrame | Series" -78 | ): -79 | ... -80 | -81 | class C: - - x: DataFrame[ - - int - - ] = 1 -82 + x: "DataFrame[int]" = 1 -83 | - - def func() -> DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]: -84 + def func() -> "DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]": -85 | ... -86 | -87 | +77 | DataFrame | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block @@ -375,11 +316,8 @@ help: Move into type-checking block 92 | def f(): - from pandas import DataFrame, Series 93 | - - def func(self) -> DataFrame | list[Series]: -94 + def func(self) -> "DataFrame | list[Series]": +94 | def func(self) -> DataFrame | list[Series]: 95 | pass -96 | -97 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.Series` into a type-checking block @@ -405,9 +343,6 @@ help: Move into type-checking block 92 | def f(): - from pandas import DataFrame, Series 93 | - - def func(self) -> DataFrame | list[Series]: -94 + def func(self) -> "DataFrame | list[Series]": +94 | def func(self) -> DataFrame | list[Series]: 95 | pass -96 | -97 | note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote2.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote2.py.snap index 0779cfdfd6..5e0b5862ab 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote2.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote2.py.snap @@ -11,18 +11,15 @@ TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` 4 | def test_remove_inner_quotes_double(self, user: AbstractBaseUser["int"]): | help: Move into type-checking block - - def f(): -1 + from typing import TYPE_CHECKING -2 + -3 + if TYPE_CHECKING: -4 | from django.contrib.auth.models import AbstractBaseUser -5 + def f(): -6 | - - def test_remove_inner_quotes_double(self, user: AbstractBaseUser["int"]): -7 + def test_remove_inner_quotes_double(self, user: "AbstractBaseUser[int]"): -8 | pass -9 | -10 | + - def f(): +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 | from django.contrib.auth.models import AbstractBaseUser +5 + def f(): +6 | +7 | def test_remove_inner_quotes_double(self, user: AbstractBaseUser["int"]): +8 | pass note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -48,11 +45,8 @@ help: Move into type-checking block 12 | def f(): - from django.contrib.auth.models import AbstractBaseUser 13 | - - def test_remove_inner_quotes_single(self, user: AbstractBaseUser['int']): -14 + def test_remove_inner_quotes_single(self, user: "AbstractBaseUser[int]"): +14 | def test_remove_inner_quotes_single(self, user: AbstractBaseUser['int']): 15 | pass -16 | -17 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -78,11 +72,8 @@ help: Move into type-checking block 19 | def f(): - from django.contrib.auth.models import AbstractBaseUser 20 | - - def test_remove_inner_quotes_mixed(self, user: AbstractBaseUser['int', "str"]): -21 + def test_remove_inner_quotes_mixed(self, user: "AbstractBaseUser[int, str]"): +21 | def test_remove_inner_quotes_mixed(self, user: AbstractBaseUser['int', "str"]): 22 | pass -23 | -24 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -109,11 +100,8 @@ help: Move into type-checking block 28 | - from django.contrib.auth.models import AbstractBaseUser 29 | - - def test_literal_annotation_args_contain_quotes(self, type1: AbstractBaseUser[Literal["user", "admin"], Annotated["int", "1", 2]]): -30 + def test_literal_annotation_args_contain_quotes(self, type1: "AbstractBaseUser[Literal['user', 'admin'], Annotated[int, '1', 2]]"): +30 | def test_literal_annotation_args_contain_quotes(self, type1: AbstractBaseUser[Literal["user", "admin"], Annotated["int", "1", 2]]): 31 | pass -32 | -33 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -140,11 +128,8 @@ help: Move into type-checking block 37 | - from django.contrib.auth.models import AbstractBaseUser 38 | - - def test_union_contain_inner_quotes(self, type1: AbstractBaseUser["int" | Literal["int"]]): -39 + def test_union_contain_inner_quotes(self, type1: "AbstractBaseUser[int | Literal['int']]"): +39 | def test_union_contain_inner_quotes(self, type1: AbstractBaseUser["int" | Literal["int"]]): 40 | pass -41 | -42 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -171,11 +156,8 @@ help: Move into type-checking block 46 | - from django.contrib.auth.models import AbstractBaseUser 47 | - - def test_inner_literal_mixed_quotes(user: AbstractBaseUser[Literal['user', "admin"]]): -48 + def test_inner_literal_mixed_quotes(user: "AbstractBaseUser[Literal['user', 'admin']]"): +48 | def test_inner_literal_mixed_quotes(user: AbstractBaseUser[Literal['user', "admin"]]): 49 | pass -50 | -51 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -202,11 +184,8 @@ help: Move into type-checking block 55 | - from django.contrib.auth.models import AbstractBaseUser 56 | - - def test_inner_literal_single_quote(user: AbstractBaseUser[Literal['int'], str]): -57 + def test_inner_literal_single_quote(user: "AbstractBaseUser[Literal['int'], str]"): +57 | def test_inner_literal_single_quote(user: AbstractBaseUser[Literal['int'], str]): 58 | pass -59 | -60 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -233,11 +212,8 @@ help: Move into type-checking block 64 | - from django.contrib.auth.models import AbstractBaseUser 65 | - - def test_mixed_quotes_literal(user: AbstractBaseUser[Literal['user'], "int"]): -66 + def test_mixed_quotes_literal(user: "AbstractBaseUser[Literal['user'], int]"): +66 | def test_mixed_quotes_literal(user: AbstractBaseUser[Literal['user'], "int"]): 67 | pass -68 | -69 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -264,9 +240,6 @@ help: Move into type-checking block 73 | - from django.contrib.auth.models import AbstractBaseUser 74 | - - def test_annotated_literal_mixed_quotes(user: AbstractBaseUser[Annotated[str, "max_length=20", Literal['user', "admin"]]]): -75 + def test_annotated_literal_mixed_quotes(user: "AbstractBaseUser[Annotated[str, 'max_length=20', Literal['user', 'admin']]]"): +75 | def test_annotated_literal_mixed_quotes(user: AbstractBaseUser[Annotated[str, "max_length=20", Literal['user', "admin"]]]): 76 | pass -77 | -78 | note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote3.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote3.py.snap index 4614aecb89..5877229b63 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote3.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote3.py.snap @@ -21,11 +21,8 @@ help: Move into type-checking block 7 | - from django.contrib.auth.models import AbstractBaseUser 8 | - - def test_union_literal_mixed_quotes(user: AbstractBaseUser[Union[Literal['active', "inactive"], str]]): -9 + def test_union_literal_mixed_quotes(user: 'AbstractBaseUser[Union[Literal["active", "inactive"], str]]'): +9 | def test_union_literal_mixed_quotes(user: AbstractBaseUser[Union[Literal['active', "inactive"], str]]): 10 | pass -11 | -12 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -52,11 +49,8 @@ help: Move into type-checking block 16 | - from django.contrib.auth.models import AbstractBaseUser 17 | - - def test_callable_literal_mixed_quotes(callable_fn: AbstractBaseUser[Callable[["int", Literal['admin', "user"]], 'bool']]): -18 + def test_callable_literal_mixed_quotes(callable_fn: 'AbstractBaseUser[Callable[[int, Literal["admin", "user"]], bool]]'): +18 | def test_callable_literal_mixed_quotes(callable_fn: AbstractBaseUser[Callable[["int", Literal['admin', "user"]], 'bool']]): 19 | pass -20 | -21 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -83,11 +77,8 @@ help: Move into type-checking block 25 | - from django.contrib.auth.models import AbstractBaseUser 26 | - - def test_callable_annotated_literal(callable_fn: AbstractBaseUser[Callable[[int, Annotated[str, Literal['active', "inactive"]]], bool]]): -27 + def test_callable_annotated_literal(callable_fn: 'AbstractBaseUser[Callable[[int, Annotated[str, Literal["active", "inactive"]]], bool]]'): +27 | def test_callable_annotated_literal(callable_fn: AbstractBaseUser[Callable[[int, Annotated[str, Literal['active', "inactive"]]], bool]]): 28 | pass -29 | -30 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models` into a type-checking block @@ -114,11 +105,8 @@ help: Move into type-checking block 34 | - from django.contrib.auth import models 35 | - - def test_attribute(arg: models.AbstractBaseUser["int"]): -36 + def test_attribute(arg: 'models.AbstractBaseUser[int]'): +36 | def test_attribute(arg: models.AbstractBaseUser["int"]): 37 | pass -38 | -39 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models` into a type-checking block @@ -145,11 +133,8 @@ help: Move into type-checking block 43 | - from django.contrib.auth import models 44 | - - def test_attribute_typing_literal(arg: models.AbstractBaseUser[Literal["admin"]]): -45 + def test_attribute_typing_literal(arg: 'models.AbstractBaseUser[Literal["admin"]]'): +45 | def test_attribute_typing_literal(arg: models.AbstractBaseUser[Literal["admin"]]): 46 | pass -47 | -48 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `third_party.Type` into a type-checking block @@ -176,11 +161,8 @@ help: Move into type-checking block 62 | from typing import Literal - from third_party import Type 63 | - - def test_string_contains_opposite_quote(self, type1: Type[Literal["'"]], type2: Type[Literal["\'"]]): -64 + def test_string_contains_opposite_quote(self, type1: 'Type[Literal["\'"]]', type2: 'Type[Literal["\'"]]'): +64 | def test_string_contains_opposite_quote(self, type1: Type[Literal["'"]], type2: Type[Literal["\'"]]): 65 | pass -66 | -67 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `third_party.Type` into a type-checking block @@ -207,7 +189,6 @@ help: Move into type-checking block 70 | from typing import Literal - from third_party import Type 71 | - - def test_quote_contains_backslash(self, type1: Type[Literal["\n"]], type2: Type[Literal["\""]]): -72 + def test_quote_contains_backslash(self, type1: 'Type[Literal["\\n"]]', type2: 'Type[Literal[\'"\']]'): +72 | def test_quote_contains_backslash(self, type1: Type[Literal["\n"]], type2: Type[Literal["\""]]): 73 | pass note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_4.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_4.py.snap index fc6eb83923..6c5ead2742 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_4.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_4.py.snap @@ -1,21 +1,4 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs --- -TC004 [*] Move import `typing.Any` out of type-checking block. Import is used for more than type hinting. - --> TC004_4.py:4:24 - | -3 | if TYPE_CHECKING: -4 | from typing import Any - | ^^^ - | -help: Move out of type-checking block -1 | from typing import TYPE_CHECKING, Type -2 + from typing import Any -3 | -4 | if TYPE_CHECKING: - - from typing import Any -5 + pass -6 | -7 | -8 | def example(*args: Any, **kwargs: Any): -note: This is an unsafe fix and may change runtime behavior + diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_5.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_5.py.snap index 98817b85bd..6c5ead2742 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_5.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_5.py.snap @@ -1,59 +1,4 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs --- -TC004 [*] Move import `typing.List` out of type-checking block. Import is used for more than type hinting. - --> TC004_5.py:4:24 - | -3 | if TYPE_CHECKING: -4 | from typing import List, Sequence, Set - | ^^^^ - | -help: Move out of type-checking block -1 | from typing import TYPE_CHECKING -2 + from typing import List, Sequence, Set -3 | -4 | if TYPE_CHECKING: - - from typing import List, Sequence, Set -5 + pass -6 | -7 | -8 | def example(a: List[int], /, b: Sequence[int], *, c: Set[int]): -note: This is an unsafe fix and may change runtime behavior -TC004 [*] Move import `typing.Sequence` out of type-checking block. Import is used for more than type hinting. - --> TC004_5.py:4:30 - | -3 | if TYPE_CHECKING: -4 | from typing import List, Sequence, Set - | ^^^^^^^^ - | -help: Move out of type-checking block -1 | from typing import TYPE_CHECKING -2 + from typing import List, Sequence, Set -3 | -4 | if TYPE_CHECKING: - - from typing import List, Sequence, Set -5 + pass -6 | -7 | -8 | def example(a: List[int], /, b: Sequence[int], *, c: Set[int]): -note: This is an unsafe fix and may change runtime behavior - -TC004 [*] Move import `typing.Set` out of type-checking block. Import is used for more than type hinting. - --> TC004_5.py:4:40 - | -3 | if TYPE_CHECKING: -4 | from typing import List, Sequence, Set - | ^^^ - | -help: Move out of type-checking block -1 | from typing import TYPE_CHECKING -2 + from typing import List, Sequence, Set -3 | -4 | if TYPE_CHECKING: - - from typing import List, Sequence, Set -5 + pass -6 | -7 | -8 | def example(a: List[int], /, b: Sequence[int], *, c: Set[int]): -note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_9.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_9.py.snap index 201e5cc19a..6c5ead2742 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_9.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_9.py.snap @@ -1,44 +1,4 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs --- -TC004 [*] Move import `typing.Tuple` out of type-checking block. Import is used for more than type hinting. - --> TC004_9.py:4:24 - | -3 | if TYPE_CHECKING: -4 | from typing import Tuple, List, Dict - | ^^^^^ -5 | -6 | x: Tuple - | -help: Move out of type-checking block -1 | from typing import TYPE_CHECKING -2 + from typing import Tuple, List -3 | -4 | if TYPE_CHECKING: - - from typing import Tuple, List, Dict -5 + from typing import Dict -6 | -7 | x: Tuple -8 | -note: This is an unsafe fix and may change runtime behavior -TC004 [*] Move import `typing.List` out of type-checking block. Import is used for more than type hinting. - --> TC004_9.py:4:31 - | -3 | if TYPE_CHECKING: -4 | from typing import Tuple, List, Dict - | ^^^^ -5 | -6 | x: Tuple - | -help: Move out of type-checking block -1 | from typing import TYPE_CHECKING -2 + from typing import Tuple, List -3 | -4 | if TYPE_CHECKING: - - from typing import Tuple, List, Dict -5 + from typing import Dict -6 | -7 | x: Tuple -8 | -note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_quote.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_quote.py.snap index 53e45cce3a..22bae5d08e 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_quote.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_quote.py.snap @@ -1,31 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs --- -TC004 [*] Move import `pandas.DataFrame` out of type-checking block. Import is used for more than type hinting. - --> quote.py:57:28 - | -56 | if TYPE_CHECKING: -57 | from pandas import DataFrame - | ^^^^^^^^^ -58 | -59 | def func(value: DataFrame): - | -help: Move out of type-checking block -1 + from pandas import DataFrame -2 | def f(): -3 | from pandas import DataFrame -4 | --------------------------------------------------------------------------------- -55 | from typing import TYPE_CHECKING -56 | -57 | if TYPE_CHECKING: - - from pandas import DataFrame -58 + pass -59 | -60 | def func(value: DataFrame): -61 | ... -note: This is an unsafe fix and may change runtime behavior - TC004 [*] Move import `pandas.DataFrame` out of type-checking block. Import is used for more than type hinting. --> quote.py:110:28 | diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TC010_2.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TC010_2.py.snap index ab0b2a8952..3588e7b241 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TC010_2.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TC010_2.py.snap @@ -1,39 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs --- -TC010 Invalid string member in `X | Y`-style union type - --> TC010_2.py:4:4 - | -4 | x: "int" | str # TC010 - | ^^^^^ -5 | x: ("int" | str) | "bool" # TC010 - | - -TC010 Invalid string member in `X | Y`-style union type - --> TC010_2.py:5:5 - | -4 | x: "int" | str # TC010 -5 | x: ("int" | str) | "bool" # TC010 - | ^^^^^ - | - -TC010 Invalid string member in `X | Y`-style union type - --> TC010_2.py:5:20 - | -4 | x: "int" | str # TC010 -5 | x: ("int" | str) | "bool" # TC010 - | ^^^^^^ - | - -TC010 Invalid string member in `X | Y`-style union type - --> TC010_2.py:12:20 - | -12 | z: list[str, str | "int"] = [] # TC010 - | ^^^^^ -13 | -14 | type A = Value["int" | str] # OK - | - TC010 Invalid string member in `X | Y`-style union type --> TC010_2.py:16:30 | diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_TC002.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_TC002.py.snap index 2d3f306807..d84c1fff21 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_TC002.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_TC002.py.snap @@ -220,32 +220,3 @@ help: Move into type-checking block 52 | x = dict["pd.DataFrame", "pd.DataFrame"] 53 | note: This is an unsafe fix and may change runtime behavior - -TC002 [*] Move third-party import `module.Member` into a type-checking block - --> TC002.py:172:24 - | -170 | global Member -171 | -172 | from module import Member - | ^^^^^^ -173 | -174 | x: Member = 1 - | -help: Move into type-checking block -1 | """Tests to determine accurate detection of typing-only imports.""" -2 + from typing import TYPE_CHECKING -3 + -4 + if TYPE_CHECKING: -5 + from module import Member -6 | -7 | -8 | def f(): --------------------------------------------------------------------------------- -173 | def f(): -174 | global Member -175 | - - from module import Member -176 | -177 | x: Member = 1 -178 | -note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_quote.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_quote.py.snap index 173cb5a2d6..8efb644122 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_quote.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_quote.py.snap @@ -1,5 +1,348 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs -snapshot_kind: text --- +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:2:24 + | +1 | def f(): +2 | from pandas import DataFrame + | ^^^^^^^^^ +3 | +4 | def baz() -> DataFrame: + | +help: Move into type-checking block + - def f(): +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 | from pandas import DataFrame +5 + def f(): +6 | +7 | def baz() -> DataFrame: +8 | ... +note: This is an unsafe fix and may change runtime behavior +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:9:24 + | + 8 | def f(): + 9 | from pandas import DataFrame + | ^^^^^^^^^ +10 | +11 | def baz() -> DataFrame[int]: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +10 | +11 | +12 | def f(): + - from pandas import DataFrame +13 | +14 | def baz() -> DataFrame[int]: +15 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas` into a type-checking block + --> quote.py:16:22 + | +15 | def f(): +16 | import pandas as pd + | ^^ +17 | +18 | def baz() -> pd.DataFrame: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + import pandas as pd +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +17 | +18 | +19 | def f(): + - import pandas as pd +20 | +21 | def baz() -> pd.DataFrame: +22 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas` into a type-checking block + --> quote.py:23:22 + | +22 | def f(): +23 | import pandas as pd + | ^^ +24 | +25 | def baz() -> pd.DataFrame.Extra: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + import pandas as pd +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +24 | +25 | +26 | def f(): + - import pandas as pd +27 | +28 | def baz() -> pd.DataFrame.Extra: +29 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas` into a type-checking block + --> quote.py:30:22 + | +29 | def f(): +30 | import pandas as pd + | ^^ +31 | +32 | def baz() -> pd.DataFrame | int: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + import pandas as pd +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +31 | +32 | +33 | def f(): + - import pandas as pd +34 | +35 | def baz() -> pd.DataFrame | int: +36 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:38:24 + | +37 | def f(): +38 | from pandas import DataFrame + | ^^^^^^^^^ +39 | +40 | def baz() -> DataFrame(): + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +39 | +40 | +41 | def f(): + - from pandas import DataFrame +42 | +43 | def baz() -> DataFrame(): +44 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:47:24 + | +45 | from typing import Literal +46 | +47 | from pandas import DataFrame + | ^^^^^^^^^ +48 | +49 | def baz() -> DataFrame[Literal["int"]]: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +48 | def f(): +49 | from typing import Literal +50 | + - from pandas import DataFrame +51 | +52 | def baz() -> DataFrame[Literal["int"]]: +53 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:64:24 + | +63 | def f(): +64 | from pandas import DataFrame, Series + | ^^^^^^^^^ +65 | +66 | def baz() -> DataFrame | Series: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame, Series +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +65 | +66 | +67 | def f(): + - from pandas import DataFrame, Series +68 | +69 | def baz() -> DataFrame | Series: +70 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.Series` into a type-checking block + --> quote.py:64:35 + | +63 | def f(): +64 | from pandas import DataFrame, Series + | ^^^^^^ +65 | +66 | def baz() -> DataFrame | Series: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame, Series +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +65 | +66 | +67 | def f(): + - from pandas import DataFrame, Series +68 | +69 | def baz() -> DataFrame | Series: +70 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:71:24 + | +70 | def f(): +71 | from pandas import DataFrame, Series + | ^^^^^^^^^ +72 | +73 | def baz() -> ( + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame, Series +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +72 | +73 | +74 | def f(): + - from pandas import DataFrame, Series +75 | +76 | def baz() -> ( +77 | DataFrame | +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.Series` into a type-checking block + --> quote.py:71:35 + | +70 | def f(): +71 | from pandas import DataFrame, Series + | ^^^^^^ +72 | +73 | def baz() -> ( + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame, Series +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +72 | +73 | +74 | def f(): + - from pandas import DataFrame, Series +75 | +76 | def baz() -> ( +77 | DataFrame | +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:89:24 + | +88 | def f(): +89 | from pandas import DataFrame, Series + | ^^^^^^^^^ +90 | +91 | def func(self) -> DataFrame | list[Series]: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame, Series +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +90 | +91 | +92 | def f(): + - from pandas import DataFrame, Series +93 | +94 | def func(self) -> DataFrame | list[Series]: +95 | pass +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.Series` into a type-checking block + --> quote.py:89:35 + | +88 | def f(): +89 | from pandas import DataFrame, Series + | ^^^^^^ +90 | +91 | def func(self) -> DataFrame | list[Series]: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame, Series +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +90 | +91 | +92 | def f(): + - from pandas import DataFrame, Series +93 | +94 | def func(self) -> DataFrame | list[Series]: +95 | pass +note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs index f7f6c64a8d..5d0938d5c6 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs @@ -11,11 +11,11 @@ mod tests { use ruff_python_ast::PythonVersion; use test_case::test_case; - use crate::assert_diagnostics; use crate::registry::Rule; use crate::settings; use crate::settings::types::PreviewMode; use crate::test::test_path; + use crate::{assert_diagnostics, assert_diagnostics_diff}; #[test_case(Path::new("full_name.py"))] #[test_case(Path::new("import_as.py"))] @@ -82,6 +82,29 @@ mod tests { Ok(()) } + #[test_case(Rule::InvalidPathlibWithSuffix, Path::new("PTH210.py"))] + #[test_case(Rule::InvalidPathlibWithSuffix, Path::new("PTH210_1.py"))] + fn deferred_annotations_diff(rule_code: Rule, path: &Path) -> Result<()> { + let snapshot = format!( + "deferred_annotations_diff_{}_{}", + rule_code.name(), + path.to_string_lossy() + ); + assert_diagnostics_diff!( + snapshot, + Path::new("flake8_use_pathlib").join(path).as_path(), + &settings::LinterSettings { + unresolved_target_version: PythonVersion::PY313.into(), + ..settings::LinterSettings::for_rule(rule_code) + }, + &settings::LinterSettings { + unresolved_target_version: PythonVersion::PY314.into(), + ..settings::LinterSettings::for_rule(rule_code) + }, + ); + Ok(()) + } + #[test_case(Path::new("full_name.py"))] #[test_case(Path::new("import_as.py"))] #[test_case(Path::new("import_from_as.py"))] diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210.py.snap index d2a76a210e..9a057749d1 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210.py.snap +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210.py.snap @@ -1,17 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs --- -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:21:1 - | -20 | ### Errors -21 | path.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^ -22 | path.with_suffix("py") -23 | path.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:22:1 | @@ -95,18 +84,6 @@ help: Add a leading dot 28 | posix_path.with_suffix("py") note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:27:1 - | -25 | path.with_suffix(suffix="js") -26 | -27 | posix_path.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -28 | posix_path.with_suffix("py") -29 | posix_path.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:28:1 | @@ -189,18 +166,6 @@ help: Add a leading dot 34 | pure_path.with_suffix("py") note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:33:1 - | -31 | posix_path.with_suffix(suffix="js") -32 | -33 | pure_path.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -34 | pure_path.with_suffix("py") -35 | pure_path.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:34:1 | @@ -283,18 +248,6 @@ help: Add a leading dot 40 | pure_posix_path.with_suffix("py") note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:39:1 - | -37 | pure_path.with_suffix(suffix="js") -38 | -39 | pure_posix_path.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -40 | pure_posix_path.with_suffix("py") -41 | pure_posix_path.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:40:1 | @@ -377,18 +330,6 @@ help: Add a leading dot 46 | pure_windows_path.with_suffix("py") note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:45:1 - | -43 | pure_posix_path.with_suffix(suffix="js") -44 | -45 | pure_windows_path.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -46 | pure_windows_path.with_suffix("py") -47 | pure_windows_path.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:46:1 | @@ -471,18 +412,6 @@ help: Add a leading dot 52 | windows_path.with_suffix("py") note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:51:1 - | -49 | pure_windows_path.with_suffix(suffix="js") -50 | -51 | windows_path.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -52 | windows_path.with_suffix("py") -53 | windows_path.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:52:1 | @@ -565,18 +494,6 @@ help: Add a leading dot 58 | Path().with_suffix("py") note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:57:1 - | -55 | windows_path.with_suffix(suffix="js") -56 | -57 | Path().with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^^^ -58 | Path().with_suffix("py") -59 | PosixPath().with_suffix("py") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:58:1 | diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210_1.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210_1.py.snap index adb00cbf99..1f523ea194 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210_1.py.snap +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210_1.py.snap @@ -1,18 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs --- -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210_1.py:13:5 - | -11 | def test_path(p: Path) -> None: -12 | ## Errors -13 | p.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^ -14 | p.with_suffix("py") -15 | p.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210_1.py:14:5 | @@ -96,18 +84,6 @@ help: Add a leading dot 20 | p.with_suffix() note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210_1.py:31:5 - | -29 | def test_posix_path(p: PosixPath) -> None: -30 | ## Errors -31 | p.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^ -32 | p.with_suffix("py") -33 | p.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210_1.py:32:5 | @@ -191,18 +167,6 @@ help: Add a leading dot 38 | p.with_suffix() note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210_1.py:49:5 - | -47 | def test_pure_path(p: PurePath) -> None: -48 | ## Errors -49 | p.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^ -50 | p.with_suffix("py") -51 | p.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210_1.py:50:5 | @@ -286,18 +250,6 @@ help: Add a leading dot 56 | p.with_suffix() note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210_1.py:67:5 - | -65 | def test_pure_posix_path(p: PurePosixPath) -> None: -66 | ## Errors -67 | p.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^ -68 | p.with_suffix("py") -69 | p.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210_1.py:68:5 | @@ -381,18 +333,6 @@ help: Add a leading dot 74 | p.with_suffix() note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210_1.py:85:5 - | -83 | def test_pure_windows_path(p: PureWindowsPath) -> None: -84 | ## Errors -85 | p.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^ -86 | p.with_suffix("py") -87 | p.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210_1.py:86:5 | @@ -476,18 +416,6 @@ help: Add a leading dot 92 | p.with_suffix() note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210_1.py:103:5 - | -101 | def test_windows_path(p: WindowsPath) -> None: -102 | ## Errors -103 | p.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^ -104 | p.with_suffix("py") -105 | p.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210_1.py:104:5 | diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__deferred_annotations_diff_invalid-pathlib-with-suffix_PTH210.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__deferred_annotations_diff_invalid-pathlib-with-suffix_PTH210.py.snap new file mode 100644 index 0000000000..3a9523b277 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__deferred_annotations_diff_invalid-pathlib-with-suffix_PTH210.py.snap @@ -0,0 +1,100 @@ +--- +source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs +--- +--- Linter settings --- +-linter.unresolved_target_version = 3.13 ++linter.unresolved_target_version = 3.14 + +--- Summary --- +Removed: 7 +Added: 0 + +--- Removed --- +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:21:1 + | +20 | ### Errors +21 | path.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^ +22 | path.with_suffix("py") +23 | path.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:27:1 + | +25 | path.with_suffix(suffix="js") +26 | +27 | posix_path.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +28 | posix_path.with_suffix("py") +29 | posix_path.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:33:1 + | +31 | posix_path.with_suffix(suffix="js") +32 | +33 | pure_path.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +34 | pure_path.with_suffix("py") +35 | pure_path.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:39:1 + | +37 | pure_path.with_suffix(suffix="js") +38 | +39 | pure_posix_path.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +40 | pure_posix_path.with_suffix("py") +41 | pure_posix_path.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:45:1 + | +43 | pure_posix_path.with_suffix(suffix="js") +44 | +45 | pure_windows_path.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +46 | pure_windows_path.with_suffix("py") +47 | pure_windows_path.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:51:1 + | +49 | pure_windows_path.with_suffix(suffix="js") +50 | +51 | windows_path.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +52 | windows_path.with_suffix("py") +53 | windows_path.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:57:1 + | +55 | windows_path.with_suffix(suffix="js") +56 | +57 | Path().with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^^^ +58 | Path().with_suffix("py") +59 | PosixPath().with_suffix("py") + | +help: Remove "." or extend to valid suffix diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__deferred_annotations_diff_invalid-pathlib-with-suffix_PTH210_1.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__deferred_annotations_diff_invalid-pathlib-with-suffix_PTH210_1.py.snap new file mode 100644 index 0000000000..ee2bdd1988 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__deferred_annotations_diff_invalid-pathlib-with-suffix_PTH210_1.py.snap @@ -0,0 +1,88 @@ +--- +source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs +--- +--- Linter settings --- +-linter.unresolved_target_version = 3.13 ++linter.unresolved_target_version = 3.14 + +--- Summary --- +Removed: 6 +Added: 0 + +--- Removed --- +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210_1.py:13:5 + | +11 | def test_path(p: Path) -> None: +12 | ## Errors +13 | p.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^ +14 | p.with_suffix("py") +15 | p.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210_1.py:31:5 + | +29 | def test_posix_path(p: PosixPath) -> None: +30 | ## Errors +31 | p.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^ +32 | p.with_suffix("py") +33 | p.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210_1.py:49:5 + | +47 | def test_pure_path(p: PurePath) -> None: +48 | ## Errors +49 | p.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^ +50 | p.with_suffix("py") +51 | p.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210_1.py:67:5 + | +65 | def test_pure_posix_path(p: PurePosixPath) -> None: +66 | ## Errors +67 | p.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^ +68 | p.with_suffix("py") +69 | p.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210_1.py:85:5 + | +83 | def test_pure_windows_path(p: PureWindowsPath) -> None: +84 | ## Errors +85 | p.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^ +86 | p.with_suffix("py") +87 | p.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210_1.py:103:5 + | +101 | def test_windows_path(p: WindowsPath) -> None: +102 | ## Errors +103 | p.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^ +104 | p.with_suffix("py") +105 | p.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix diff --git a/crates/ruff_linter/src/rules/pyflakes/mod.rs b/crates/ruff_linter/src/rules/pyflakes/mod.rs index 6f3e7bbafe..3dd7edde65 100644 --- a/crates/ruff_linter/src/rules/pyflakes/mod.rs +++ b/crates/ruff_linter/src/rules/pyflakes/mod.rs @@ -3769,7 +3769,7 @@ lambda: fu def f(a: A) -> A: pass class A: pass ", - &[Rule::UndefinedName, Rule::UndefinedName], + &[], ); flakes( r" @@ -3783,7 +3783,7 @@ lambda: fu a: A class A: pass ", - &[Rule::UndefinedName], + &[], ); flakes( r" diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_18.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_18.py.snap index a487e4ddb8..95d305eb2b 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_18.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_18.py.snap @@ -1,5 +1,11 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs -snapshot_kind: text --- - +F821 Undefined name `y` + --> F821_18.py:19:7 + | +17 | # This is no longer allowed on Python 3.14+ +18 | x: (y := 1) +19 | print(y) + | ^ + | diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_2.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_2.py.snap index 89dd2df81f..d0b409f39e 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_2.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_2.py.snap @@ -1,12 +1,4 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs --- -F821 Undefined name `Model` - --> F821_2.py:5:13 - | -4 | # F821 Undefined name `Model` -5 | x: Literal["Model"] - | ^^^^^ -6 | -7 | from typing_extensions import Literal - | + diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_26.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_26.py.snap index 9bb838ffff..121eb3edd0 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_26.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_26.py.snap @@ -21,48 +21,6 @@ F821 Undefined name `C` 12 | CStr2: TypeAlias = Union["C", str] # always okay | -F821 Undefined name `C` - --> F821_26.py:16:12 - | -14 | # References to a class from inside the class: -15 | class C: -16 | other: C = ... # valid in a `.pyi` stub file, not in a `.py` runtime file - | ^ -17 | other2: "C" = ... # always okay -18 | def from_str(self, s: str) -> C: ... # valid in a `.pyi` stub file, not in a `.py` runtime file - | - -F821 Undefined name `C` - --> F821_26.py:18:35 - | -16 | other: C = ... # valid in a `.pyi` stub file, not in a `.py` runtime file -17 | other2: "C" = ... # always okay -18 | def from_str(self, s: str) -> C: ... # valid in a `.pyi` stub file, not in a `.py` runtime file - | ^ -19 | def from_str2(self, s: str) -> "C": ... # always okay - | - -F821 Undefined name `B` - --> F821_26.py:23:10 - | -21 | # Circular references: -22 | class A: -23 | foo: B # valid in a `.pyi` stub file, not in a `.py` runtime file - | ^ -24 | foo2: "B" # always okay -25 | bar: dict[str, B] # valid in a `.pyi` stub file, not in a `.py` runtime file - | - -F821 Undefined name `B` - --> F821_26.py:25:20 - | -23 | foo: B # valid in a `.pyi` stub file, not in a `.py` runtime file -24 | foo2: "B" # always okay -25 | bar: dict[str, B] # valid in a `.pyi` stub file, not in a `.py` runtime file - | ^ -26 | bar2: dict[str, "A"] # always okay - | - F821 Undefined name `Tree` --> F821_26.py:33:17 | diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_2.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_2.py.snap index e03ab73fc8..272d7d64e7 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_2.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_2.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pyupgrade/mod.rs --- -UP006 Use `collections.defaultdict` instead of `typing.DefaultDict` for type annotation +UP006 [*] Use `collections.defaultdict` instead of `typing.DefaultDict` for type annotation --> UP006_2.py:7:10 | 7 | def f(x: typing.DefaultDict[str, str]) -> None: @@ -9,3 +9,9 @@ UP006 Use `collections.defaultdict` instead of `typing.DefaultDict` for type ann 8 | ... | help: Replace with `collections.defaultdict` +4 | from collections import defaultdict +5 | +6 | + - def f(x: typing.DefaultDict[str, str]) -> None: +7 + def f(x: defaultdict[str, str]) -> None: +8 | ... diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP007.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP007.py.snap index c050ba4988..d2ce0fd604 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP007.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP007.py.snap @@ -171,6 +171,40 @@ help: Convert to `X | Y` 35 | 36 | +UP007 [*] Use `X | Y` for type annotations + --> UP007.py:37:10 + | +37 | def f(x: Union["str", int]) -> None: + | ^^^^^^^^^^^^^^^^^ +38 | ... + | +help: Convert to `X | Y` +34 | ... +35 | +36 | + - def f(x: Union["str", int]) -> None: +37 + def f(x: "str" | int) -> None: +38 | ... +39 | +40 | + +UP007 [*] Use `X | Y` for type annotations + --> UP007.py:41:10 + | +41 | def f(x: Union[("str", "int"), float]) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +42 | ... + | +help: Convert to `X | Y` +38 | ... +39 | +40 | + - def f(x: Union[("str", "int"), float]) -> None: +41 + def f(x: "str" | "int" | float) -> None: +42 | ... +43 | +44 | + UP007 Use `X | Y` for type annotations --> UP007.py:46:9 | diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_0.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_0.py.snap index 82253977ca..a198337648 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_0.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_0.py.snap @@ -685,6 +685,46 @@ help: Remove outdated version block 184 | print("py3") note: This is an unsafe fix and may change runtime behavior +UP036 [*] Version block is outdated for minimum Python version + --> UP036_0.py:185:4 + | +183 | print("py3") +184 | +185 | if sys.version_info <= (3,13): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +186 | print("py3") + | +help: Remove outdated version block +182 | if sys.version_info < (3,13): +183 | print("py3") +184 | + - if sys.version_info <= (3,13): + - print("py3") +185 | +186 | if sys.version_info <= (3,13): +187 | print("py3") +note: This is an unsafe fix and may change runtime behavior + +UP036 [*] Version block is outdated for minimum Python version + --> UP036_0.py:188:4 + | +186 | print("py3") +187 | +188 | if sys.version_info <= (3,13): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +189 | print("py3") + | +help: Remove outdated version block +185 | if sys.version_info <= (3,13): +186 | print("py3") +187 | + - if sys.version_info <= (3,13): + - print("py3") +188 | +189 | if sys.version_info == 10000000: +190 | print("py3") +note: This is an unsafe fix and may change runtime behavior + UP036 Version specifier is invalid --> UP036_0.py:191:24 | @@ -715,6 +755,27 @@ UP036 Version specifier is invalid 198 | print("py3") | +UP036 [*] Version block is outdated for minimum Python version + --> UP036_0.py:200:4 + | +198 | print("py3") +199 | +200 | if sys.version_info > (3,13): + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +201 | print("py3") + | +help: Remove outdated version block +197 | if sys.version_info <= (3,10000000): +198 | print("py3") +199 | + - if sys.version_info > (3,13): + - print("py3") +200 + print("py3") +201 | +202 | if sys.version_info >= (3,13): +203 | print("py3") +note: This is an unsafe fix and may change runtime behavior + UP036 [*] Version block is outdated for minimum Python version --> UP036_0.py:203:4 | diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_5.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_5.py.snap index ea5a1b487a..98e269b932 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_5.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_5.py.snap @@ -159,6 +159,66 @@ UP036 Version specifier is invalid 49 | print() | +UP036 [*] Version block is outdated for minimum Python version + --> UP036_5.py:60:4 + | +58 | print() +59 | +60 | if sys.version_info <= (3, 13, 0): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +61 | print() + | +help: Remove outdated version block +57 | if sys.version_info <= (3, 13, 'final'): +58 | print() +59 | + - if sys.version_info <= (3, 13, 0): + - print() +60 | +61 | if sys.version_info < (3, 13, 37): +62 | print() +note: This is an unsafe fix and may change runtime behavior + +UP036 [*] Version block is outdated for minimum Python version + --> UP036_5.py:63:4 + | +61 | print() +62 | +63 | if sys.version_info < (3, 13, 37): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +64 | print() + | +help: Remove outdated version block +60 | if sys.version_info <= (3, 13, 0): +61 | print() +62 | + - if sys.version_info < (3, 13, 37): + - print() +63 | +64 | if sys.version_info <= (3, 13, 37): +65 | print() +note: This is an unsafe fix and may change runtime behavior + +UP036 [*] Version block is outdated for minimum Python version + --> UP036_5.py:66:4 + | +64 | print() +65 | +66 | if sys.version_info <= (3, 13, 37): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +67 | print() + | +help: Remove outdated version block +63 | if sys.version_info < (3, 13, 37): +64 | print() +65 | + - if sys.version_info <= (3, 13, 37): + - print() +66 | +67 | if sys.version_info <= (3, 14, 0): +68 | print() +note: This is an unsafe fix and may change runtime behavior + UP036 [*] Version block is outdated for minimum Python version --> UP036_5.py:77:4 | @@ -278,3 +338,27 @@ help: Remove outdated version block 99 | # Semantically incorrect, skip fixing 100 | note: This is an unsafe fix and may change runtime behavior + +UP036 [*] Version block is outdated for minimum Python version + --> UP036_5.py:109:4 + | +107 | print(2) +108 | +109 | if sys.version_info.major > (3, 13): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +110 | print(3) +111 | else: + | +help: Remove outdated version block +106 | else: +107 | print(2) +108 | + - if sys.version_info.major > (3, 13): + - print(3) + - else: + - print(2) +109 + print(3) +110 | +111 | if sys.version_info.major[:2] > (3, 13): +112 | print(3) +note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP037_1.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP037_1.py.snap index 26631a2904..04fbed2e63 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP037_1.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP037_1.py.snap @@ -18,4 +18,18 @@ help: Remove quotes 9 + x: Tuple[int, int] = (0, 0) 10 | print(x) 11 | -12 | +12 | + +UP037 [*] Remove quotes from type annotation + --> UP037_1.py:14:4 + | +13 | # OK +14 | X: "Tuple[int, int]" = (0, 0) + | ^^^^^^^^^^^^^^^^^ + | +help: Remove quotes +11 | +12 | +13 | # OK + - X: "Tuple[int, int]" = (0, 0) +14 + X: Tuple[int, int] = (0, 0) diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__add_future_annotation_UP037_1.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__add_future_annotation_UP037_1.py.snap index fa2af22e0c..04fbed2e63 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__add_future_annotation_UP037_1.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__add_future_annotation_UP037_1.py.snap @@ -28,14 +28,8 @@ UP037 [*] Remove quotes from type annotation | ^^^^^^^^^^^^^^^^^ | help: Remove quotes -1 + from __future__ import annotations -2 | from typing import TYPE_CHECKING -3 | -4 | if TYPE_CHECKING: --------------------------------------------------------------------------------- +11 | 12 | -13 | -14 | # OK +13 | # OK - X: "Tuple[int, int]" = (0, 0) -15 + X: Tuple[int, int] = (0, 0) -note: This is an unsafe fix and may change runtime behavior +14 + X: Tuple[int, int] = (0, 0) diff --git a/crates/ruff_linter/src/rules/ruff/mod.rs b/crates/ruff_linter/src/rules/ruff/mod.rs index b484095aae..7cdc557841 100644 --- a/crates/ruff_linter/src/rules/ruff/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/mod.rs @@ -22,7 +22,7 @@ mod tests { use crate::settings::LinterSettings; use crate::settings::types::{CompiledPerFileIgnoreList, PerFileIgnore, PreviewMode}; use crate::test::{test_path, test_resource_path}; - use crate::{assert_diagnostics, settings}; + use crate::{assert_diagnostics, assert_diagnostics_diff, settings}; #[test_case(Rule::CollectionLiteralConcatenation, Path::new("RUF005.py"))] #[test_case(Rule::CollectionLiteralConcatenation, Path::new("RUF005_slices.py"))] @@ -245,6 +245,32 @@ mod tests { Ok(()) } + #[test] + fn confusables_deferred_annotations_diff() -> Result<()> { + assert_diagnostics_diff!( + Path::new("ruff/confusables.py"), + &LinterSettings { + unresolved_target_version: PythonVersion::PY313.into(), + allowed_confusables: FxHashSet::from_iter(['−', 'ρ', '∗']), + ..settings::LinterSettings::for_rules(vec![ + Rule::AmbiguousUnicodeCharacterString, + Rule::AmbiguousUnicodeCharacterDocstring, + Rule::AmbiguousUnicodeCharacterComment, + ]) + }, + &LinterSettings { + unresolved_target_version: PythonVersion::PY314.into(), + allowed_confusables: FxHashSet::from_iter(['−', 'ρ', '∗']), + ..settings::LinterSettings::for_rules(vec![ + Rule::AmbiguousUnicodeCharacterString, + Rule::AmbiguousUnicodeCharacterDocstring, + Rule::AmbiguousUnicodeCharacterComment, + ]) + }, + ); + Ok(()) + } + #[test] fn preview_confusables() -> Result<()> { let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF058_RUF058_0.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF058_RUF058_0.py.snap index 83bc6b8d0c..62756e9e9e 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF058_RUF058_0.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF058_RUF058_0.py.snap @@ -215,4 +215,62 @@ help: Use `map` instead 59 + 60 | ) 61 | -62 | +62 | + +RUF058 [*] `itertools.starmap` called on `zip` iterable + --> RUF058_0.py:71:1 + | +69 | starmap(func, zip(a, b, c), lorem=ipsum) +70 | +71 | starmap(func, zip(a, b, c, strict=True)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +72 | starmap(func, zip(a, b, c, strict=False)) +73 | starmap(func, zip(a, b, c, strict=strict)) + | +help: Use `map` instead +68 | starmap(func, zip(a, b, c, lorem=ipsum)) +69 | starmap(func, zip(a, b, c), lorem=ipsum) +70 | + - starmap(func, zip(a, b, c, strict=True)) +71 + map(func, a, b, c, strict=True) +72 | starmap(func, zip(a, b, c, strict=False)) +73 | starmap(func, zip(a, b, c, strict=strict)) +74 | + +RUF058 [*] `itertools.starmap` called on `zip` iterable + --> RUF058_0.py:72:1 + | +71 | starmap(func, zip(a, b, c, strict=True)) +72 | starmap(func, zip(a, b, c, strict=False)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +73 | starmap(func, zip(a, b, c, strict=strict)) + | +help: Use `map` instead +69 | starmap(func, zip(a, b, c), lorem=ipsum) +70 | +71 | starmap(func, zip(a, b, c, strict=True)) + - starmap(func, zip(a, b, c, strict=False)) +72 + map(func, a, b, c, strict=False) +73 | starmap(func, zip(a, b, c, strict=strict)) +74 | +75 | # https://github.com/astral-sh/ruff/issues/15742 + +RUF058 [*] `itertools.starmap` called on `zip` iterable + --> RUF058_0.py:73:1 + | +71 | starmap(func, zip(a, b, c, strict=True)) +72 | starmap(func, zip(a, b, c, strict=False)) +73 | starmap(func, zip(a, b, c, strict=strict)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +74 | +75 | # https://github.com/astral-sh/ruff/issues/15742 + | +help: Use `map` instead +70 | +71 | starmap(func, zip(a, b, c, strict=True)) +72 | starmap(func, zip(a, b, c, strict=False)) + - starmap(func, zip(a, b, c, strict=strict)) +73 + map(func, a, b, c, strict=strict) +74 | +75 | # https://github.com/astral-sh/ruff/issues/15742 +76 | starmap(func, zip(*a)) diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables.snap index 4e9ab5604c..27e8db6209 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables.snap @@ -191,11 +191,3 @@ RUF001 String contains ambiguous `𝐁` (MATHEMATICAL BOLD CAPITAL B). Did you m 59 | 60 | from typing import Literal | - -RUF001 String contains ambiguous `ﮨ` (ARABIC LETTER HEH GOAL INITIAL FORM). Did you mean `o` (LATIN SMALL LETTER O)? - --> confusables.py:61:20 - | -60 | from typing import Literal -61 | x: '''"""'Literal["ﮨ"]'"""''' - | ^ - | diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables_deferred_annotations_diff.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables_deferred_annotations_diff.snap new file mode 100644 index 0000000000..3411053a0d --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables_deferred_annotations_diff.snap @@ -0,0 +1,19 @@ +--- +source: crates/ruff_linter/src/rules/ruff/mod.rs +--- +--- Linter settings --- +-linter.unresolved_target_version = 3.13 ++linter.unresolved_target_version = 3.14 + +--- Summary --- +Removed: 1 +Added: 0 + +--- Removed --- +RUF001 String contains ambiguous `ﮨ` (ARABIC LETTER HEH GOAL INITIAL FORM). Did you mean `o` (LATIN SMALL LETTER O)? + --> confusables.py:61:20 + | +60 | from typing import Literal +61 | x: '''"""'Literal["ﮨ"]'"""''' + | ^ + | diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview_confusables.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview_confusables.snap index d9c235de30..332cf21fec 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview_confusables.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview_confusables.snap @@ -200,11 +200,3 @@ RUF001 String contains ambiguous `𝐁` (MATHEMATICAL BOLD CAPITAL B). Did you m 59 | 60 | from typing import Literal | - -RUF001 String contains ambiguous `ﮨ` (ARABIC LETTER HEH GOAL INITIAL FORM). Did you mean `o` (LATIN SMALL LETTER O)? - --> confusables.py:61:20 - | -60 | from typing import Literal -61 | x: '''"""'Literal["ﮨ"]'"""''' - | ^ - | diff --git a/crates/ruff_linter/src/test.rs b/crates/ruff_linter/src/test.rs index 2ebb244d62..01eb02705e 100644 --- a/crates/ruff_linter/src/test.rs +++ b/crates/ruff_linter/src/test.rs @@ -508,12 +508,18 @@ macro_rules! assert_diagnostics { #[macro_export] macro_rules! assert_diagnostics_diff { - ($snapshot:expr, $path:expr, $settings_before:expr, $settings_after:expr) => {{ + ($snapshot:expr, $path:expr, $settings_before:expr, $settings_after:expr $(,)?) => {{ let diff = $crate::test::test_path_with_settings_diff($path, $settings_before, $settings_after)?; insta::with_settings!({ omit_expression => true }, { insta::assert_snapshot!($snapshot, format!("{}", diff)); }); }}; + ($path:expr, $settings_before:expr, $settings_after:expr $(,)?) => {{ + let diff = $crate::test::test_path_with_settings_diff($path, $settings_before, $settings_after)?; + insta::with_settings!({ omit_expression => true }, { + insta::assert_snapshot!(format!("{}", diff)); + }); + }}; } #[cfg(test)] diff --git a/crates/ruff_python_ast/src/python_version.rs b/crates/ruff_python_ast/src/python_version.rs index 7eab154ad1..2cedb435fd 100644 --- a/crates/ruff_python_ast/src/python_version.rs +++ b/crates/ruff_python_ast/src/python_version.rs @@ -55,9 +55,8 @@ impl PythonVersion { Self::PY37 } - // TODO: change this to 314 when it is released pub const fn latest() -> Self { - Self::PY313 + Self::PY314 } /// The latest Python version supported in preview @@ -94,7 +93,7 @@ impl PythonVersion { impl Default for PythonVersion { fn default() -> Self { - Self::PY39 + Self::PY310 } } diff --git a/crates/ruff_python_formatter/tests/snapshots/format@blank_line_before_class_docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@blank_line_before_class_docstring.py.snap index 58cfb7e492..60177086ce 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@blank_line_before_class_docstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@blank_line_before_class_docstring.py.snap @@ -56,7 +56,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Enabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap index 9ca83e81de..e3117a2e39 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap @@ -175,7 +175,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -351,7 +351,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -527,7 +527,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -703,7 +703,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -879,7 +879,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples.py.snap index 024d662e0a..26fcfcff6c 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples.py.snap @@ -1368,7 +1368,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -2740,7 +2740,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -4112,7 +4112,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -5484,7 +5484,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -6856,7 +6856,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -8221,7 +8221,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -9586,7 +9586,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -10960,7 +10960,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -12325,7 +12325,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = 60 preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -13699,7 +13699,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_crlf.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_crlf.py.snap index 9b1dc4ba52..c40ab98413 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_crlf.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_crlf.py.snap @@ -27,7 +27,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_dynamic_line_width.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_dynamic_line_width.py.snap index 7112865500..628910f153 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_dynamic_line_width.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_dynamic_line_width.py.snap @@ -308,7 +308,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -879,7 +879,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -1425,7 +1425,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -1996,7 +1996,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@docstring_tab_indentation.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@docstring_tab_indentation.py.snap index a7e5f5d84f..c6736bcfa9 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@docstring_tab_indentation.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@docstring_tab_indentation.py.snap @@ -90,7 +90,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -184,7 +184,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap index 0e9e65cf92..7b021391c9 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap @@ -140,7 +140,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -296,7 +296,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap index c929f160df..40ac13cb24 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap @@ -768,7 +768,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Enabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -1595,7 +1595,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring_preview.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring_preview.py.snap index 508eda3e94..052758abce 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring_preview.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring_preview.py.snap @@ -38,7 +38,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Enabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_preserve.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_preserve.py.snap index a9d9f10827..526d24e02a 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_preserve.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_preserve.py.snap @@ -40,7 +40,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap index 953b2c34cb..0da0050e34 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap @@ -232,7 +232,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -482,7 +482,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__fmt_off_docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__fmt_off_docstring.py.snap index c7775aba83..db1d53e68b 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__fmt_off_docstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__fmt_off_docstring.py.snap @@ -37,7 +37,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -75,7 +75,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__indent.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__indent.py.snap index 8fd14d6634..3a68d9ceae 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__indent.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__indent.py.snap @@ -18,7 +18,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -37,7 +37,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -56,7 +56,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__mixed_space_and_tab.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__mixed_space_and_tab.py.snap index 99bde1d1d6..1c52065c5a 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__mixed_space_and_tab.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__mixed_space_and_tab.py.snap @@ -33,7 +33,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -68,7 +68,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -103,7 +103,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@notebook_docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@notebook_docstring.py.snap index 6018c9fc26..6195b43c21 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@notebook_docstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@notebook_docstring.py.snap @@ -24,7 +24,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Ipynb ``` @@ -48,7 +48,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@preview.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@preview.py.snap index 3557b8c478..f268f65783 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@preview.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@preview.py.snap @@ -84,7 +84,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -167,7 +167,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Enabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@quote_style.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@quote_style.py.snap index b79b0ead6e..a430dc1bb7 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@quote_style.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@quote_style.py.snap @@ -68,7 +68,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -142,7 +142,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -216,7 +216,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__docstring_code_examples.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__docstring_code_examples.py.snap index 0cb72a9fb4..cad5ee4f2a 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__docstring_code_examples.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__docstring_code_examples.py.snap @@ -121,7 +121,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -273,7 +273,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = 88 preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__indent.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__indent.py.snap index 669f8f5eb5..1609cf657e 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__indent.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__indent.py.snap @@ -81,7 +81,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -160,7 +160,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -239,7 +239,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__stub.pyi.snap b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__stub.pyi.snap index d2ec8f05d8..5610ef79ee 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__stub.pyi.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__stub.pyi.snap @@ -34,7 +34,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Stub ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@skip_magic_trailing_comma.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@skip_magic_trailing_comma.py.snap index 12521c9433..e427d077c7 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@skip_magic_trailing_comma.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@skip_magic_trailing_comma.py.snap @@ -51,7 +51,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -109,7 +109,7 @@ magic-trailing-comma = Ignore docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class.pyi.snap b/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class.pyi.snap index 1d7b8c7636..a1efd92a9c 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class.pyi.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class.pyi.snap @@ -201,7 +201,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Enabled -target_version = 3.9 +target_version = 3.10 source_type = Stub ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class_eof.pyi.snap b/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class_eof.pyi.snap index 55485257f3..9e9dc166fb 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class_eof.pyi.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class_eof.pyi.snap @@ -35,7 +35,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Enabled -target_version = 3.9 +target_version = 3.10 source_type = Stub ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@tab_width.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@tab_width.py.snap index ab1caa6805..51b77d3f16 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@tab_width.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@tab_width.py.snap @@ -26,7 +26,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -53,7 +53,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -83,7 +83,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_workspace/src/options.rs b/crates/ruff_workspace/src/options.rs index 055291b53c..21fc2269eb 100644 --- a/crates/ruff_workspace/src/options.rs +++ b/crates/ruff_workspace/src/options.rs @@ -299,8 +299,8 @@ pub struct Options { /// code upgrades, like rewriting type annotations. Ruff will not propose /// changes using features that are not available in the given version. /// - /// For example, to represent supporting Python >=3.10 or ==3.10 - /// specify `target-version = "py310"`. + /// For example, to represent supporting Python >=3.11 or ==3.11 + /// specify `target-version = "py311"`. /// /// If you're already using a `pyproject.toml` file, we recommend /// `project.requires-python` instead, as it's based on Python packaging @@ -327,8 +327,8 @@ pub struct Options { /// file than it would for an equivalent runtime file with the same target /// version. #[option( - default = r#""py39""#, - value_type = r#""py37" | "py38" | "py39" | "py310" | "py311" | "py312" | "py313""#, + default = r#""py310""#, + value_type = r#""py37" | "py38" | "py39" | "py310" | "py311" | "py312" | "py313" | "py314""#, example = r#" # Always generate Python 3.7-compatible code. target-version = "py37" diff --git a/crates/ty/docs/cli.md b/crates/ty/docs/cli.md index 83de0b3d6d..f9f5580f2b 100644 --- a/crates/ty/docs/cli.md +++ b/crates/ty/docs/cli.md @@ -86,6 +86,7 @@ over all configuration files.
3.11
3.12
3.13
3.14
--quiet
, -q
Use quiet output (or -qq
for silent output)
--respect-ignore-files
Respect file exclusions via .gitignore
and other standard ignore files. Use --no-respect-gitignore
to disable
--typeshed
, --custom-typeshed-dir
pathCustom directory to use for stdlib typeshed stubs
diff --git a/crates/ty/src/python_version.rs b/crates/ty/src/python_version.rs index 09ce15f526..694e81b55e 100644 --- a/crates/ty/src/python_version.rs +++ b/crates/ty/src/python_version.rs @@ -7,9 +7,9 @@ pub enum PythonVersion { Py37, #[value(name = "3.8")] Py38, - #[default] #[value(name = "3.9")] Py39, + #[default] #[value(name = "3.10")] Py310, #[value(name = "3.11")] @@ -18,6 +18,8 @@ pub enum PythonVersion { Py312, #[value(name = "3.13")] Py313, + #[value(name = "3.14")] + Py314, } impl PythonVersion { @@ -30,6 +32,7 @@ impl PythonVersion { Self::Py311 => "3.11", Self::Py312 => "3.12", Self::Py313 => "3.13", + Self::Py314 => "3.14", } } } @@ -50,6 +53,7 @@ impl From